mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-26 03:17:38 +00:00
Compare commits
69 Commits
v1.21-1.11
...
feature/po
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94e7d2d03b | ||
|
|
c271ed7c7f | ||
|
|
d6a246c122 | ||
|
|
0bef3ee0d8 | ||
|
|
bb04df7086 | ||
|
|
a70baf0d74 | ||
|
|
86e2f92493 | ||
|
|
e9aceca1de | ||
|
|
3042950507 | ||
|
|
f7a6aac657 | ||
|
|
782564e6ab | ||
|
|
6b8ba8b80b | ||
|
|
52b76d8886 | ||
|
|
ba36c69583 | ||
|
|
370e5f92a0 | ||
|
|
36d05e4774 | ||
|
|
89d1be17c9 | ||
|
|
0069591af9 | ||
|
|
c36c8605bf | ||
|
|
3c72a00d46 | ||
|
|
58aefc8df8 | ||
|
|
97ddfc2794 | ||
|
|
6e4ec86586 | ||
|
|
d24984c1d5 | ||
|
|
8080dcdd9e | ||
|
|
d7cea55e2a | ||
|
|
9b2f974a81 | ||
|
|
43770fa9bd | ||
|
|
80c7a54ad4 | ||
|
|
e57b6fede2 | ||
|
|
34a2fd039f | ||
|
|
3299d0e72a | ||
|
|
b89e2615db | ||
|
|
cdcd82679c | ||
|
|
cdfa866760 | ||
|
|
aa8078ddeb | ||
|
|
7e53c19d74 | ||
|
|
b7a8432cfb | ||
|
|
356c8e8aeb | ||
|
|
ed283155f7 | ||
|
|
87dfad026e | ||
|
|
bb97c465d9 | ||
|
|
9484315d37 | ||
|
|
be59f1a875 | ||
|
|
bfb28b4710 | ||
|
|
216f0adb3c | ||
|
|
77af4bc213 | ||
|
|
5abab982c7 | ||
|
|
764e1aa332 | ||
|
|
c47718b09d | ||
|
|
08d4f91c8b | ||
|
|
b9eac4e509 | ||
|
|
dc3d8ea198 | ||
|
|
cbe075b001 | ||
|
|
ed0b156e05 | ||
|
|
4dd0735066 | ||
|
|
38e516d7c7 | ||
|
|
70a31855ac | ||
|
|
6c8e64ffcd | ||
|
|
7285c32d58 | ||
|
|
99c60ac54b | ||
|
|
63e40cf3cb | ||
|
|
1d45935a25 | ||
|
|
f80373e7a2 | ||
|
|
63185629b7 | ||
|
|
4bfb9ac323 | ||
|
|
5926b6c994 | ||
|
|
f5ed43584d | ||
|
|
d77f5f135f |
10
.github/workflows/main-ci.yml
vendored
10
.github/workflows/main-ci.yml
vendored
@@ -30,8 +30,16 @@ jobs:
|
||||
- name: ⚒️ Build
|
||||
run: ./gradlew assemble || ./gradlew assemble
|
||||
|
||||
- name: Cache pre-commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: 💡 Lint
|
||||
uses: pre-commit/action@v3.0.0
|
||||
run: |
|
||||
pipx install pre-commit
|
||||
pre-commit run --show-diff-on-failure --color=always
|
||||
|
||||
- name: 🧪 Run tests
|
||||
run: ./gradlew test validateMixinNames checkChangelog
|
||||
|
||||
@@ -27,7 +27,7 @@ repos:
|
||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||
|
||||
- repo: https://github.com/fsfe/reuse-tool
|
||||
rev: v2.1.0
|
||||
rev: v4.0.3
|
||||
hooks:
|
||||
- id: reuse
|
||||
|
||||
|
||||
99
.reuse/dep5
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
|
||||
@@ -22,16 +22,16 @@ If you have a bug, suggestion, or other feedback, the best thing to do is [file
|
||||
use the issue templates - they provide a useful hint on what information to provide.
|
||||
|
||||
## 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!
|
||||
Translations are managed through [CrowdIn], an online interface for managing language strings. Translations may either
|
||||
be contributed there, or directly via a pull request.
|
||||
|
||||
## Setting up a development environment
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
|
||||
- Make sure you've got the following software 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/).
|
||||
- [NodeJS][node].
|
||||
- [NodeJS 20 or later][node].
|
||||
|
||||
- Download CC: Tweaked's source code:
|
||||
```
|
||||
@@ -101,10 +101,10 @@ about how you can build on that until you've covered everything!
|
||||
[community]: README.md#community "Get in touch with the community."
|
||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
||||
[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"
|
||||
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
|
||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
|
||||
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
||||
[node]: https://nodejs.org/en/ "Node.js"
|
||||
[architecture]: projects/ARCHITECTURE.md
|
||||
[Crowdin]: https://crowdin.com/project/cc-tweaked/
|
||||
|
||||
@@ -26,8 +26,9 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
|
||||
|
||||
## 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 [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
|
||||
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
|
||||
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].
|
||||
|
||||
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
|
||||
|
||||
@@ -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"
|
||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
||||
[forum]: https://forums.computercraft.cc/
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
[EsperNet]: https://www.esper.net/
|
||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||
|
||||
110
REUSE.toml
Normal file
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/**"]
|
||||
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"
|
||||
@@ -54,6 +54,7 @@ repositories {
|
||||
filter {
|
||||
includeGroup("cc.tweaked")
|
||||
// Things we mirror
|
||||
includeGroup("com.simibubi.create")
|
||||
includeGroup("commoble.morered")
|
||||
includeGroup("dev.architectury")
|
||||
includeGroup("dev.emi")
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.configurationcache.extensions.capitalized
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.gradle.language.jvm.tasks.ProcessResources
|
||||
import org.gradle.process.JavaForkOptions
|
||||
@@ -181,7 +180,7 @@ abstract class CCTweakedExtension(
|
||||
|
||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
|
||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||
val reportTaskName = "jacoco${task.name.capitalise()}Report"
|
||||
|
||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||
task.configure {
|
||||
|
||||
@@ -143,7 +143,7 @@ fun getNextVersion(version: String): String {
|
||||
val lastIndex = mainVersion.lastIndexOf('.')
|
||||
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
|
||||
val lastVersion = try {
|
||||
version.substring(lastIndex + 1).toInt()
|
||||
mainVersion.substring(lastIndex + 1).toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
|
||||
}
|
||||
@@ -155,3 +155,15 @@ fun getNextVersion(version: String): String {
|
||||
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
|
||||
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)
|
||||
}
|
||||
|
||||
27
crowdin.yml
Normal file
27
crowdin.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
files:
|
||||
- source: projects/common/src/generated/resources/assets/computercraft/lang/en_us.json
|
||||
translation: /projects/common/src/main/resources/assets/computercraft/lang/%locale_with_underscore%.json
|
||||
languages_mapping:
|
||||
locale_with_underscore:
|
||||
cs: cs_cs # Czech
|
||||
da: da_dk # Danish
|
||||
de: de_de # German
|
||||
es-ES: es_es # Spanish
|
||||
fr: fr_fr # French
|
||||
it: it_it # Italian
|
||||
ja: ja_jp # Japanese
|
||||
ko: ko_kr # Korean
|
||||
nb: nb_no # Norwegian Bokmal
|
||||
nl: nl_nl # Dutch
|
||||
pl: pl_pl # Polish
|
||||
pt-BR: pt_br # Portuguese, Brazilian
|
||||
ru: ru_ru # Russian
|
||||
sv-SE: sv_se # Sweedish
|
||||
tok: tok # Toki Pona
|
||||
tr: tr_tr # Turkish
|
||||
uk: uk_ua # Ukraine
|
||||
vi: vi_vn # Vietnamese
|
||||
zh-CN: zh_cn # Chinese Simplified
|
||||
@@ -191,7 +191,7 @@ end
|
||||
|
||||
> [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
|
||||
> 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
|
||||
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"
|
||||
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
|
||||
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
[Community]: /#community
|
||||
|
||||
@@ -50,7 +50,11 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
|
||||
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.
|
||||
|
||||
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
|
||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
||||
@@ -65,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."
|
||||
[lua]: https://www.lua.org/ "Lua's main website"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
[EsperNet]: https://www.esper.net/
|
||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||
|
||||
@@ -50,7 +50,11 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
|
||||
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.
|
||||
|
||||
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
|
||||
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"
|
||||
[lua]: https://www.lua.org/ "Lua's main website"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
[EsperNet]: https://www.esper.net/
|
||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||
|
||||
@@ -81,7 +81,7 @@ compatibility for these newer versions.
|
||||
| `string.dump` strip argument | ✔ | |
|
||||
| `string.pack`/`string.unpack`/`string.packsize` | ✔ | |
|
||||
| `table.move` | ✔ | |
|
||||
| `math.atan2` -> `math.atan` | ❌ | |
|
||||
| `math.atan2` -> `math.atan` | 🔶 | `math.atan` supports its two argument form. |
|
||||
| Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | |
|
||||
| `math.maxinteger`/`math.mininteger` | ❌ | |
|
||||
| `math.tointeger` | ❌ | |
|
||||
|
||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.111.0
|
||||
modVersion=1.113.1
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.1
|
||||
|
||||
2
gradle/gradle-daemon-jvm.properties
Normal file
2
gradle/gradle-daemon-jvm.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainVersion=17
|
||||
@@ -26,7 +26,7 @@ slf4j = "2.0.1"
|
||||
asm = "9.6"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = "0.9.3"
|
||||
cobalt = "0.9.4"
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jsr305 = "3.0.2"
|
||||
@@ -46,6 +46,8 @@ oculus = "1.2.5"
|
||||
rei = "12.0.626"
|
||||
rubidium = "0.6.1"
|
||||
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
|
||||
hamcrest = "2.2"
|
||||
@@ -69,6 +71,7 @@ librarian = "1.+"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.+"
|
||||
nullAway = "0.10.25"
|
||||
shadow = "8.3.1"
|
||||
spotless = "6.23.3"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.11.0-SQUID.1"
|
||||
@@ -92,19 +95,22 @@ jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }
|
||||
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
||||
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
||||
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
|
||||
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
||||
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
||||
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
|
||||
# Minecraft mods
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
|
||||
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
|
||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
fabric-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" }
|
||||
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" }
|
||||
@@ -171,6 +177,7 @@ githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "g
|
||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
5
gradlew
vendored
5
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
package dan200.computercraft.api;
|
||||
|
||||
import dan200.computercraft.api.component.ComputerComponent;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.filesystem.WritableMount;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.IComputerSystem;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* @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");
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An item detail provider for {@link ItemStack}'s whose {@link Item} has a specific type.
|
||||
* An item detail provider for {@link ItemStack}s whose {@link Item} has a specific type.
|
||||
*
|
||||
* @param <T> The type the stack's item must have.
|
||||
*/
|
||||
@@ -22,7 +22,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
||||
private final @Nullable String namespace;
|
||||
|
||||
/**
|
||||
* Create a new item detail provider. Meta will be inserted into a new sub-map named as per {@code namespace}.
|
||||
* Create a new item detail provider. Details will be inserted into a new sub-map named as per {@code namespace}.
|
||||
*
|
||||
* @param itemType The type the stack's item must have.
|
||||
* @param namespace The namespace to use for this provider.
|
||||
@@ -34,7 +34,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new item detail provider. Meta will be inserted directly into the results.
|
||||
* Create a new item detail provider. Details will be inserted directly into the results.
|
||||
*
|
||||
* @param itemType The type the stack's item must have.
|
||||
*/
|
||||
@@ -53,21 +53,18 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
||||
* @param stack The item stack to provide details for.
|
||||
* @param item The item to provide details for.
|
||||
*/
|
||||
public abstract void provideDetails(
|
||||
Map<? super String, Object> data, ItemStack stack, T item
|
||||
);
|
||||
public abstract void provideDetails(Map<? super String, Object> data, ItemStack stack, T item);
|
||||
|
||||
@Override
|
||||
public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||
public final void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
if (!itemType.isInstance(item)) return;
|
||||
|
||||
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
||||
Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
|
||||
|
||||
provideDetails(child, stack, itemType.cast(item));
|
||||
|
||||
if (namespace != null) {
|
||||
if (namespace == null) {
|
||||
provideDetails(data, stack, itemType.cast(item));
|
||||
} else {
|
||||
Map<? super String, Object> child = new HashMap<>();
|
||||
provideDetails(child, stack, itemType.cast(item));
|
||||
data.put(namespace, child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface DetailRegistry<T> {
|
||||
* @param provider The detail provider to register.
|
||||
* @see DetailProvider
|
||||
*/
|
||||
void addProvider(DetailProvider<T> provider);
|
||||
void addProvider(DetailProvider<? super T> provider);
|
||||
|
||||
/**
|
||||
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
||||
|
||||
@@ -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;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Construct an {@link ILuaAPI} for a specific computer.
|
||||
* Construct an {@link ILuaAPI} for a computer.
|
||||
*
|
||||
* @see ILuaAPI
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ILuaAPIFactory {
|
||||
@@ -6,10 +6,14 @@ package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
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 org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
@@ -17,7 +21,22 @@ import java.util.Map;
|
||||
/**
|
||||
* Wrapper class for pocket computers.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
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.
|
||||
* <p>
|
||||
@@ -64,6 +83,26 @@ public interface IPocketAccess {
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
@@ -73,6 +112,7 @@ public interface IPocketAccess {
|
||||
* @see #updateUpgradeNBTData()
|
||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||
* @see #getUpgrade()
|
||||
*/
|
||||
CompoundTag getUpgradeNBTData();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
/**
|
||||
* An enum representing the two sides of the turtle that a turtle turtle might reside.
|
||||
* An enum representing the two sides of the turtle that a turtle upgrade might reside.
|
||||
*/
|
||||
public enum TurtleSide {
|
||||
/**
|
||||
|
||||
@@ -38,11 +38,12 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
||||
implementation(project(":core"))
|
||||
implementation(commonClasses(project(":common-api")))
|
||||
clientImplementation(clientClasses(project(":common-api")))
|
||||
api(project(":core"))
|
||||
api(commonClasses(project(":common-api")))
|
||||
clientApi(clientClasses(project(":common-api")))
|
||||
|
||||
compileOnly(libs.bundles.externalMods.common)
|
||||
compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
|
||||
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||
|
||||
annotationProcessorEverywhere(libs.autoService)
|
||||
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
||||
@@ -73,6 +74,7 @@ public final class ClientRegistry {
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.LECTERN.get(), CustomLecternRenderer::new);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,15 +6,22 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
|
||||
|
||||
@@ -23,40 +30,75 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA
|
||||
*
|
||||
* @see dan200.computercraft.client.render.PrintoutRenderer
|
||||
*/
|
||||
public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
private final boolean book;
|
||||
private final int pages;
|
||||
private final TextBuffer[] text;
|
||||
private final TextBuffer[] colours;
|
||||
private int page;
|
||||
public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu> implements ContainerListener {
|
||||
private PrintoutInfo printout = PrintoutInfo.DEFAULT;
|
||||
private int page = 0;
|
||||
|
||||
public PrintoutScreen(HeldItemMenu container, Inventory player, Component title) {
|
||||
public PrintoutScreen(PrintoutMenu container, Inventory player, Component title) {
|
||||
super(container, player, title);
|
||||
|
||||
imageHeight = Y_SIZE;
|
||||
}
|
||||
|
||||
var text = PrintoutItem.getText(container.getStack());
|
||||
this.text = new TextBuffer[text.length];
|
||||
for (var i = 0; i < this.text.length; i++) this.text[i] = new TextBuffer(text[i]);
|
||||
private void setPrintout(ItemStack stack) {
|
||||
var text = PrintoutItem.getText(stack);
|
||||
var textBuffers = new TextBuffer[text.length];
|
||||
for (var i = 0; i < textBuffers.length; i++) textBuffers[i] = new TextBuffer(text[i]);
|
||||
|
||||
var colours = PrintoutItem.getColours(container.getStack());
|
||||
this.colours = new TextBuffer[colours.length];
|
||||
for (var i = 0; i < this.colours.length; i++) this.colours[i] = new TextBuffer(colours[i]);
|
||||
var colours = PrintoutItem.getColours(stack);
|
||||
var colourBuffers = new TextBuffer[colours.length];
|
||||
for (var i = 0; i < colours.length; i++) colourBuffers[i] = new TextBuffer(colours[i]);
|
||||
|
||||
page = 0;
|
||||
pages = Math.max(this.text.length / PrintoutItem.LINES_PER_PAGE, 1);
|
||||
book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
|
||||
var pages = Math.max(text.length / PrintoutItem.LINES_PER_PAGE, 1);
|
||||
var book = stack.is(ModRegistry.Items.PRINTED_BOOK.get());
|
||||
|
||||
printout = new PrintoutInfo(pages, book, textBuffers, colourBuffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
menu.addSlotListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
menu.removeSlotListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void slotChanged(AbstractContainerMenu menu, int slot, ItemStack stack) {
|
||||
if (slot == 0) setPrintout(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataChanged(AbstractContainerMenu menu, int slot, int data) {
|
||||
if (slot == PrintoutMenu.DATA_CURRENT_PAGE) page = data;
|
||||
}
|
||||
|
||||
private void setPage(int page) {
|
||||
this.page = page;
|
||||
|
||||
var gameMode = Objects.requireNonNull(Objects.requireNonNull(minecraft).gameMode);
|
||||
gameMode.handleInventoryButtonClick(menu.containerId, PrintoutMenu.PAGE_BUTTON_OFFSET + page);
|
||||
}
|
||||
|
||||
private void previousPage() {
|
||||
if (page > 0) setPage(page - 1);
|
||||
}
|
||||
|
||||
private void nextPage() {
|
||||
if (page < printout.pages() - 1) setPage(page + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, int scancode, int modifiers) {
|
||||
if (key == GLFW.GLFW_KEY_RIGHT) {
|
||||
if (page < pages - 1) page++;
|
||||
nextPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == GLFW.GLFW_KEY_LEFT) {
|
||||
if (page > 0) page--;
|
||||
previousPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,13 +110,13 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
if (super.mouseScrolled(x, y, delta)) return true;
|
||||
if (delta < 0) {
|
||||
// Scroll up goes to the next page
|
||||
if (page < pages - 1) page++;
|
||||
nextPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (delta > 0) {
|
||||
// Scroll down goes to the previous page
|
||||
if (page > 0) page--;
|
||||
previousPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -85,8 +127,9 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
// Draw the printout
|
||||
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
||||
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
||||
|
||||
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, printout.pages(), printout.book(), FULL_BRIGHT_LIGHTMAP);
|
||||
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, printout.text(), printout.colour());
|
||||
renderer.endBatch();
|
||||
}
|
||||
|
||||
@@ -105,4 +148,18 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||
// Skip rendering labels.
|
||||
}
|
||||
|
||||
record PrintoutInfo(int pages, boolean book, TextBuffer[] text, TextBuffer[] colour) {
|
||||
public static final PrintoutInfo DEFAULT;
|
||||
|
||||
static {
|
||||
var textLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
|
||||
Arrays.fill(textLines, new TextBuffer(" ".repeat(PrintoutItem.LINE_MAX_LENGTH)));
|
||||
|
||||
var colourLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
|
||||
Arrays.fill(colourLines, new TextBuffer("f".repeat(PrintoutItem.LINE_MAX_LENGTH)));
|
||||
|
||||
DEFAULT = new PrintoutInfo(1, false, textLines, colourLines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A model for {@linkplain PrintoutItem printouts} placed on a lectern.
|
||||
* <p>
|
||||
* This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
|
||||
* number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
|
||||
*
|
||||
* @see CustomLecternRenderer
|
||||
*/
|
||||
public class LecternPrintoutModel {
|
||||
public static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/printout");
|
||||
public static final Material MATERIAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE);
|
||||
|
||||
private static final int TEXTURE_WIDTH = 32;
|
||||
private static final int TEXTURE_HEIGHT = 32;
|
||||
|
||||
private static final String PAGE_1 = "page_1";
|
||||
private static final String PAGE_2 = "page_2";
|
||||
private static final String PAGE_3 = "page_3";
|
||||
private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
|
||||
|
||||
private final ModelPart pagesRoot;
|
||||
private final ModelPart bookRoot;
|
||||
private final ModelPart[] pages;
|
||||
|
||||
public LecternPrintoutModel() {
|
||||
pagesRoot = buildPages();
|
||||
bookRoot = buildBook();
|
||||
pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
|
||||
}
|
||||
|
||||
private static ModelPart buildPages() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
parts.addOrReplaceChild(
|
||||
PAGE_1,
|
||||
CubeListBuilder.create().texOffs(0, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
PAGE_2,
|
||||
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.125f, 0, 1.5f, (float) Math.PI * (1f / 16), 0, 0)
|
||||
);
|
||||
parts.addOrReplaceChild(
|
||||
PAGE_3,
|
||||
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
|
||||
);
|
||||
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
private static ModelPart buildBook() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"spine",
|
||||
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
|
||||
var angle = (float) Math.toRadians(5);
|
||||
parts.addOrReplaceChild(
|
||||
"left",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
|
||||
);
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"right",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
|
||||
);
|
||||
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
public void renderBook(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay) {
|
||||
bookRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
|
||||
if (pageCount > pages.length) pageCount = pages.length;
|
||||
var i = 0;
|
||||
for (; i < pageCount; i++) pages[i].visible = true;
|
||||
for (; i < pages.length; i++) pages[i].visible = false;
|
||||
|
||||
pagesRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -56,4 +56,8 @@ public final class ClientPocketComputers {
|
||||
var id = PocketComputerItem.getInstanceID(stack);
|
||||
return id == null ? null : instances.get(id);
|
||||
}
|
||||
|
||||
static @Nullable PocketComputerData get(UUID id) {
|
||||
return instances.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.pocket.items.PocketTooltipComponent;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
|
||||
/**
|
||||
* Renders the pocket computer's terminal in the item's tooltip.
|
||||
* <p>
|
||||
* The rendered terminal is downscaled by a factor of {@link #SCALE}.
|
||||
*/
|
||||
public class PocketClientTooltipComponent implements ClientTooltipComponent {
|
||||
private static final float SCALE = 0.5f;
|
||||
|
||||
private final UUID id;
|
||||
private final ComputerFamily family;
|
||||
|
||||
public PocketClientTooltipComponent(PocketTooltipComponent component) {
|
||||
this.id = component.id();
|
||||
this.family = component.family();
|
||||
}
|
||||
|
||||
private @Nullable PocketComputerData computer() {
|
||||
return ClientPocketComputers.get(id);
|
||||
}
|
||||
|
||||
private @Nullable NetworkedTerminal terminal() {
|
||||
var computer = computer();
|
||||
return computer == null ? null : computer.getTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
var terminal = terminal();
|
||||
if (terminal == null) return 0;
|
||||
|
||||
return (int) Math.ceil(
|
||||
(terminal.getHeight() * FixedWidthFontRenderer.FONT_HEIGHT + ComputerBorderRenderer.BORDER * 2 + ComputerBorderRenderer.MARGIN * 2) * SCALE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(Font font) {
|
||||
var terminal = terminal();
|
||||
if (terminal == null) return 0;
|
||||
|
||||
return (int) Math.ceil(
|
||||
(terminal.getWidth() * FixedWidthFontRenderer.FONT_WIDTH + ComputerBorderRenderer.BORDER * 2 + ComputerBorderRenderer.MARGIN * 2) * SCALE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderImage(Font font, int x, int y, GuiGraphics guiGraphics) {
|
||||
var terminal = terminal();
|
||||
if (terminal == null) return;
|
||||
|
||||
var pose = guiGraphics.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(x, y, 0);
|
||||
pose.scale(SCALE, SCALE, 1);
|
||||
|
||||
|
||||
render(pose, guiGraphics.bufferSource(), terminal);
|
||||
|
||||
pose.popPose();
|
||||
}
|
||||
|
||||
private void render(PoseStack stack, MultiBufferSource buffers, Terminal terminal) {
|
||||
var width = terminal.getWidth() * FONT_WIDTH + MARGIN * 2;
|
||||
var height = terminal.getHeight() * FONT_HEIGHT + MARGIN * 2;
|
||||
|
||||
var renderer = SpriteRenderer.createForGui(stack.last().pose(), buffers.getBuffer(RenderTypes.GUI_SPRITES));
|
||||
ComputerBorderRenderer.render(renderer, GuiSprites.getComputerTextures(family), BORDER, BORDER, width, height, false);
|
||||
|
||||
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(stack, buffers.getBuffer(RenderTypes.TERMINAL));
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, BORDER + MARGIN, BORDER + MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.client.renderer.blockentity.LecternRenderer;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
|
||||
/**
|
||||
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
|
||||
* <p>
|
||||
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
|
||||
*/
|
||||
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
|
||||
private final LecternPrintoutModel printoutModel;
|
||||
|
||||
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
|
||||
printoutModel = new LecternPrintoutModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(0.5f, 1.0625f, 0.5f);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
|
||||
poseStack.translate(0, -0.125f, 0);
|
||||
|
||||
var item = lectern.getItem();
|
||||
if (item.getItem() instanceof PrintoutItem printout) {
|
||||
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
|
||||
if (printout.getType() == PrintoutItem.Type.BOOK) {
|
||||
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
|
||||
} else {
|
||||
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item));
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.util.ARGB32;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -92,16 +93,11 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
}
|
||||
|
||||
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
||||
var r = (byte) ((colour >>> 16) & 0xFF);
|
||||
var g = (byte) ((colour >>> 8) & 0xFF);
|
||||
var b = (byte) (colour & 0xFF);
|
||||
var c = new byte[]{ r, g, b, (byte) 255 };
|
||||
|
||||
var buffer = render.getBuffer(RenderTypes.TERMINAL);
|
||||
FixedWidthFontRenderer.drawQuad(
|
||||
FixedWidthFontRenderer.toVertexConsumer(transform, buffer),
|
||||
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
|
||||
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
|
||||
ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,12 @@ public class SpriteRenderer {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static SpriteRenderer createForGui(Matrix4f transform, VertexConsumer builder) {
|
||||
return new SpriteRenderer(transform, builder, 0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255);
|
||||
}
|
||||
|
||||
public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) {
|
||||
return new SpriteRenderer(
|
||||
graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType),
|
||||
0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255
|
||||
);
|
||||
return createForGui(graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,9 +13,11 @@ import dan200.computercraft.core.terminal.Palette;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.util.ARGB32;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
|
||||
import static org.lwjgl.system.MemoryUtil.*;
|
||||
@@ -38,10 +40,12 @@ import static org.lwjgl.system.MemoryUtil.*;
|
||||
* {@link FixedWidthFontRenderer}.
|
||||
*/
|
||||
public final class DirectFixedWidthFontRenderer {
|
||||
private static final boolean IS_LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
private DirectFixedWidthFontRenderer() {
|
||||
}
|
||||
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour) {
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour) {
|
||||
// Short circuit to avoid the common case - the texture should be blank here after all.
|
||||
if (index == '\0' || index == ' ') return;
|
||||
|
||||
@@ -158,8 +162,8 @@ public final class DirectFixedWidthFontRenderer {
|
||||
return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2;
|
||||
}
|
||||
|
||||
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
|
||||
buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
|
||||
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
|
||||
buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
|
||||
}
|
||||
|
||||
public interface QuadEmitter {
|
||||
@@ -167,7 +171,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
|
||||
ByteBuffer buffer();
|
||||
|
||||
void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2);
|
||||
void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2);
|
||||
}
|
||||
|
||||
public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter {
|
||||
@@ -177,12 +181,12 @@ public final class DirectFixedWidthFontRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
|
||||
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
|
||||
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
|
||||
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
|
||||
}
|
||||
}
|
||||
|
||||
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
|
||||
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
|
||||
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
|
||||
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
|
||||
// This provides significant performance gains, at the cost of well, using Unsafe.
|
||||
@@ -196,16 +200,15 @@ public final class DirectFixedWidthFontRenderer {
|
||||
if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException();
|
||||
// Require the pointer to be aligned to a 32-bit boundary.
|
||||
if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned");
|
||||
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
|
||||
if (rgba.length != 4) throw new IllegalStateException();
|
||||
|
||||
// Pack colour so it is equivalent to rgba:BBBB.
|
||||
var colourAbgr = ARGB32.toABGR32(colour);
|
||||
var nativeColour = IS_LITTLE_ENDIAN ? colourAbgr : Integer.reverseBytes(colourAbgr);
|
||||
|
||||
memPutFloat(addr + 0, x1);
|
||||
memPutFloat(addr + 4, y1);
|
||||
memPutFloat(addr + 8, z);
|
||||
memPutByte(addr + 12, rgba[0]);
|
||||
memPutByte(addr + 13, rgba[1]);
|
||||
memPutByte(addr + 14, rgba[2]);
|
||||
memPutByte(addr + 15, (byte) 255);
|
||||
memPutInt(addr + 12, nativeColour);
|
||||
memPutFloat(addr + 16, u1);
|
||||
memPutFloat(addr + 20, v1);
|
||||
memPutShort(addr + 24, (short) 0xF0);
|
||||
@@ -214,10 +217,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
memPutFloat(addr + 28, x1);
|
||||
memPutFloat(addr + 32, y2);
|
||||
memPutFloat(addr + 36, z);
|
||||
memPutByte(addr + 40, rgba[0]);
|
||||
memPutByte(addr + 41, rgba[1]);
|
||||
memPutByte(addr + 42, rgba[2]);
|
||||
memPutByte(addr + 43, (byte) 255);
|
||||
memPutInt(addr + 40, nativeColour);
|
||||
memPutFloat(addr + 44, u1);
|
||||
memPutFloat(addr + 48, v2);
|
||||
memPutShort(addr + 52, (short) 0xF0);
|
||||
@@ -226,10 +226,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
memPutFloat(addr + 56, x2);
|
||||
memPutFloat(addr + 60, y2);
|
||||
memPutFloat(addr + 64, z);
|
||||
memPutByte(addr + 68, rgba[0]);
|
||||
memPutByte(addr + 69, rgba[1]);
|
||||
memPutByte(addr + 70, rgba[2]);
|
||||
memPutByte(addr + 71, (byte) 255);
|
||||
memPutInt(addr + 68, nativeColour);
|
||||
memPutFloat(addr + 72, u2);
|
||||
memPutFloat(addr + 76, v2);
|
||||
memPutShort(addr + 80, (short) 0xF0);
|
||||
@@ -238,10 +235,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
memPutFloat(addr + 84, x2);
|
||||
memPutFloat(addr + 88, y1);
|
||||
memPutFloat(addr + 92, z);
|
||||
memPutByte(addr + 96, rgba[0]);
|
||||
memPutByte(addr + 97, rgba[1]);
|
||||
memPutByte(addr + 98, rgba[2]);
|
||||
memPutByte(addr + 99, (byte) 255);
|
||||
memPutInt(addr + 96, nativeColour);
|
||||
memPutFloat(addr + 100, u2);
|
||||
memPutFloat(addr + 104, v1);
|
||||
memPutShort(addr + 108, (short) 0xF0);
|
||||
|
||||
@@ -12,6 +12,7 @@ import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.FastColor;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
@@ -41,7 +42,7 @@ public final class FixedWidthFontRenderer {
|
||||
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
|
||||
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
|
||||
|
||||
private static final byte[] BLACK = new byte[]{ byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), (byte) 255 };
|
||||
private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()));
|
||||
private static final float Z_OFFSET = 1e-3f;
|
||||
|
||||
private FixedWidthFontRenderer() {
|
||||
@@ -59,7 +60,7 @@ public final class FixedWidthFontRenderer {
|
||||
return 15 - Terminal.getColour(c, def);
|
||||
}
|
||||
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour, int light) {
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) {
|
||||
// Short circuit to avoid the common case - the texture should be blank here after all.
|
||||
if (index == '\0' || index == ' ') return;
|
||||
|
||||
@@ -75,7 +76,7 @@ public final class FixedWidthFontRenderer {
|
||||
);
|
||||
}
|
||||
|
||||
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light) {
|
||||
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) {
|
||||
quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
|
||||
}
|
||||
|
||||
@@ -216,10 +217,10 @@ public final class FixedWidthFontRenderer {
|
||||
return new QuadEmitter(transform.last().pose(), consumer);
|
||||
}
|
||||
|
||||
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light) {
|
||||
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) {
|
||||
var poseMatrix = c.poseMatrix();
|
||||
var consumer = c.consumer();
|
||||
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
|
||||
int r = FastColor.ARGB32.red(colour), g = FastColor.ARGB32.green(colour), b = FastColor.ARGB32.blue(colour), a = FastColor.ARGB32.alpha(colour);
|
||||
|
||||
consumer.vertex(poseMatrix, x1, y1, z).color(r, g, b, a).uv(u1, v1).uv2(light).endVertex();
|
||||
consumer.vertex(poseMatrix, x1, y2, z).color(r, g, b, a).uv(u1, v2).uv2(light).endVertex();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.data.client;
|
||||
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.data.DataProviders;
|
||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
||||
@@ -30,7 +31,8 @@ public final class ClientDataProviders {
|
||||
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
|
||||
out.accept(new ResourceLocation("blocks"), List.of(
|
||||
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
|
||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()),
|
||||
new SingleFile(LecternPrintoutModel.TEXTURE, Optional.empty())
|
||||
));
|
||||
out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
|
||||
// Buttons
|
||||
|
||||
8
projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
generated
Normal file
8
projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"variants": {
|
||||
"facing=east": {"model": "minecraft:block/lectern", "y": 90},
|
||||
"facing=north": {"model": "minecraft:block/lectern", "y": 0},
|
||||
"facing=south": {"model": "minecraft:block/lectern", "y": 180},
|
||||
"facing=west": {"model": "minecraft:block/lectern", "y": 270}
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,6 @@
|
||||
"commands.computercraft.generic.additional_rows": "%d additional rows…",
|
||||
"commands.computercraft.generic.exception": "Unhandled exception (%s)",
|
||||
"commands.computercraft.generic.no": "N",
|
||||
"commands.computercraft.generic.no_position": "<no pos>",
|
||||
"commands.computercraft.generic.position": "%s, %s, %s",
|
||||
"commands.computercraft.generic.yes": "Y",
|
||||
"commands.computercraft.help.desc": "Displays this help message",
|
||||
"commands.computercraft.help.no_children": "%s has no sub-commands",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"sources": [
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"},
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"},
|
||||
{"type": "minecraft:single", "resource": "computercraft:entity/printout"}
|
||||
]
|
||||
}
|
||||
|
||||
12
projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/lectern.json
generated
Normal file
12
projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/lectern.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"bonus_rolls": 0.0,
|
||||
"conditions": [{"condition": "minecraft:survives_explosion"}],
|
||||
"entries": [{"type": "minecraft:item", "name": "minecraft:lectern"}],
|
||||
"rolls": 1.0
|
||||
}
|
||||
],
|
||||
"random_sequence": "computercraft:blocks/lectern"
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.blockstates.*;
|
||||
import net.minecraft.data.models.model.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
@@ -100,6 +101,11 @@ class BlockModelProvider {
|
||||
registerTurtleUpgrade(generators, "block/turtle_speaker", "block/turtle_speaker_face");
|
||||
registerTurtleModem(generators, "block/turtle_modem_normal", "block/wireless_modem_normal_face");
|
||||
registerTurtleModem(generators, "block/turtle_modem_advanced", "block/wireless_modem_advanced_face");
|
||||
|
||||
generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(
|
||||
ModRegistry.Blocks.LECTERN.get(),
|
||||
Variant.variant().with(VariantProperties.MODEL, ModelLocationUtils.getModelLocation(Blocks.LECTERN))
|
||||
).with(createHorizontalFacingDispatch()));
|
||||
}
|
||||
|
||||
private static void registerDiskDrive(BlockModelGenerators generators) {
|
||||
|
||||
@@ -160,8 +160,6 @@ public final class LanguageProvider implements DataProvider {
|
||||
add("commands.computercraft.queue.synopsis", "Send a computer_command event to a command computer");
|
||||
add("commands.computercraft.queue.desc", "Send a computer_command event to a command computer, passing through the additional arguments. This is mostly designed for map makers, acting as a more computer-friendly version of /trigger. Any player can run the command, which would most likely be done through a text component's click event.");
|
||||
|
||||
add("commands.computercraft.generic.no_position", "<no pos>");
|
||||
add("commands.computercraft.generic.position", "%s, %s, %s");
|
||||
add("commands.computercraft.generic.yes", "Y");
|
||||
add("commands.computercraft.generic.no", "N");
|
||||
add("commands.computercraft.generic.exception", "Unhandled exception (%s)");
|
||||
@@ -284,7 +282,9 @@ public final class LanguageProvider implements DataProvider {
|
||||
return Stream.of(
|
||||
RegistryWrappers.BLOCKS.stream()
|
||||
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(Block::getDescriptionId),
|
||||
.map(Block::getDescriptionId)
|
||||
// Exclude blocks that just reuse vanilla translations, such as the lectern.
|
||||
.filter(x -> !x.startsWith("block.minecraft.")),
|
||||
RegistryWrappers.ITEMS.stream()
|
||||
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(Item::getDescriptionId),
|
||||
|
||||
@@ -15,6 +15,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
||||
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.storage.loot.LootPool;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
@@ -57,6 +58,8 @@ class LootTableProvider {
|
||||
computerDrop(add, ModRegistry.Blocks.TURTLE_NORMAL);
|
||||
computerDrop(add, ModRegistry.Blocks.TURTLE_ADVANCED);
|
||||
|
||||
blockDrop(add, ModRegistry.Blocks.LECTERN, LootItem.lootTableItem(Items.LECTERN), ExplosionCondition.survivesExplosion());
|
||||
|
||||
add.accept(ModRegistry.Blocks.CABLE.get().getLootTable(), LootTable
|
||||
.lootTable()
|
||||
.withPool(LootPool.lootPool()
|
||||
|
||||
@@ -102,8 +102,14 @@ class TagProvider {
|
||||
ModRegistry.Items.MONITOR_ADVANCED.get()
|
||||
);
|
||||
|
||||
// Allow printed books to be placed in bookshelves.
|
||||
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
|
||||
|
||||
// Allow any printout to be placed on lecterns. See also PrintoutItem and CustomLecternBlock.
|
||||
tags.tag(ItemTags.LECTERN_BOOKS).add(
|
||||
ModRegistry.Items.PRINTED_PAGE.get(), ModRegistry.Items.PRINTED_PAGES.get(), ModRegistry.Items.PRINTED_BOOK.get()
|
||||
);
|
||||
|
||||
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
||||
.add(Items.GLASS_BOTTLE)
|
||||
.addTag(ItemTags.BOATS);
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.Objects;
|
||||
/**
|
||||
* The global factory for {@link ILuaAPIFactory}s.
|
||||
*
|
||||
* @see dan200.computercraft.core.ComputerContext.Builder#apiFactories(Collection)
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public final class ApiFactories {
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.*;
|
||||
* @param <T> The type of object that this registry provides details for.
|
||||
*/
|
||||
public class DetailRegistryImpl<T> implements DetailRegistry<T> {
|
||||
private final Collection<DetailProvider<T>> providers = new ArrayList<>();
|
||||
private final Collection<DetailProvider<? super T>> providers = new ArrayList<>();
|
||||
private final DetailProvider<T> basic;
|
||||
|
||||
public DetailRegistryImpl(DetailProvider<T> basic) {
|
||||
@@ -24,7 +24,7 @@ public class DetailRegistryImpl<T> implements DetailRegistry<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addProvider(DetailProvider<T> provider) {
|
||||
public synchronized void addProvider(DetailProvider<? super T> provider) {
|
||||
Objects.requireNonNull(provider, "provider cannot be null");
|
||||
if (!providers.contains(provider)) providers.add(provider);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package dan200.computercraft.shared;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.component.ComputerComponents;
|
||||
import dan200.computercraft.api.detail.DetailProvider;
|
||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
@@ -22,7 +23,7 @@ import dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType;
|
||||
import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
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.ComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
||||
@@ -39,6 +40,9 @@ import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||
import dan200.computercraft.shared.details.BlockDetails;
|
||||
import dan200.computercraft.shared.details.ItemDetails;
|
||||
import dan200.computercraft.shared.integration.PermissionRegistry;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
||||
@@ -47,7 +51,6 @@ import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
||||
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
import dan200.computercraft.shared.network.container.HeldItemContainerData;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveMenu;
|
||||
@@ -64,6 +67,7 @@ import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.platform.RegistrationHelper;
|
||||
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.peripherals.PocketModem;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||
@@ -73,14 +77,17 @@ import dan200.computercraft.shared.recipe.CustomShapelessRecipe;
|
||||
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
||||
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
||||
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.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.turtle.core.TurtleAccessInternal;
|
||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.ComponentMap;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||
@@ -95,13 +102,16 @@ import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -165,6 +175,10 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
||||
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||
|
||||
public static final RegistryEntry<CustomLecternBlock> LECTERN = REGISTRY.register("lectern", () -> new CustomLecternBlock(
|
||||
BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.5F).sound(SoundType.WOOD).ignitedByLava()
|
||||
));
|
||||
}
|
||||
|
||||
public static class BlockEntities {
|
||||
@@ -206,6 +220,8 @@ public final class ModRegistry {
|
||||
ofBlock(Blocks.WIRELESS_MODEM_NORMAL, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_NORMAL.get(), p, s, false));
|
||||
public static final RegistryEntry<BlockEntityType<WirelessModemBlockEntity>> WIRELESS_MODEM_ADVANCED =
|
||||
ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_ADVANCED.get(), p, s, true));
|
||||
|
||||
public static final RegistryEntry<BlockEntityType<CustomLecternBlockEntity>> LECTERN = ofBlock(Blocks.LECTERN, CustomLecternBlockEntity::new);
|
||||
}
|
||||
|
||||
public static final class Items {
|
||||
@@ -302,11 +318,8 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<MenuType<PrinterMenu>> PRINTER = REGISTRY.register("printer",
|
||||
() -> new MenuType<>(PrinterMenu::new, FeatureFlags.VANILLA_SET));
|
||||
|
||||
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
|
||||
() -> ContainerData.toType(
|
||||
HeldItemContainerData::new,
|
||||
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
|
||||
));
|
||||
public static final RegistryEntry<MenuType<PrintoutMenu>> PRINTOUT = REGISTRY.register("printout",
|
||||
() -> new MenuType<>((i, c) -> PrintoutMenu.createRemote(i), FeatureFlags.VANILLA_SET));
|
||||
}
|
||||
|
||||
static class ArgumentTypes {
|
||||
@@ -447,6 +460,22 @@ public final class ModRegistry {
|
||||
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.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
||||
}
|
||||
|
||||
@@ -40,9 +40,8 @@ public final class ChatHelpers {
|
||||
return component;
|
||||
}
|
||||
|
||||
public static MutableComponent position(@Nullable BlockPos pos) {
|
||||
if (pos == null) return Component.translatable("commands.computercraft.generic.no_position");
|
||||
return Component.translatable("commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ());
|
||||
public static MutableComponent position(BlockPos pos) {
|
||||
return Component.literal(pos.toShortString());
|
||||
}
|
||||
|
||||
public static MutableComponent bool(boolean value) {
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TableBuilder {
|
||||
/**
|
||||
* Get the number of columns for this table.
|
||||
* <p>
|
||||
* This will be the same as {@link #getHeaders()}'s length if it is is non-{@code null},
|
||||
* This will be the same as {@link #getHeaders()}'s length if it is non-{@code null},
|
||||
* otherwise the length of the first column.
|
||||
*
|
||||
* @return The number of columns.
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
package dan200.computercraft.shared.common;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class HeldItemMenu extends AbstractContainerMenu {
|
||||
private final ItemStack stack;
|
||||
private final InteractionHand hand;
|
||||
|
||||
public HeldItemMenu(MenuType<? extends HeldItemMenu> type, int id, Player player, InteractionHand hand) {
|
||||
super(type, id);
|
||||
|
||||
this.hand = hand;
|
||||
stack = player.getItemInHand(hand).copy();
|
||||
}
|
||||
|
||||
public ItemStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int slot) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
if (!player.isAlive()) return false;
|
||||
|
||||
var stack = player.getItemInHand(hand);
|
||||
return stack == this.stack || !stack.isEmpty() && !this.stack.isEmpty() && stack.getItem() == this.stack.getItem();
|
||||
}
|
||||
|
||||
public static class Factory implements MenuProvider {
|
||||
private final MenuType<HeldItemMenu> type;
|
||||
private final Component name;
|
||||
private final InteractionHand hand;
|
||||
|
||||
public Factory(MenuType<HeldItemMenu> type, ItemStack stack, InteractionHand hand) {
|
||||
this.type = type;
|
||||
name = stack.getHoverName();
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
|
||||
return new HeldItemMenu(type, id, player, hand);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@ package dan200.computercraft.shared.computer.apis;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import dan200.computercraft.api.component.AdminComputer;
|
||||
import dan200.computercraft.api.detail.BlockReference;
|
||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.commands.CommandSource;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -35,11 +35,13 @@ import java.util.*;
|
||||
public class CommandAPI implements ILuaAPI {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CommandAPI.class);
|
||||
|
||||
private final ServerComputer computer;
|
||||
private final IComputerSystem computer;
|
||||
private final AdminComputer admin;
|
||||
private final OutputReceiver receiver = new OutputReceiver();
|
||||
|
||||
public CommandAPI(ServerComputer computer) {
|
||||
public CommandAPI(IComputerSystem computer, AdminComputer admin) {
|
||||
this.computer = computer;
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -287,7 +289,7 @@ public class CommandAPI implements ILuaAPI {
|
||||
|
||||
return new CommandSourceStack(receiver,
|
||||
Vec3.atCenterOf(computer.getPosition()), Vec2.ZERO,
|
||||
computer.getLevel(), 2,
|
||||
computer.getLevel(), admin.permissionLevel(),
|
||||
name, Component.literal(name),
|
||||
computer.getLevel().getServer(), null
|
||||
);
|
||||
|
||||
@@ -35,9 +35,9 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.storage.loot.LootParams;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
|
||||
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
|
||||
@@ -110,9 +110,19 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
return super.getCloneItemStack(world, pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public List<ItemStack> getDrops(BlockState state, LootParams.Builder params) {
|
||||
if (params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) instanceof AbstractComputerBlockEntity computer) {
|
||||
params = params.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
||||
}
|
||||
|
||||
return super.getDrops(state, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity tile, ItemStack tool) {
|
||||
// Don't drop blocks here - see onBlockHarvested.
|
||||
// Don't drop blocks here - see playerWillDestroy.
|
||||
player.awardStat(Stats.BLOCK_MINED.get(this));
|
||||
player.causeFoodExhaustion(0.005F);
|
||||
}
|
||||
@@ -120,25 +130,11 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
@Override
|
||||
public void playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) {
|
||||
super.playerWillDestroy(world, pos, state, player);
|
||||
if (!(world instanceof ServerLevel serverWorld)) return;
|
||||
if (!(world instanceof ServerLevel serverLevel)) return;
|
||||
|
||||
// We drop the item here instead of doing it in the harvest method, as we should
|
||||
// drop computers for creative players too.
|
||||
|
||||
var tile = world.getBlockEntity(pos);
|
||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
||||
var context = new LootParams.Builder(serverWorld)
|
||||
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
|
||||
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
|
||||
.withParameter(LootContextParams.THIS_ENTITY, player)
|
||||
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
|
||||
.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
||||
for (var item : state.getDrops(context)) {
|
||||
popResource(world, pos, item);
|
||||
}
|
||||
|
||||
state.spawnAfterBreak(serverWorld, pos, player.getMainHandItem(), true);
|
||||
}
|
||||
dropResources(state, serverLevel, pos, world.getBlockEntity(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,7 +142,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
super.setPlacedBy(world, pos, state, placer, stack);
|
||||
|
||||
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);
|
||||
if (id != -1) computer.setComputerID(id);
|
||||
|
||||
@@ -38,7 +38,7 @@ import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
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_LABEL = "Label";
|
||||
private static final String NBT_ON = "On";
|
||||
@@ -110,7 +110,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
fresh = false;
|
||||
computerID = computer.getID();
|
||||
|
||||
// If the on state has changed, mark as as dirty.
|
||||
// If the on state has changed, mark as dirty.
|
||||
var newOn = computer.isOn();
|
||||
if (on != newOn) {
|
||||
on = newOn;
|
||||
@@ -300,17 +300,14 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
for (var dir : DirectionUtil.FACINGS) updateRedstoneTo(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getComputerID() {
|
||||
return computerID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @Nullable String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setComputerID(int id) {
|
||||
if (getLevel().isClientSide || computerID == id) return;
|
||||
|
||||
@@ -318,7 +315,6 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
BlockEntityHelpers.updateBlock(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setLabel(@Nullable String label) {
|
||||
if (getLevel().isClientSide || Objects.equals(this.label, label)) return;
|
||||
|
||||
@@ -328,7 +324,6 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
BlockEntityHelpers.updateBlock(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerFamily getFamily() {
|
||||
return family;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.util.ComponentMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -34,7 +35,8 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
|
||||
protected ServerComputer createComputer(int id) {
|
||||
return new ServerComputer(
|
||||
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
||||
getFamily(), Config.computerTermWidth, Config.computerTermHeight
|
||||
getFamily(), Config.computerTermWidth, Config.computerTermHeight,
|
||||
ComponentMap.empty()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,16 @@
|
||||
package dan200.computercraft.shared.computer.core;
|
||||
|
||||
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.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.WorkMonitor;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.ComputerEnvironment;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.shared.computer.apis.CommandAPI;
|
||||
import dan200.computercraft.impl.ApiFactories;
|
||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
@@ -22,6 +23,7 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import dan200.computercraft.shared.util.ComponentMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@@ -49,7 +51,8 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
private int ticksSincePing;
|
||||
|
||||
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.position = position;
|
||||
@@ -60,10 +63,27 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
terminal = new NetworkedTerminal(terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged);
|
||||
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.setLabel(label);
|
||||
|
||||
if (family == ComputerFamily.COMMAND) addAPI(new CommandAPI(this));
|
||||
// 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() {
|
||||
@@ -217,10 +237,6 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
computer.getEnvironment().setBundledRedstoneInput(side, combination);
|
||||
}
|
||||
|
||||
public void addAPI(ILuaAPI api) {
|
||||
computer.addApi(api);
|
||||
}
|
||||
|
||||
public void setPeripheral(ComputerSide side, @Nullable IPeripheral peripheral) {
|
||||
computer.getEnvironment().setPeripheral(side, peripheral);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
||||
import dan200.computercraft.impl.ApiFactories;
|
||||
import dan200.computercraft.impl.GenericSources;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||
@@ -74,7 +73,6 @@ public final class ServerContext {
|
||||
.computerThreads(ConfigSpec.computerThreads.get())
|
||||
.mainThreadScheduler(mainThread)
|
||||
.luaFactory(luaMachine)
|
||||
.apiFactories(ApiFactories.getAll())
|
||||
.genericMethods(GenericSources.getAllMethods())
|
||||
.build();
|
||||
idAssigner = new IDAssigner(storageDir.resolve("ids.json"));
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
|
||||
package dan200.computercraft.shared.container;
|
||||
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A basic implementation of {@link Container} which operates on a {@linkplain #getContents() list of stacks}.
|
||||
*/
|
||||
public interface BasicContainer extends Container {
|
||||
NonNullList<ItemStack> getContents();
|
||||
List<ItemStack> getContents();
|
||||
|
||||
@Override
|
||||
default int getContainerSize() {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package dan200.computercraft.shared.data;
|
||||
|
||||
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.parameters.LootContextParam;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
@@ -27,7 +27,7 @@ public final class HasComputerIdLootCondition implements LootItemCondition {
|
||||
@Override
|
||||
public boolean test(LootContext lootContext) {
|
||||
var tile = lootContext.getParamOrNull(LootContextParams.BLOCK_ENTITY);
|
||||
return tile instanceof IComputerBlockEntity computer && computer.getComputerID() >= 0;
|
||||
return tile instanceof AbstractComputerBlockEntity computer && computer.getComputerID() >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -50,12 +50,12 @@ public class ItemDetails {
|
||||
if (tag != null && tag.contains("display", Tag.TAG_COMPOUND)) {
|
||||
var displayTag = tag.getCompound("display");
|
||||
if (displayTag.contains("Lore", Tag.TAG_LIST)) {
|
||||
var loreTag = displayTag.getList("Lore", Tag.TAG_STRING);
|
||||
data.put("lore", loreTag.stream()
|
||||
var lore = displayTag.getList("Lore", Tag.TAG_STRING).stream()
|
||||
.map(ItemDetails::parseTextComponent)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Component::getString)
|
||||
.toList());
|
||||
.toList();
|
||||
if (!lore.isEmpty()) data.put("lore", lore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.tags.TagKey;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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) {
|
||||
return TagKey.create(Registries.BLOCK, new ResourceLocation(mod, name));
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.lectern;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
|
||||
/**
|
||||
* Extends {@link LecternBlock} with support for {@linkplain PrintoutItem printouts}.
|
||||
* <p>
|
||||
* Unlike the vanilla lectern, this block is never empty. If the book is removed from the lectern, it converts back to
|
||||
* its vanilla version (see {@link #clearLectern(Level, BlockPos, BlockState)}).
|
||||
*
|
||||
* @see PrintoutItem#useOn(UseOnContext) Placing books into a lectern.
|
||||
*/
|
||||
public class CustomLecternBlock extends LecternBlock {
|
||||
public CustomLecternBlock(Properties properties) {
|
||||
super(properties);
|
||||
registerDefaultState(defaultBlockState().setValue(HAS_BOOK, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a vanilla lectern with a custom one.
|
||||
*
|
||||
* @param level The current level.
|
||||
* @param pos The position of the lectern.
|
||||
* @param blockState The current state of the lectern.
|
||||
* @param item The item to place in the custom lectern.
|
||||
*/
|
||||
public static void replaceLectern(Level level, BlockPos pos, BlockState blockState, ItemStack item) {
|
||||
level.setBlockAndUpdate(pos, ModRegistry.Blocks.LECTERN.get().defaultBlockState()
|
||||
.setValue(HAS_BOOK, true)
|
||||
.setValue(FACING, blockState.getValue(FACING))
|
||||
.setValue(POWERED, blockState.getValue(POWERED)));
|
||||
|
||||
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity be) be.setItem(item.split(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a custom lectern and replace it with an empty vanilla one.
|
||||
*
|
||||
* @param level The current level.
|
||||
* @param pos The position of the lectern.
|
||||
* @param blockState The current state of the lectern.
|
||||
*/
|
||||
static void clearLectern(Level level, BlockPos pos, BlockState blockState) {
|
||||
level.setBlockAndUpdate(pos, Blocks.LECTERN.defaultBlockState()
|
||||
.setValue(HAS_BOOK, false)
|
||||
.setValue(FACING, blockState.getValue(FACING))
|
||||
.setValue(POWERED, blockState.getValue(POWERED)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ItemStack getCloneItemStack(BlockGetter level, BlockPos pos, BlockState state) {
|
||||
return new ItemStack(Items.LECTERN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
// If we've no lectern, remove it.
|
||||
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern && lectern.getItem().isEmpty()) {
|
||||
clearLectern(level, pos, state);
|
||||
return;
|
||||
}
|
||||
|
||||
super.tick(state, level, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||
if (state.is(newState.getBlock())) return;
|
||||
|
||||
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
|
||||
dropItem(level, pos, state, lectern.getItem().copy());
|
||||
}
|
||||
|
||||
super.onRemove(state, level, pos, newState, isMoving);
|
||||
}
|
||||
|
||||
private static void dropItem(Level level, BlockPos pos, BlockState state, ItemStack stack) {
|
||||
if (stack.isEmpty()) return;
|
||||
|
||||
var direction = state.getValue(FACING);
|
||||
var dx = 0.25 * direction.getStepX();
|
||||
var dz = 0.25 * direction.getStepZ();
|
||||
var entity = new ItemEntity(level, pos.getX() + 0.5 + dx, pos.getY() + 1, pos.getZ() + 0.5 + dz, stack);
|
||||
entity.setDefaultPickUpDelay();
|
||||
level.addFreshEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescriptionId() {
|
||||
return Blocks.LECTERN.getDescriptionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomLecternBlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new CustomLecternBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
|
||||
return level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern ? lectern.getRedstoneSignal() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (!level.isClientSide && level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
|
||||
if (player.isSecondaryUseActive()) {
|
||||
// When shift+clicked with an empty hand, drop the item and replace with the normal lectern.
|
||||
clearLectern(level, pos, state);
|
||||
} else {
|
||||
// Otherwise open the screen.
|
||||
player.openMenu(lectern);
|
||||
}
|
||||
|
||||
player.awardStat(Stats.INTERACT_WITH_LECTERN);
|
||||
}
|
||||
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.lectern;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.container.BasicContainer;
|
||||
import dan200.computercraft.shared.container.SingleContainerData;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The block entity for our {@link CustomLecternBlock}.
|
||||
*
|
||||
* @see LecternBlockEntity
|
||||
*/
|
||||
public final class CustomLecternBlockEntity extends BlockEntity implements MenuProvider {
|
||||
private static final String NBT_ITEM = "Item";
|
||||
private static final String NBT_PAGE = "Page";
|
||||
|
||||
private ItemStack item = ItemStack.EMPTY;
|
||||
private int page, pageCount;
|
||||
|
||||
public CustomLecternBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(ModRegistry.BlockEntities.LECTERN.get(), pos, blockState);
|
||||
}
|
||||
|
||||
public ItemStack getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
void setItem(ItemStack item) {
|
||||
this.item = item;
|
||||
itemChanged();
|
||||
BlockEntityHelpers.updateBlock(this);
|
||||
}
|
||||
|
||||
int getRedstoneSignal() {
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
var progress = pageCount > 1 ? (float) page / (pageCount - 1) : 1F;
|
||||
return Mth.floor(progress * 14f) + 1;
|
||||
}
|
||||
|
||||
return 15;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the item has changed. This sets up the state for the new item.
|
||||
*/
|
||||
private void itemChanged() {
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
pageCount = PrintoutItem.getPageCount(item);
|
||||
page = Mth.clamp(page, 0, pageCount - 1);
|
||||
} else {
|
||||
pageCount = page = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current page, emitting a redstone pulse if needed.
|
||||
*
|
||||
* @param page The new page.
|
||||
*/
|
||||
private void setPage(int page) {
|
||||
if (this.page == page) return;
|
||||
|
||||
this.page = page;
|
||||
setChanged();
|
||||
if (getLevel() != null) LecternBlock.signalPageChange(getLevel(), getBlockPos(), getBlockState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(CompoundTag tag) {
|
||||
super.load(tag);
|
||||
|
||||
item = tag.contains(NBT_ITEM, Tag.TAG_COMPOUND) ? ItemStack.of(tag.getCompound(NBT_ITEM)) : ItemStack.EMPTY;
|
||||
page = tag.getInt(NBT_PAGE);
|
||||
itemChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(CompoundTag tag) {
|
||||
super.saveAdditional(tag);
|
||||
|
||||
if (!item.isEmpty()) tag.put(NBT_ITEM, item.save(new CompoundTag()));
|
||||
if (item.getItem() instanceof PrintoutItem) tag.putInt(NBT_PAGE, page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||
return ClientboundBlockEntityDataPacket.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getUpdateTag() {
|
||||
var tag = super.getUpdateTag();
|
||||
tag.put(NBT_ITEM, item.save(new CompoundTag()));
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
|
||||
var item = getItem();
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
return new PrintoutMenu(
|
||||
containerId, new LecternContainer(), 0,
|
||||
p -> Container.stillValidBlockEntity(this, player, Container.DEFAULT_DISTANCE_LIMIT),
|
||||
new PrintoutContainerData()
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return getItem().getDisplayName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A read-only container storing the lectern's contents.
|
||||
*/
|
||||
private final class LecternContainer implements BasicContainer {
|
||||
private final List<ItemStack> itemView = new AbstractList<>() {
|
||||
@Override
|
||||
public ItemStack get(int index) {
|
||||
if (index != 0) throw new IndexOutOfBoundsException("Inventory only has one slot");
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return itemView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
// Should never happen, so a no-op.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return !isRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ContainerData} for a {@link PrintoutMenu}. This provides a read/write view of the current page.
|
||||
*/
|
||||
private final class PrintoutContainerData implements SingleContainerData {
|
||||
@Override
|
||||
public int get() {
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, int value) {
|
||||
if (index == 0) setPage(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.media;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.container.InvisibleSlot;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.*;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The menus for {@linkplain PrintoutItem printouts}.
|
||||
* <p>
|
||||
* This is a somewhat similar design to {@link LecternMenu}, which is used to read written books.
|
||||
* <p>
|
||||
* This holds a single slot (containing the printout), and a single data slot ({@linkplain #DATA_CURRENT_PAGE holding
|
||||
* the current page}). The page is set by the client by sending a {@linkplain #clickMenuButton(Player, int) button
|
||||
* press} with an index of {@link #PAGE_BUTTON_OFFSET} plus the current page.
|
||||
* <p>
|
||||
* The client-side screen uses {@linkplain ContainerListener container listeners} to subscribe to item and page changes.
|
||||
* However, listeners aren't fired on the client, so we copy {@link LecternMenu}'s hack and call
|
||||
* {@link #broadcastChanges()} whenever an item or data value are changed.
|
||||
*/
|
||||
public class PrintoutMenu extends AbstractContainerMenu {
|
||||
public static final int DATA_CURRENT_PAGE = 0;
|
||||
private static final int DATA_SIZE = 1;
|
||||
|
||||
public static final int PAGE_BUTTON_OFFSET = 100;
|
||||
|
||||
private final Predicate<Player> valid;
|
||||
private final ContainerData currentPage;
|
||||
|
||||
public PrintoutMenu(
|
||||
int containerId, Container container, int slotIdx, Predicate<Player> valid, ContainerData currentPage
|
||||
) {
|
||||
super(ModRegistry.Menus.PRINTOUT.get(), containerId);
|
||||
this.valid = valid;
|
||||
this.currentPage = currentPage;
|
||||
|
||||
addSlot(new InvisibleSlot(container, slotIdx) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
slotsChanged(container); // Trigger listeners on the client.
|
||||
}
|
||||
});
|
||||
addDataSlots(currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link PrintoutMenu} for use a remote (client).
|
||||
*
|
||||
* @param containerId The current container id.
|
||||
* @return The constructed container.
|
||||
*/
|
||||
public static PrintoutMenu createRemote(int containerId) {
|
||||
return new PrintoutMenu(containerId, new SimpleContainer(1), 0, p -> true, new SimpleContainerData(DATA_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link PrintoutMenu} for the printout in the current player's hand.
|
||||
*
|
||||
* @param containerId The current container id.
|
||||
* @param player The player to open the container.
|
||||
* @param hand The hand containing the item.
|
||||
* @return The constructed container.
|
||||
*/
|
||||
public static PrintoutMenu createInHand(int containerId, Player player, InteractionHand hand) {
|
||||
var currentStack = player.getItemInHand(hand);
|
||||
var currentItem = currentStack.getItem();
|
||||
|
||||
var slot = switch (hand) {
|
||||
case MAIN_HAND -> player.getInventory().selected;
|
||||
case OFF_HAND -> Inventory.SLOT_OFFHAND;
|
||||
};
|
||||
return new PrintoutMenu(
|
||||
containerId, player.getInventory(), slot,
|
||||
p -> player.getItemInHand(hand).getItem() == currentItem, new SimpleContainerData(DATA_SIZE)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return valid.test(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clickMenuButton(Player player, int id) {
|
||||
if (id >= PAGE_BUTTON_OFFSET) {
|
||||
var page = Mth.clamp(id - PAGE_BUTTON_OFFSET, 0, PrintoutItem.getPageCount(getPrintout()) - 1);
|
||||
setData(DATA_CURRENT_PAGE, page);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.clickMenuButton(player, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current printout.
|
||||
*
|
||||
* @return The current printout.
|
||||
*/
|
||||
public ItemStack getPrintout() {
|
||||
return getSlot(0).getItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current page.
|
||||
*
|
||||
* @return The current page.
|
||||
*/
|
||||
public int getPage() {
|
||||
return currentPage.get(DATA_CURRENT_PAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(int id, int data) {
|
||||
super.setData(id, data);
|
||||
broadcastChanges(); // Trigger listeners on the client.
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,23 @@
|
||||
|
||||
package dan200.computercraft.shared.media.items;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.network.container.HeldItemContainerData;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
@@ -50,12 +55,30 @@ public class PrintoutItem extends Item {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
if (!world.isClientSide) {
|
||||
new HeldItemContainerData(hand)
|
||||
.open(player, new HeldItemMenu.Factory(ModRegistry.Menus.PRINTOUT.get(), player.getItemInHand(hand), hand));
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
var level = context.getLevel();
|
||||
var blockPos = context.getClickedPos();
|
||||
var blockState = level.getBlockState(blockPos);
|
||||
if (blockState.is(Blocks.LECTERN) && !blockState.getValue(LecternBlock.HAS_BOOK)) {
|
||||
// If we have an empty lectern, place our book into it.
|
||||
if (!level.isClientSide) {
|
||||
CustomLecternBlock.replaceLectern(level, blockPos, blockState, context.getItemInHand());
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), player.getItemInHand(hand));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
var stack = player.getItemInHand(hand);
|
||||
if (!world.isClientSide) {
|
||||
var title = getTitle(stack);
|
||||
var displayTitle = Strings.isNullOrEmpty(title) ? stack.getDisplayName() : Component.literal(title);
|
||||
player.openMenu(new SimpleMenuProvider((id, playerInventory, p) -> PrintoutMenu.createInHand(id, p, hand), displayTitle));
|
||||
}
|
||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack);
|
||||
}
|
||||
|
||||
private ItemStack createFromTitleAndText(@Nullable String title, @Nullable String[] text, @Nullable String[] colours) {
|
||||
|
||||
@@ -27,7 +27,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
||||
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
||||
clientId = computer.getInstanceUUID();
|
||||
state = computer.getState();
|
||||
lightState = computer.getLight();
|
||||
lightState = computer.getBrain().getLight();
|
||||
terminal = sendTerminal ? computer.getTerminalState() : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.network.container;
|
||||
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
|
||||
/**
|
||||
* Opens a printout GUI based on the currently held item.
|
||||
*
|
||||
* @see HeldItemMenu
|
||||
* @see PrintoutItem
|
||||
*/
|
||||
public class HeldItemContainerData implements ContainerData {
|
||||
private final InteractionHand hand;
|
||||
|
||||
public HeldItemContainerData(InteractionHand hand) {
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
public HeldItemContainerData(FriendlyByteBuf buffer) {
|
||||
hand = buffer.readEnum(InteractionHand.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
buf.writeEnum(hand);
|
||||
}
|
||||
|
||||
public InteractionHand getHand() {
|
||||
return hand;
|
||||
}
|
||||
}
|
||||
@@ -96,11 +96,13 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
||||
|
||||
@Override
|
||||
public void clearRemoved() {
|
||||
super.clearRemoved();
|
||||
updateMedia();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
super.setRemoved();
|
||||
if (recordPlaying) stopRecord();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,10 @@ public abstract class AbstractFluidMethods<T> implements GenericPeripheral {
|
||||
) throws LuaException;
|
||||
|
||||
/**
|
||||
* Move a fluid from a connected fluid container into this oneone.
|
||||
* Move a fluid from a connected fluid container into this one.
|
||||
* <p>
|
||||
* This allows you to pull fluid in the current fluid container from another container <em>on the same wired
|
||||
* network</em>. Both containers must attached to wired modems which are connected via a cable.
|
||||
* network</em>. Both containers must be attached to wired modems which are connected via a cable.
|
||||
*
|
||||
* @param to Container to move fluid to.
|
||||
* @param computer The current computer.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.peripheral.generic.methods;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package dan200.computercraft.shared.peripheral.monitor;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.peripheral.AttachedComputerSet;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
@@ -25,9 +26,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MonitorBlockEntity extends BlockEntity {
|
||||
@@ -53,7 +51,7 @@ public class MonitorBlockEntity extends BlockEntity {
|
||||
private @Nullable ClientMonitor clientMonitor;
|
||||
|
||||
private @Nullable MonitorPeripheral peripheral;
|
||||
private final Set<IComputerAccess> computers = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private final AttachedComputerSet computers = new AttachedComputerSet();
|
||||
|
||||
private boolean needsUpdate = false;
|
||||
private boolean needsValidating = false;
|
||||
@@ -487,7 +485,7 @@ public class MonitorBlockEntity extends BlockEntity {
|
||||
var monitor = getLoadedMonitor(x, y).getMonitor();
|
||||
if (monitor == null) continue;
|
||||
|
||||
for (var computer : monitor.computers) fun.accept(computer);
|
||||
computers.forEach(fun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.LuaTable;
|
||||
import dan200.computercraft.api.peripheral.AttachedComputerSet;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
@@ -33,7 +33,10 @@ import net.minecraft.world.item.RecordItem;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
|
||||
@@ -60,7 +63,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
public static final int SAMPLE_RATE = 48000;
|
||||
|
||||
private final UUID source = UUID.randomUUID();
|
||||
private final @GuardedBy("computers") Set<IComputerAccess> computers = new HashSet<>();
|
||||
private final AttachedComputerSet computers = new AttachedComputerSet();
|
||||
|
||||
private long clock = 0;
|
||||
private long lastPositionTime;
|
||||
@@ -140,11 +143,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
syncedPosition(position);
|
||||
|
||||
// And notify computers that we have space for more audio.
|
||||
synchronized (computers) {
|
||||
for (var computer : computers) {
|
||||
computer.queueEvent("speaker_audio_empty", computer.getAttachmentName());
|
||||
}
|
||||
}
|
||||
computers.forEach(c -> c.queueEvent("speaker_audio_empty", c.getAttachmentName()));
|
||||
}
|
||||
|
||||
// Push position updates to any speakers which have ever played a note,
|
||||
@@ -273,16 +272,18 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
* Attempt to stream some audio data to the speaker.
|
||||
* <p>
|
||||
* 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
|
||||
* a [`speaker_audio_empty`] event before trying again.
|
||||
* and played back at 48kHz. If this buffer is full, this function will return {@literal false}. Programs should
|
||||
* wait for a [`speaker_audio_empty`] event before trying to play audio again.
|
||||
* <p>
|
||||
* > [!NOTE]
|
||||
* > 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
|
||||
* > (up to 128×1024), as this reduces the chances of audio stuttering or halting, especially when the server or
|
||||
* > computer is lagging.
|
||||
* 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
|
||||
* (up to 128×1024), as this reduces the chances of audio stuttering or halting, especially when the server or
|
||||
* computer is lagging.
|
||||
* <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 audio The audio data to play.
|
||||
@@ -351,16 +352,12 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
|
||||
@Override
|
||||
public void attach(IComputerAccess computer) {
|
||||
synchronized (computers) {
|
||||
computers.add(computer);
|
||||
}
|
||||
computers.add(computer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach(IComputerAccess computer) {
|
||||
synchronized (computers) {
|
||||
computers.remove(computer);
|
||||
}
|
||||
computers.remove(computer);
|
||||
}
|
||||
|
||||
static double clampVolume(double volume) {
|
||||
|
||||
@@ -6,10 +6,10 @@ package dan200.computercraft.shared.pocket.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -30,14 +30,20 @@ import java.util.Objects;
|
||||
* print("On something else")
|
||||
* end
|
||||
* }</pre>
|
||||
* <p>
|
||||
* ## Recipes
|
||||
* <div class="recipe-container">
|
||||
* <mc-recipe recipe="computercraft:pocket_computer_normal"></mc-recipe>
|
||||
* <mc-recipe recipe="computercraft:pocket_computer_advanced"></mc-recipe>
|
||||
* </div>
|
||||
*
|
||||
* @cc.module pocket
|
||||
*/
|
||||
public class PocketAPI implements ILuaAPI {
|
||||
private final PocketServerComputer computer;
|
||||
private final IPocketAccess pocket;
|
||||
|
||||
public PocketAPI(PocketServerComputer computer) {
|
||||
this.computer = computer;
|
||||
public PocketAPI(IPocketAccess pocket) {
|
||||
this.pocket = pocket;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,10 +62,10 @@ public class PocketAPI implements ILuaAPI {
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public final Object[] equipBack() {
|
||||
var entity = computer.getEntity();
|
||||
var entity = pocket.getEntity();
|
||||
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
||||
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
|
||||
// one. We start from the position the item is currently in and loop round to the start.
|
||||
@@ -73,7 +79,7 @@ public class PocketAPI implements ILuaAPI {
|
||||
if (previousUpgrade != null) storeItem(player, previousUpgrade.getUpgradeItem());
|
||||
|
||||
// Set the new upgrade
|
||||
computer.setUpgrade(newUpgrade);
|
||||
pocket.setUpgrade(newUpgrade);
|
||||
|
||||
return new Object[]{ true };
|
||||
}
|
||||
@@ -87,13 +93,13 @@ public class PocketAPI implements ILuaAPI {
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public final Object[] unequipBack() {
|
||||
var entity = computer.getEntity();
|
||||
var entity = pocket.getEntity();
|
||||
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" };
|
||||
|
||||
computer.setUpgrade(null);
|
||||
pocket.setUpgrade(null);
|
||||
|
||||
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;
|
||||
|
||||
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.api.component.ComputerComponents;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
@@ -18,169 +13,81 @@ import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import dan200.computercraft.shared.util.ComponentMap;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
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 net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.Set;
|
||||
|
||||
public class PocketServerComputer extends ServerComputer implements IPocketAccess {
|
||||
private @Nullable IPocketUpgrade upgrade;
|
||||
private @Nullable Entity entity;
|
||||
private ItemStack stack = ItemStack.EMPTY;
|
||||
|
||||
private int lightColour = -1;
|
||||
/**
|
||||
* A {@link ServerComputer}-subclass for {@linkplain PocketComputerItem pocket computers}.
|
||||
* <p>
|
||||
* This extends default {@link ServerComputer} behaviour by also syncing pocket computer state to nearby players, and
|
||||
* syncing the terminal to the current player.
|
||||
* <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.
|
||||
private int oldLightColour = -1;
|
||||
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) {
|
||||
super(world, position, computerID, label, family, Config.pocketTermWidth, Config.pocketTermHeight);
|
||||
PocketServerComputer(PocketBrain brain, PocketHolder holder, int computerID, @Nullable String label, ComputerFamily family) {
|
||||
super(
|
||||
holder.level(), holder.blockPos(), computerID, label, family, Config.pocketTermWidth, Config.pocketTermHeight,
|
||||
ComponentMap.builder().add(ComputerComponents.POCKET, brain).build()
|
||||
);
|
||||
this.brain = brain;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
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
|
||||
public int getColour() {
|
||||
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) setPosition((ServerLevel) entity.level(), 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();
|
||||
}
|
||||
public PocketBrain getBrain() {
|
||||
return brain;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tickServer() {
|
||||
super.tickServer();
|
||||
|
||||
// Find any players which have gone missing and remove them from the tracking list.
|
||||
tracking.removeIf(player -> !player.isAlive() || player.level() != getLevel());
|
||||
// Get the new set of players tracking the current position.
|
||||
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.
|
||||
var state = getState();
|
||||
if (oldLightColour != lightColour || oldComputerState != state) {
|
||||
var light = brain.getLight();
|
||||
if (oldLightColour != light || oldComputerState != state) {
|
||||
oldComputerState = state;
|
||||
oldLightColour = lightColour;
|
||||
oldLightColour = light;
|
||||
|
||||
// Broadcast the state to all players
|
||||
tracking.addAll(getLevel().players());
|
||||
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), tracking);
|
||||
} else {
|
||||
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), newTracking);
|
||||
} else if (trackingChanged) {
|
||||
// Broadcast the state to new players.
|
||||
List<ServerPlayer> added = new ArrayList<>();
|
||||
for (var player : getLevel().players()) {
|
||||
if (tracking.add(player)) added.add(player);
|
||||
}
|
||||
var added = newTracking.stream().filter(x -> !tracking.contains(x)).toList();
|
||||
if (!added.isEmpty()) {
|
||||
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), added);
|
||||
}
|
||||
}
|
||||
|
||||
if (trackingChanged) tracking = Set.copyOf(newTracking);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void 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.
|
||||
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), player);
|
||||
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), holder.entity());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,27 +14,31 @@ import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
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.items.IComputerItem;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
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.inventory.PocketComputerMenuProvider;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.tooltip.TooltipComponent;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
@@ -43,6 +47,7 @@ import net.minecraft.world.level.Level;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem {
|
||||
@@ -72,12 +77,33 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean tick(ItemStack stack, 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.updateValues(entity, stack, upgrade);
|
||||
// Update pocket 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
|
||||
var id = computer.getID();
|
||||
@@ -99,21 +125,24 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
stack.getOrCreateTag().putBoolean(NBT_ON, on);
|
||||
}
|
||||
|
||||
// Update pocket upgrade
|
||||
if (upgrade != null) upgrade.update(computer, computer.getPeripheral(ComputerSide.BACK));
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack stack, Level world, Entity entity, int slotNum, boolean selected) {
|
||||
if (world.isClientSide) return;
|
||||
Container inventory = entity instanceof Player player ? player.getInventory() : null;
|
||||
var computer = createServerComputer((ServerLevel) world, entity, inventory, stack);
|
||||
computer.keepAlive();
|
||||
public void inventoryTick(ItemStack stack, Level world, Entity entity, int compartmentSlot, boolean selected) {
|
||||
// This (in vanilla at least) is only called for players. Don't bother to handle other entities.
|
||||
if (world.isClientSide || !(entity instanceof ServerPlayer player)) return;
|
||||
|
||||
var changed = tick(stack, entity, computer);
|
||||
if (changed && inventory != null) inventory.setChanged();
|
||||
// Find the actual slot the item exists in, aborting if it can't be found.
|
||||
var slot = InventoryUtil.getInventorySlotFromCompartment(player, compartmentSlot, stack);
|
||||
if (slot < 0) return;
|
||||
|
||||
// If we're in the inventory, create a computer and keep it alive.
|
||||
var holder = new PocketHolder.PlayerHolder(player, slot);
|
||||
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
|
||||
brain.computer().keepAlive();
|
||||
|
||||
tick(stack, holder, brain);
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
@@ -121,8 +150,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
var level = entity.level();
|
||||
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);
|
||||
if (computer != null && tick(stack, entity, computer)) entity.setItem(stack.copy());
|
||||
if (computer != null) tick(stack, new PocketHolder.ItemEntityHolder(entity), computer.getBrain());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -130,14 +162,18 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
var stack = player.getItemInHand(hand);
|
||||
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();
|
||||
|
||||
var stop = false;
|
||||
var upgrade = getUpgrade(stack);
|
||||
if (upgrade != null) {
|
||||
computer.updateValues(player, stack, upgrade);
|
||||
stop = upgrade.onRightClick(world, computer, computer.getPeripheral(ComputerSide.BACK));
|
||||
brain.updateHolder(holder);
|
||||
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) {
|
||||
@@ -161,6 +197,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TooltipComponent> getTooltipImage(ItemStack stack) {
|
||||
var id = getInstanceID(stack);
|
||||
return id == null ? Optional.empty() : Optional.of(new PocketTooltipComponent(id, family));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag flag) {
|
||||
@@ -187,40 +228,49 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
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 computer = (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
||||
if (computer == null) {
|
||||
var computerID = getComputerID(stack);
|
||||
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();
|
||||
{
|
||||
var computer = getServerComputer(registry, stack);
|
||||
if (computer != null) return computer.getBrain();
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
@@ -306,8 +356,4 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
compound.put(NBT_UPGRADE_INFO, upgrade.data().copy());
|
||||
}
|
||||
}
|
||||
|
||||
public static CompoundTag getUpgradeInfo(ItemStack stack) {
|
||||
return stack.getOrCreateTagElement(NBT_UPGRADE_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.pocket.items;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.world.inventory.tooltip.TooltipComponent;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A tooltip computer describing a pocket computer.
|
||||
* <p>
|
||||
* This has no behaviour on its own. When rendering, this is converted to an equivalent client-side component,
|
||||
* that renders the computer's terminal.
|
||||
*
|
||||
* @param id The instance ID of this pocket computer.
|
||||
* @param family The family of this pocket computer.
|
||||
* @see PocketComputerItem#getTooltipImage(ItemStack)
|
||||
* @see dan200.computercraft.client.pocket.PocketClientTooltipComponent
|
||||
*/
|
||||
public record PocketTooltipComponent(UUID id, ComputerFamily family) implements TooltipComponent {
|
||||
}
|
||||
@@ -31,8 +31,6 @@ public class PocketModem extends AbstractPocketUpgrade {
|
||||
public void update(IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||
if (!(peripheral instanceof PocketModemPeripheral modem)) return;
|
||||
|
||||
modem.setLocation(access);
|
||||
|
||||
var state = modem.getModemState();
|
||||
if (state.pollChanged()) access.setLight(state.isOpen() ? 0xBA0000 : -1);
|
||||
}
|
||||
|
||||
@@ -14,31 +14,21 @@ import net.minecraft.world.phys.Vec3;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PocketModemPeripheral extends WirelessModemPeripheral {
|
||||
private @Nullable Level level = null;
|
||||
private Vec3 position = Vec3.ZERO;
|
||||
private final IPocketAccess access;
|
||||
|
||||
public PocketModemPeripheral(boolean advanced, IPocketAccess access) {
|
||||
super(new ModemState(), advanced);
|
||||
setLocation(access);
|
||||
}
|
||||
|
||||
void setLocation(IPocketAccess access) {
|
||||
var entity = access.getEntity();
|
||||
if (entity != null) {
|
||||
level = entity.level();
|
||||
position = entity.getEyePosition(1);
|
||||
}
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
if (level == null) throw new IllegalStateException("Using modem before position has been defined");
|
||||
return level;
|
||||
return access.getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getPosition() {
|
||||
return position;
|
||||
return access.getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,15 +8,11 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||
private final IPocketAccess access;
|
||||
private @Nullable Level level;
|
||||
private Vec3 position = Vec3.ZERO;
|
||||
|
||||
public PocketSpeakerPeripheral(IPocketAccess access) {
|
||||
this.access = access;
|
||||
@@ -25,7 +21,7 @@ public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||
@Override
|
||||
public SpeakerPosition getPosition() {
|
||||
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
|
||||
@@ -35,12 +31,6 @@ public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
var entity = access.getEntity();
|
||||
if (entity != null) {
|
||||
level = entity.level();
|
||||
position = entity.position();
|
||||
}
|
||||
|
||||
super.update();
|
||||
|
||||
access.setLight(madeSound() ? 0x3320fc : -1);
|
||||
|
||||
@@ -59,7 +59,6 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
||||
|
||||
if (computer.isEmpty()) return ItemStack.EMPTY;
|
||||
|
||||
var itemComputer = (PocketComputerItem) computer.getItem();
|
||||
if (PocketComputerItem.getUpgrade(computer) != null) return ItemStack.EMPTY;
|
||||
|
||||
// Check for upgrades around the item
|
||||
@@ -81,10 +80,9 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
||||
if (upgrade == null) return ItemStack.EMPTY;
|
||||
|
||||
// Construct the new stack
|
||||
var computerID = itemComputer.getComputerID(computer);
|
||||
var label = itemComputer.getLabel(computer);
|
||||
var colour = itemComputer.getColour(computer);
|
||||
return itemComputer.create(computerID, label, colour, upgrade);
|
||||
var newStack = computer.copyWithCount(1);
|
||||
PocketComputerItem.setUpgrade(newStack, upgrade);
|
||||
return newStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,7 +11,6 @@ import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.AbstractInventoryMethods;
|
||||
import dan200.computercraft.shared.turtle.core.*;
|
||||
|
||||
@@ -48,18 +47,24 @@ import java.util.Optional;
|
||||
* <p>
|
||||
* ## Turtle upgrades
|
||||
* While a normal turtle can move about the world and place blocks, its functionality is limited. Thankfully, turtles
|
||||
* can be upgraded with *tools* and [peripherals][`peripheral`]. Turtles have two upgrade slots, one on the left and right
|
||||
* sides. Upgrades can be equipped by crafting a turtle with the upgrade, or calling the [`turtle.equipLeft`]/[`turtle.equipRight`]
|
||||
* functions.
|
||||
* can be upgraded with upgrades. Turtles have two upgrade slots, one on the left and right sides. Upgrades can be
|
||||
* equipped by crafting a turtle with the upgrade, or calling the [`turtle.equipLeft`]/[`turtle.equipRight`] functions.
|
||||
* <p>
|
||||
* Turtle tools allow you to break blocks ([`turtle.dig`]) and attack entities ([`turtle.attack`]). Some tools are more
|
||||
* suitable to a task than others. For instance, a diamond pickaxe can break every block, while a sword does more
|
||||
* damage. Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||
* By default, any diamond tool may be used as an upgrade (though more may be added with [datapacks]). The diamond
|
||||
* pickaxe may be used to break blocks (with [`turtle.dig`]), while the sword can attack entities ([`turtle.attack`]).
|
||||
* Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||
* <p>
|
||||
* Peripherals (such as the [wireless modem][`modem`] or [`speaker`]) can also be equipped as upgrades. These are then
|
||||
* accessible by accessing the `"left"` or `"right"` peripheral.
|
||||
* Some peripherals (namely [speakers][`speaker`] and Ender and Wireless [modems][`modem`]) can also be equipped as
|
||||
* upgrades. These are then accessible by accessing the `"left"` or `"right"` peripheral.
|
||||
* <p>
|
||||
* ## Recipes
|
||||
* <div class="recipe-container">
|
||||
* <mc-recipe recipe="computercraft:turtle_normal"></mc-recipe>
|
||||
* <mc-recipe recipe="computercraft:turtle_advanced"></mc-recipe>
|
||||
* </div>
|
||||
* <p>
|
||||
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
|
||||
* [datapacks]: https://datapacks.madefor.cc ""
|
||||
*
|
||||
* @cc.module turtle
|
||||
* @cc.since 1.3
|
||||
@@ -68,8 +73,8 @@ public class TurtleAPI implements ILuaAPI {
|
||||
private final MetricsObserver metrics;
|
||||
private final TurtleAccessInternal turtle;
|
||||
|
||||
public TurtleAPI(ServerComputer computer, TurtleAccessInternal turtle) {
|
||||
this.metrics = computer.getMetrics();
|
||||
public TurtleAPI(MetricsObserver metrics, TurtleAccessInternal turtle) {
|
||||
this.metrics = metrics;
|
||||
this.turtle = turtle;
|
||||
}
|
||||
|
||||
@@ -346,7 +351,7 @@ public class TurtleAPI implements ILuaAPI {
|
||||
* For instance, if a slot contains 13 blocks of dirt, it has room for another 51.
|
||||
*
|
||||
* @param slot The slot we wish to check. Defaults to the {@link #select selected slot}.
|
||||
* @return The space left in in this slot.
|
||||
* @return The space left in this slot.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
*/
|
||||
@LuaFunction
|
||||
|
||||
@@ -125,11 +125,15 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
|
||||
public final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||
if (state.is(newState.getBlock())) return;
|
||||
|
||||
if (!level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle && !turtle.hasMoved()) {
|
||||
Containers.dropContents(level, pos, turtle);
|
||||
}
|
||||
// Most blocks drop items and then remove the BE. However, if a turtle is consuming drops right now, that can
|
||||
// lead to loops where it tries to insert an item back into the inventory. To prevent this, take a reference to
|
||||
// the turtle BE now, remove it, and then drop the items.
|
||||
var turtle = !level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity t && !t.hasMoved()
|
||||
? t : null;
|
||||
|
||||
super.onRemove(state, level, pos, newState, isMoving);
|
||||
|
||||
if (turtle != null) Containers.dropContents(level, pos, turtle);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.turtle.blocks;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.api.component.ComputerComponents;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
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.config.Config;
|
||||
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.inventory.TurtleMenu;
|
||||
import dan200.computercraft.shared.util.ComponentMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.NonNullList;
|
||||
@@ -75,10 +76,9 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
protected ServerComputer createComputer(int id) {
|
||||
var computer = new ServerComputer(
|
||||
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
||||
getFamily(), Config.turtleTermWidth,
|
||||
Config.turtleTermHeight
|
||||
getFamily(), Config.turtleTermWidth, Config.turtleTermHeight,
|
||||
ComponentMap.builder().add(ComputerComponents.TURTLE, brain).build()
|
||||
);
|
||||
computer.addAPI(new TurtleAPI(computer, brain));
|
||||
brain.setupComputer(computer);
|
||||
return computer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.util.FastColor;
|
||||
|
||||
/**
|
||||
* Utilities for working with 32-bit ARGB colours.
|
||||
*
|
||||
* @see FastColor.ARGB32
|
||||
*/
|
||||
public final class ARGB32 {
|
||||
private ARGB32() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha channel to be fully opaque.
|
||||
*
|
||||
* @param colour The colour to make opaque.
|
||||
* @return The fully-opaque colour
|
||||
*/
|
||||
public static int opaque(int colour) {
|
||||
return 0xFF000000 | colour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ARGB32 colour to a {@linkplain FastColor.ABGR32 ABGR32} one.
|
||||
*
|
||||
* @param colour The colour to convert.
|
||||
* @return The converted colour.
|
||||
*/
|
||||
public static int toABGR32(int colour) {
|
||||
// Swap B and R components, converting ARGB32 to ABGR32.
|
||||
return colour & 0xFF00FF00 | (colour & 0xFF0000) >> 16 | (colour & 0xFF) << 16;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,13 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@@ -18,6 +24,42 @@ public final class 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;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a slot inside a player's compartment to a slot in the full player's inventory.
|
||||
* <p>
|
||||
* {@link Inventory#tick()} passes in a slot to {@link Item#inventoryTick(ItemStack, Level, Entity, int, boolean)}.
|
||||
* However, this slot corresponds to the index within the current compartment (items, armour, offhand) and not
|
||||
* the actual slot.
|
||||
* <p>
|
||||
* This method searches the relevant compartments (inventory and offhand, skipping armour) for the stack, returning
|
||||
* its slot if found.
|
||||
*
|
||||
* @param player The player holding the item.
|
||||
* @param slot The slot inside the compartment.
|
||||
* @param stack The stack being ticked.
|
||||
* @return The inventory slot, or {@code -1} if the item could not be found in the inventory.
|
||||
*/
|
||||
public static int getInventorySlotFromCompartment(Player player, int slot, ItemStack stack) {
|
||||
if (stack.isEmpty()) throw new IllegalArgumentException("Cannot search for empty stack");
|
||||
if (player.getInventory().getItem(slot) == stack) return slot;
|
||||
if (player.getInventory().getItem(Inventory.SLOT_OFFHAND) == stack) return Inventory.SLOT_OFFHAND;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static @Nullable Container getEntityContainer(ServerLevel level, BlockPos pos, Direction side) {
|
||||
var vecStart = new Vec3(
|
||||
pos.getX() + 0.5 + 0.6 * side.getStepX(),
|
||||
|
||||
@@ -109,8 +109,9 @@ public final class TickScheduler {
|
||||
return State.UNLOADED;
|
||||
} else {
|
||||
// This should be impossible: either the block entity is at the above position, or it has been removed.
|
||||
if (level.getBlockEntity(pos) != blockEntity) {
|
||||
throw new IllegalStateException("Expected " + blockEntity + " at " + pos);
|
||||
var currentBlockEntity = level.getBlockEntity(pos);
|
||||
if (currentBlockEntity != blockEntity) {
|
||||
throw new IllegalStateException("Expected " + blockEntity + " at " + pos + ", got " + currentBlockEntity);
|
||||
}
|
||||
|
||||
// Otherwise schedule a tick and remove it from the queue.
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"commands.computercraft.track.start.stop": "Führe %s aus um die Aufzeichnung zu stoppen und die Ergebnisse anzusehen",
|
||||
"commands.computercraft.track.start.synopsis": "Startet die Aufzeichnung von Computern",
|
||||
"commands.computercraft.track.stop.action": "Klicke um die Aufzeichnung zu stoppen",
|
||||
"commands.computercraft.track.stop.desc": "Stopt die Aufzeichnung aller Computer Events und Laufzeiten",
|
||||
"commands.computercraft.track.stop.desc": "Stopt die Aufzeichnung aller Computer-Events und Laufzeiten",
|
||||
"commands.computercraft.track.stop.not_enabled": "Momentan werden keine Computer aufgezeichnet",
|
||||
"commands.computercraft.track.stop.synopsis": "Stoppt die Aufzeichnung aller Computer",
|
||||
"commands.computercraft.track.synopsis": "Zeichnet die Laufzeiten von Computern auf.",
|
||||
@@ -75,7 +75,7 @@
|
||||
"gui.computercraft.config.default_computer_settings": "Computer-Standardeinstellungen",
|
||||
"gui.computercraft.config.default_computer_settings.tooltip": "eine mit Komma separierte Liste an standardmäßige Systemeinstellungen für neuen Computern.\nBeispiel: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nwürde jegliche Autovervollständigung deaktivieren.",
|
||||
"gui.computercraft.config.disabled_generic_methods": "Generische Methoden deaktiviert.",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "Eine Liste an generischen Methoden oder Methodenquellen zum deaktivieren.\nGenerische Methoden sind Methoden die zu einem block/block entity hinzugefügt werden, insofern kein expliziter Peripheral Provider\ngefunden wurde. Mitbetroffen sind Inventarmethoden (d.h. inventory.getItemDetail,\ninventory.pushItems) und, wenn in Forge gespielt wird, die fluid_storage und energy_storage\nMethoden.\nMethoden in dieser Liste können entweder Gruppen von Methoden (wie computercraft:inventory)\noder einzelne Methoden (wie computercraft:inventory#pushItems) sein.\n",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "Eine Liste an generischen Methoden oder Methodenquellen zum deaktivieren.\nGenerische Methoden sind Methoden die zu einem Block oder Blockentity hinzugefügt werden, insofern kein expliziter Peripheral Provider\ngefunden wurde. Mitbetroffen sind Inventarmethoden (d.h. inventory.getItemDetail,\ninventory.pushItems) und, wenn in Forge gespielt wird, die fluid_storage und energy_storage\nMethoden.\nMethoden in dieser Liste können entweder Gruppen von Methoden (wie computercraft:inventory)\noder einzelne Methoden (wie computercraft:inventory#pushItems) sein.\n",
|
||||
"gui.computercraft.config.execution": "Ausführung",
|
||||
"gui.computercraft.config.execution.computer_threads": "Computer Threads",
|
||||
"gui.computercraft.config.execution.computer_threads.tooltip": "Setzt die Anzahl an Hintergrundprozessen fest, auf denen Computer laufen können. Eine hohe Nummer heißt,\ndass mehrere Computer zur selben Zeit laufen können, jedoch aber auch ggf. mehr Verzögerungen verursachen. Bitte beachte, dass manche mods\nnicht mit einer Anzahl an Hintergrundprozessen laufen, die höher als 1 ist. Benutze also mit bedacht.\nBereich: > 1",
|
||||
@@ -133,15 +133,15 @@
|
||||
"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'",
|
||||
"argument.computercraft.computer.label": "Computername",
|
||||
"argument.computercraft.unknown_computer_family": "Unbekannte Computerfamilie '%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.global_upload": "Globale Uploadbandbreite",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Die maximale Bandbreite aller Computer in bytes/s mit der heruntergeladen werden kann.\nBereich: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_download": "Globale Downloadbandbreite",
|
||||
"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"
|
||||
"argument.computercraft.computer.family": "Computerfamilie",
|
||||
"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 Bandbreite aller Computer in bytes/s mit der hochgeladen werden kann.\nBereich: > 1"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
{
|
||||
"argument.computercraft.argument_expected": "引数が期待される",
|
||||
"argument.computercraft.computer.distance": "エンティティまでの距離",
|
||||
"argument.computercraft.computer.family": "コンピューターファミリー",
|
||||
"argument.computercraft.computer.id": "コンピューターID",
|
||||
"argument.computercraft.computer.instance": "固有インスタンスID",
|
||||
"argument.computercraft.computer.label": "コンピューターラベル",
|
||||
"argument.computercraft.computer.many_matching": "'%s'に一致する複数のコンピューター (インスタンス %s)",
|
||||
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピュータはありません",
|
||||
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピューターはありません",
|
||||
"argument.computercraft.tracking_field.no_field": "'%s'は未知のフィールドです",
|
||||
"argument.computercraft.unknown_computer_family": "'%s'は未知のコンピューターファミリーです",
|
||||
"block.computercraft.cable": "ネットワークケーブル",
|
||||
"block.computercraft.computer_advanced": "高度なコンピューター",
|
||||
"block.computercraft.computer_command": "コマンドコンピューター",
|
||||
@@ -12,9 +18,9 @@
|
||||
"block.computercraft.monitor_normal": "モニター",
|
||||
"block.computercraft.printer": "プリンター",
|
||||
"block.computercraft.speaker": "スピーカー",
|
||||
"block.computercraft.turtle_advanced": "高度なタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded": "高度な%sタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded_twice": "高度な%s%sタートル",
|
||||
"block.computercraft.turtle_advanced": "アドバンスドタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded": "アドバンスド%sタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded_twice": "アドバンスド%s%sタートル",
|
||||
"block.computercraft.turtle_normal": "タートル",
|
||||
"block.computercraft.turtle_normal.upgraded": "%sタートル",
|
||||
"block.computercraft.turtle_normal.upgraded_twice": "%s%sタートル",
|
||||
@@ -24,11 +30,11 @@
|
||||
"block.computercraft.wireless_modem_normal": "無線モデム",
|
||||
"chat.computercraft.wired_modem.peripheral_connected": "周辺の\"%s\"のネットワークに接続されました",
|
||||
"chat.computercraft.wired_modem.peripheral_disconnected": "周辺の\"%s\"のネットワークから切断されました",
|
||||
"commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
|
||||
"commands.computercraft.dump.action": "このコンピュータの詳細を表示します",
|
||||
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します",
|
||||
"commands.computercraft.dump.synopsis": "コンピュータの状態を表示します。",
|
||||
"commands.computercraft.desc": "/computercraft コマンドは、コンピューターとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
|
||||
"commands.computercraft.dump.action": "このコンピューターの詳細を表示します",
|
||||
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピューターのインスタンスID (例えば 123), コンピューターID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.dump.open_path": "このコンピューターのファイルを表示します",
|
||||
"commands.computercraft.dump.synopsis": "コンピューターの状態を表示します。",
|
||||
"commands.computercraft.generic.additional_rows": "%d行を追加…",
|
||||
"commands.computercraft.generic.exception": "未処理の例外 (%s)",
|
||||
"commands.computercraft.generic.no": "N",
|
||||
@@ -39,65 +45,182 @@
|
||||
"commands.computercraft.help.no_children": "%s にサブコマンドはありません",
|
||||
"commands.computercraft.help.no_command": "%s というコマンドはありません",
|
||||
"commands.computercraft.help.synopsis": "特定のコマンドのヘルプを提供します",
|
||||
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
|
||||
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピューターフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
|
||||
"commands.computercraft.queue.synopsis": "computer_command インベントをコマンドコンピューターに送信します",
|
||||
"commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.shutdown.desc": "指定されたコンピューター、指定されていない場合はすべてのコンピューターをシャットダウンします。 コンピューターのインスタンスID (例えば 123), コンピューターID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.shutdown.done": "%s/%s コンピューターをシャットダウンしました",
|
||||
"commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。",
|
||||
"commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。",
|
||||
"commands.computercraft.shutdown.synopsis": "コンピューターをリモートでシャットダウンする。",
|
||||
"commands.computercraft.synopsis": "コンピューターを制御するためのさまざまなコマンド。",
|
||||
"commands.computercraft.tp.action": "このコンピューターへテレポートします",
|
||||
"commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。",
|
||||
"commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
|
||||
"commands.computercraft.tp.desc": "コンピューターの場所にテレポート.コンピューターのインスタンスID(例えば 123)またはコンピューターID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.tp.synopsis": "特定のコンピューターにテレポート。",
|
||||
"commands.computercraft.track.desc": "コンピューターの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
|
||||
"commands.computercraft.track.dump.computer": "コンピューター",
|
||||
"commands.computercraft.track.dump.desc": "コンピュータの最新の追跡結果をダンプしてください。",
|
||||
"commands.computercraft.track.dump.desc": "コンピューターの最新の追跡結果をダンプしてください。",
|
||||
"commands.computercraft.track.dump.no_timings": "利用可能なタイミングはありません",
|
||||
"commands.computercraft.track.dump.synopsis": "最新の追跡結果をダンプしてください",
|
||||
"commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
|
||||
"commands.computercraft.track.start.desc": "すべてのコンピューターの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
|
||||
"commands.computercraft.track.start.stop": "トラッキングを停止して結果を表示するには %s を実行してください",
|
||||
"commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します",
|
||||
"commands.computercraft.track.start.synopsis": "すべてのコンピューターの追跡を開始します。",
|
||||
"commands.computercraft.track.stop.action": "追跡を中止するためにクリックしてください",
|
||||
"commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します",
|
||||
"commands.computercraft.track.stop.not_enabled": "現在コンピュータを追跡していません",
|
||||
"commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します",
|
||||
"commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。",
|
||||
"commands.computercraft.turn_on.desc": "指定されているコンピュータを起動します。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.track.stop.desc": "すべてのコンピューターのイベントと実行時間の追跡を停止します",
|
||||
"commands.computercraft.track.stop.not_enabled": "現在コンピューターを追跡していません",
|
||||
"commands.computercraft.track.stop.synopsis": "すべてのコンピューターの追跡を停止します。",
|
||||
"commands.computercraft.track.synopsis": "コンピューターの実行時間を追跡します。",
|
||||
"commands.computercraft.turn_on.desc": "指定されているコンピューターを起動します。 コンピューターのインスタンスID (例えば 123), コンピューターID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.turn_on.done": "%s/%s コンピューターを起動しました",
|
||||
"commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。",
|
||||
"commands.computercraft.view.action": "このコンピュータを見ます",
|
||||
"commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.view.action": "このコンピューターを見ます",
|
||||
"commands.computercraft.view.desc": "コンピューターのターミナルを開き、コンピューターのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピューターのインスタンスID(例えば 123)またはコンピューターID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.view.not_player": "非プレイヤー用のターミナルを開くことができません",
|
||||
"commands.computercraft.view.synopsis": "コンピュータのターミナルを表示します。",
|
||||
"gui.computercraft.pocket_computer_overlay": "ポケットコンピュータを開いています。 ESCを押して閉じます。",
|
||||
"gui.computercraft.tooltip.computer_id": "コンピュータID: %s",
|
||||
"commands.computercraft.view.synopsis": "コンピューターのターミナルを表示します。",
|
||||
"gui.computercraft.config.command_require_creative": "コマンドコンピューターはクリエイティブモードが必要です。",
|
||||
"gui.computercraft.config.command_require_creative.tooltip": "コマンドコンピューターと対話するためにはプレイヤーがクリエイティブモードかつOP権限保有者でなければなりません。\nこれはバニラのコマンドブロックのデフォルト挙動です。",
|
||||
"gui.computercraft.config.computer_space_limit": "コンピューターの限容制限(バイト)",
|
||||
"gui.computercraft.config.computer_space_limit.tooltip": "コンピューターとタートルのディスク容量制限、バイト単位。",
|
||||
"gui.computercraft.config.default_computer_settings": "デフォルトのコンピューター設定",
|
||||
"gui.computercraft.config.default_computer_settings.tooltip": "新しいコンピューターに設定するデフォルトのシステム設定のコンマ区切りのリスト。\n例: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nは全ての自動補完を無効にします。",
|
||||
"gui.computercraft.config.disabled_generic_methods": "無効化するジェネリックメソッド",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "無効にするジェネリックメソッドまたはメソッドソースのリスト。\nジェネリックメソッドは、明示的な周辺プロバイダがない場合にブロック/ブロックエンティティに追加されるメソッドです。\nこれには、インベントリメソッド (inventory.getItemDetail や inventory.pushItems) や、(Forgeであれば)fluid_storage や energy_storage メソッドが含まれます。\nこのリストに含まれるメソッドは、メソッド群全体 (computercraft:inventory) か、単一のメソッド (computercraft:inventory#pushItems) のどちらかになります。",
|
||||
"gui.computercraft.config.execution": "実行",
|
||||
"gui.computercraft.config.execution.computer_threads": "コンピューターのスレッド",
|
||||
"gui.computercraft.config.execution.computer_threads.tooltip": "コンピューターが実行できるスレッド数を設定する。\n数値が高いほどより多くのコンピューターが一度に実行できますが、ラグを誘発する可能性があります。\nスレッド数が1より大きいと動作しないMODもあるので注意してください\n範囲: > 1",
|
||||
"gui.computercraft.config.execution.max_main_computer_time": "サーバーティックのコンピューター時間上限",
|
||||
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "コンピューターが1ティックで実行できる理想的な最大時間、ミリ秒単位。\nどれぐらい時間がかかるか不明であるため、上限を超える可能性があることに注意。\nこれは平均時間の上限を目的とする。\n範囲: > 1",
|
||||
"gui.computercraft.config.execution.max_main_global_time": "サーバーティックのグローバル回数上限",
|
||||
"gui.computercraft.config.execution.max_main_global_time.tooltip": "1ティックでタスクを実行できる最大時間、ミリ秒単位。\nどれぐらい時間がかかるか不明であるため、上限を超える可能性があることに注意。\nこれは平均回数の上限を時間とする。",
|
||||
"gui.computercraft.config.execution.tooltip": "コンピュータの実行挙動を制御する。\nこれは主にサーバーを微調整するためのもので、一般的には触る必要はない。",
|
||||
"gui.computercraft.config.floppy_space_limit": "フロッピーディスクの容量制限(バイト)",
|
||||
"gui.computercraft.config.floppy_space_limit.tooltip": "フロッピーディスクのディスク容量制限(バイト単位)。",
|
||||
"gui.computercraft.config.http": "HTTP",
|
||||
"gui.computercraft.config.http.bandwidth": "帯域幅",
|
||||
"gui.computercraft.config.http.bandwidth.global_download": "グローバルダウンロード制限",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "1秒間にダウンロードできるバイト数。これはすべてのコンピュータで共有されます。(byte/s).\n範囲: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload": "グローバルアップロード制限",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "1秒間にアップロードできるバイト数。これはすべてのコンピュータで共有されます。(byte/s).\n範囲: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.tooltip": "コンピュータが使用する帯域幅を制限する。",
|
||||
"gui.computercraft.config.http.enabled": "HTTP APIを有効にする",
|
||||
"gui.computercraft.config.http.enabled.tooltip": "コンピュータの\"http\" APIを有効にする。これを無効にすると、多くのユーザーが依存している\"pastebin\"と\"wget\"プログラムも無効になる。\nこのオプションはオンのままにしておき、よりきめ細かい制御を行うために\"ルール\"の設定オプションを使用することを推奨する。",
|
||||
"gui.computercraft.config.http.max_requests": "最大同時リクエスト数",
|
||||
"gui.computercraft.config.http.max_requests.tooltip": "コンピューターが一度にできるhttpリクエストの数。追加のリクエストはキューに入れられ、実行中のリクエストが終了したときに送信されます。無制限の場合は0に設定します。\n範囲: > 0",
|
||||
"gui.computercraft.config.http.max_websockets": "最大同時ウェブソケット数",
|
||||
"gui.computercraft.config.http.max_websockets.tooltip": "コンピュータが一度に開くことのできるウェブソケットの数。\n範囲: > 1",
|
||||
"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.tooltip": "HTTPとウェブソケットリクエストをプロキシサーバ経由でトンネリングする。\"use_proxy\"が\"true\"(デフォルトでは\"off\")に設定されている HTTPルールにのみ影響します。\nプロキシに認証が必要な場合は、\"computercraft-server.toml\"と同じディレクトリに\"myuser:mypassword\"のようにユーザー名とパスワードをコロンで区切って記述した\"computercraft-proxy.pw\"ファイルを作成します。\nSOCKS4プロキシでは、ユーザー名のみが必要です。",
|
||||
"gui.computercraft.config.http.proxy.type": "プロキシ種類",
|
||||
"gui.computercraft.config.http.proxy.type.tooltip": "使用するプロキシの種類。\n許可された値: HTTP, HTTPS, SOCKS4, SOCKS5",
|
||||
"gui.computercraft.config.http.rules": "ルールの許可/拒否",
|
||||
"gui.computercraft.config.http.rules.tooltip": "特定のドメインやIPに対する\"http\" APIの動作を制御するルールのリスト。それぞれのルールはホスト名とオプションのポートに対して対応し、 リクエストに対していくつかのプロパティを設定します。 ルールは順番に評価され、前のルールが後のルールを上書きします。\n\n有効なプロパティ:\n - \"host\" (必須): このルールが対応するドメインまたはIPアドレス。 これはドメイン名(\"pastebin.com\")、ワイルドカード(\"*.pastebin.com\")、あるいはCIDR表記(\"127.0.0.0/8\")となります。\n - \"port\" (オプション): 80や 443など、特定のポートに対するリクエストにのみマッチする。.\n\n - \"action\" (オプション): このリクエストを許可するか拒否するか。\n - \"max_download\" (オプション): このリクエストでコンピューターがダウンロードできる最大サイズ(バイト単位)。\n - \"max_upload\" (オプション): このリクエストでコンピューターがアップロードできる最大サイズ(バイト)。\n - \"max_websocket_message\" (オプション): コンピューターが1つのウェブソケット・パケットで送受信できる最大サイズ(バイト)。\n - \"use_proxy\" (オプション): HTTP/SOCKSプロキシが設定されている場合は、その使用を有効にする。",
|
||||
"gui.computercraft.config.http.tooltip": "HTTP APIの制御",
|
||||
"gui.computercraft.config.http.websocket_enabled": "ウェブソケットを有効にする",
|
||||
"gui.computercraft.config.http.websocket_enabled.tooltip": "httpウェブソケットの使用を有効にする。これには、\"http_enable\"オプションもtrueである必要があります。.",
|
||||
"gui.computercraft.config.log_computer_errors": "コンピュータのエラーを記録する",
|
||||
"gui.computercraft.config.log_computer_errors.tooltip": "周辺機器やその他のLuaオブジェクトが発生させた例外を記録します。これにより、MODの作者が問題をデバッグしやすくなりますが、バグを含んだメソッドを使用した場合、ログスパムが発生する可能性があります。",
|
||||
"gui.computercraft.config.maximum_open_files": "1台のコンピューターで開けるファイルの最大数",
|
||||
"gui.computercraft.config.maximum_open_files.tooltip": "コンピューターが同時に開くことができるファイルの数を設定します。無制限の場合は0に設定します。\n範囲: > 0",
|
||||
"gui.computercraft.config.monitor_distance": "モニター距離",
|
||||
"gui.computercraft.config.monitor_distance.tooltip": "モニターがレンダリングする最大距離。デフォルトは標準的なタイルエンティティの制限値ですが、より大きなモニタを構築したい場合は拡張することができます。\n範囲: 16 ~ 1024",
|
||||
"gui.computercraft.config.monitor_renderer": "モニターレンダラー",
|
||||
"gui.computercraft.config.monitor_renderer.tooltip": "モニターに使用するレンダラー。一般的に、この値は\"best\"に保つたれるべきです。 - モニターにパフォーマンス上の問題がある場合は、別のレンダラーを試してみるとよいでしょう。\n許可された値: BEST, TBO, VBO",
|
||||
"gui.computercraft.config.peripheral": "周辺機器",
|
||||
"gui.computercraft.config.peripheral.command_block_enabled": "コマンドブロック周辺機器を有効にする",
|
||||
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "コマンドブロック周辺機器サポートを有効にする",
|
||||
"gui.computercraft.config.peripheral.max_notes_per_tick": "コンピューターが一度に演奏できる最大音符数",
|
||||
"gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "スピーカーが一度に演奏できる最大音符数。\n範囲: > 1",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range": "モデム範囲(高高度)",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "晴天時の最大高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "モデム範囲(高高度、悪天候)",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "悪天候の最大高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_range": "モデム範囲(デフォルト)",
|
||||
"gui.computercraft.config.peripheral.modem_range.tooltip": "晴天時の低高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_range_during_storm": "モデム範囲(悪天候)",
|
||||
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "荒天時の低高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.monitor_bandwidth": "モニター帯域幅",
|
||||
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "1ティックあたりのモニターデータ送信量の上限。注:\n - 帯域幅は圧縮前に測定されるため、クライアントに送信されるデータはより小さくなります。\n - これは、パケットを送信するプレーヤーの数を無視する。1人のプレーヤーのモニターを更新することは、20人に送信するのと同じ帯域幅の制限を消費する。\n - フルサイズのモニターは~25kbのデータを送信する。そのため、デフォルト(1MB)では、1回のティックで~40個のモニターを更新することができる。\n 無効にするには 0 を設定する。\n範囲: > 0",
|
||||
"gui.computercraft.config.peripheral.tooltip": "周辺機器に関する各種オプション。",
|
||||
"gui.computercraft.config.term_sizes": "ターミナルサイズ",
|
||||
"gui.computercraft.config.term_sizes.computer": "コンピューター",
|
||||
"gui.computercraft.config.term_sizes.computer.height": "ターミナルの高さ",
|
||||
"gui.computercraft.config.term_sizes.computer.height.tooltip": "範囲: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.computer.tooltip": "コンピュータの端末サイズ。",
|
||||
"gui.computercraft.config.term_sizes.computer.width": "ターミナルの幅",
|
||||
"gui.computercraft.config.term_sizes.computer.width.tooltip": "範囲: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.monitor": "モニター",
|
||||
"gui.computercraft.config.term_sizes.monitor.height": "モニターの最大高さ",
|
||||
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "範囲: 1 ~ 32",
|
||||
"gui.computercraft.config.term_sizes.monitor.tooltip": "モニターの最大サイズ(ブロック単位).",
|
||||
"gui.computercraft.config.term_sizes.monitor.width": "モニターの最大幅",
|
||||
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "範囲: 1 ~ 32",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer": "ポケットコンピューター",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.height": "ターミナルの高さ",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "範囲: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "ポケットコンピュータのターミナルサイズ。",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width": "ターミナルの幅",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "範囲: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.tooltip": "各種コンピュータのターミナルサイズを設定します。ターミナルサイズが大きくなるとより多くの帯域幅を必要としますので、注意して使用してください。",
|
||||
"gui.computercraft.config.turtle": "タートル",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit": "アドバンスドタートルの燃料制限",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "アドバンスドタートルの燃料制限。\n範囲: > 0",
|
||||
"gui.computercraft.config.turtle.can_push": "タートルのによるエンティティ押出し",
|
||||
"gui.computercraft.config.turtle.can_push.tooltip": "trueに設定すると、タートルは空間がある場合停止する代わりにエンティティを押し出す。",
|
||||
"gui.computercraft.config.turtle.need_fuel": "燃料を有効にする",
|
||||
"gui.computercraft.config.turtle.need_fuel.tooltip": "タートルズが移動に燃料を必要とするかどうかを設定する。",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit": "タートルの燃料制限",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "タートルの燃料制限。\n範囲: > 0",
|
||||
"gui.computercraft.config.turtle.tooltip": "タートルに関する様々なオプション。",
|
||||
"gui.computercraft.config.upload_max_size": "ファイルアップロードサイズ制限(バイト)",
|
||||
"gui.computercraft.config.upload_max_size.tooltip": "ファイルアップロードサイズの上限をバイト数で指定します。1KiBから16MiBの範囲でなければなりません。 \nアップロードは1ティックで処理されることを覚えておいてください - 大きなファイルやネットワークパフォーマンスの低下はネットワーキングスレッドを停止させる可能性があります。\nディスク容量にも注意してください!\n範囲: 1024 ~ 16777216",
|
||||
"gui.computercraft.config.upload_nag_delay": "アップロード催促遅延",
|
||||
"gui.computercraft.config.upload_nag_delay.tooltip": "未処理の入力について通知するまでの遅延時間(秒)。無効にするには0を設定する。\n範囲: 0 ~ 60",
|
||||
"gui.computercraft.pocket_computer_overlay": "ポケットコンピューターを開いています。 ESCを押して閉じます。",
|
||||
"gui.computercraft.terminal": "コンピューターターミナル",
|
||||
"gui.computercraft.tooltip.computer_id": "コンピューターID: %s",
|
||||
"gui.computercraft.tooltip.copy": "クリップボードにコピー",
|
||||
"gui.computercraft.tooltip.disk_id": "ディスクID: %s",
|
||||
"gui.computercraft.tooltip.terminate": "現在実行中のコードを停止する",
|
||||
"gui.computercraft.tooltip.terminate.key": "Ctrl+T 長押し",
|
||||
"gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする",
|
||||
"gui.computercraft.tooltip.turn_off": "このコンピューターをオフにする",
|
||||
"gui.computercraft.tooltip.turn_off.key": "Ctrl+S 長押し",
|
||||
"gui.computercraft.tooltip.turn_on": "このコンピュータをオンにする",
|
||||
"gui.computercraft.tooltip.turn_on": "このコンピューターをオンにする",
|
||||
"gui.computercraft.upload.failed": "アップロードに失敗しました",
|
||||
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。",
|
||||
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピューターを起動する必要があります。",
|
||||
"gui.computercraft.upload.failed.corrupted": "アップロード時にファイルが破損しました。 もう一度やり直してください。",
|
||||
"gui.computercraft.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)",
|
||||
"gui.computercraft.upload.failed.name_too_long": "ファイル名が長すぎてアップロードできません。",
|
||||
"gui.computercraft.upload.failed.too_many_files": "多くのファイルをアップロードできません。",
|
||||
"gui.computercraft.upload.failed.too_much": "アップロードするにはファイルが大きスギます。",
|
||||
"gui.computercraft.upload.no_response": "ファイルの転送",
|
||||
"gui.computercraft.upload.no_response.msg": "コンピュータが転送されたファイルを使用していません。プログラム %s を実行して再試行する必要があります。",
|
||||
"item.computercraft.disk": "フロッピーディスク",
|
||||
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_normal": "ポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピューター",
|
||||
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピューター",
|
||||
"item.computercraft.pocket_computer_normal": "ポケットコンピューター",
|
||||
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピューター",
|
||||
"item.computercraft.printed_book": "印刷された本",
|
||||
"item.computercraft.printed_page": "印刷された紙",
|
||||
"item.computercraft.printed_pages": "印刷された紙束",
|
||||
"item.computercraft.treasure_disk": "フロッピーディスク",
|
||||
"itemGroup.computercraft": "ComputerCraft",
|
||||
"tag.item.computercraft.computer": "コンピューター",
|
||||
"tag.item.computercraft.monitor": "モニター",
|
||||
"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.fs.name": "ファイルシステム演算",
|
||||
"tracking_field.computercraft.http_download.name": "HTTPダウンロード",
|
||||
"tracking_field.computercraft.http_requests.name": "HTTPリクエスト",
|
||||
"tracking_field.computercraft.http_upload.name": "HTTPアップロード",
|
||||
"tracking_field.computercraft.java_allocation.name": "Java割当",
|
||||
"tracking_field.computercraft.max": "%s (最大)",
|
||||
"tracking_field.computercraft.peripheral.name": "実行呼び出し",
|
||||
"tracking_field.computercraft.server_tasks.name": "サーバータスク",
|
||||
"tracking_field.computercraft.turtle_ops.name": "タートル操作",
|
||||
"tracking_field.computercraft.websocket_incoming.name": "Websocket 受信",
|
||||
"tracking_field.computercraft.websocket_outgoing.name": "Websocket 送信",
|
||||
"upgrade.computercraft.speaker.adjective": "騒音",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user