mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-15 14:07:38 +00:00
Compare commits
62 Commits
v1.21.4-1.
...
v1.21.1-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9cf0f85fcb | ||
![]() |
018ce7c8a5 | ||
![]() |
44726827b4 | ||
![]() |
2bf0aba455 | ||
![]() |
3cf914cb4c | ||
![]() |
180156ff1c | ||
![]() |
c6ba753568 | ||
![]() |
9f45c91925 | ||
![]() |
5fa724ed24 | ||
![]() |
fbf994e803 | ||
![]() |
8344c0a5c2 | ||
![]() |
a292d33830 | ||
![]() |
341d1c7bc2 | ||
![]() |
531eacfac7 | ||
![]() |
1f3da5205c | ||
![]() |
798ceefafe | ||
![]() |
7c0f79fc3c | ||
![]() |
b35cefc5dd | ||
![]() |
89dd521930 | ||
![]() |
9272e2efcd | ||
![]() |
69353a4fcf | ||
![]() |
ff363dca5a | ||
![]() |
1c51282426 | ||
![]() |
4a3a1c9275 | ||
![]() |
2557dd0af9 | ||
![]() |
b5c0c6e104 | ||
![]() |
876fd8ddb8 | ||
![]() |
ee3b1343b5 | ||
![]() |
b440b964b7 | ||
![]() |
5dfc401b45 | ||
![]() |
418c9be7ac | ||
![]() |
b491f6b11f | ||
![]() |
4344c3072f | ||
![]() |
947001104d | ||
![]() |
8711512769 | ||
![]() |
fdae94b3c1 | ||
![]() |
9c0ce27ce6 | ||
![]() |
c458360b18 | ||
![]() |
09ad6c1905 | ||
![]() |
0e1e8a72d3 | ||
![]() |
ffa6eadc26 | ||
![]() |
7c1e8e1951 | ||
![]() |
b805a34c2d | ||
![]() |
b03546a158 | ||
![]() |
582713467f | ||
![]() |
b6f41a0df5 | ||
![]() |
594738a022 | ||
![]() |
27f2ab364c | ||
![]() |
5a43273757 | ||
![]() |
97e28516fb | ||
![]() |
676fb5fb53 | ||
![]() |
08dc08b5a3 | ||
![]() |
8f4d4038f6 | ||
![]() |
63ba3fe274 | ||
![]() |
749b3df227 | ||
![]() |
b97634b717 | ||
![]() |
1b8344d0a3 | ||
![]() |
b42bc0a01a | ||
![]() |
70a7478529 | ||
![]() |
0cff73e2fc | ||
![]() |
a892739f8e | ||
![]() |
f8785a092f |
@@ -27,7 +27,7 @@ indent_size = 2
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[{*.kt,*.kts}]
|
||||
[*.{kt,kts}]
|
||||
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||
ij_kotlin_continuation_indent_size = 4
|
||||
ij_kotlin_spaces_around_equality_operators = true
|
||||
@@ -39,3 +39,14 @@ ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||
ij_kotlin_method_parameters_wrap = off
|
||||
ij_kotlin_call_parameters_wrap = off
|
||||
ij_kotlin_extends_list_wrap = off
|
||||
|
||||
ktlint_code_style = intellij_idea
|
||||
ktlint_standard_class-naming = disabled
|
||||
ktlint_standard_class-signature = disabled
|
||||
ktlint_standard_function-naming = disabled
|
||||
ktlint_standard_no-wildcard-imports = disabled
|
||||
|
||||
# FIXME: These two are disable right now as they're over-eager in putting things
|
||||
# on the same line. We should set max_line_length and handle this properly.
|
||||
ktlint_standard_function-signature = disabled
|
||||
ktlint_standard_function-expression-body = disabled
|
||||
|
3
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -11,7 +11,8 @@ body:
|
||||
What version of Minecraft are you using? If your version is not listed, please try to reproduce on one of the supported versions.
|
||||
options:
|
||||
- 1.20.1
|
||||
- 1.21.x
|
||||
- 1.21.1
|
||||
- 1.21.7
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
@@ -88,8 +88,8 @@ You'll first need to [set up a development environment as above](#setting-up-a-d
|
||||
|
||||
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
||||
writing the resulting HTML into `./projects/web/build/site`, which can then be opened in a browser. When iterating on
|
||||
documentation, you can instead run `./gradlew docWebsite -t`, which will rebuild documentation every time you change a
|
||||
file.
|
||||
documentation, you can instead run `./gradlew :web:assemble -x :web:compileTeaVM -t`, which will rebuild documentation
|
||||
every time you change a file.
|
||||
|
||||
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
||||
|
@@ -6,7 +6,6 @@
|
||||
|
||||
import cc.tweaked.gradle.CCTweakedExtension
|
||||
import cc.tweaked.gradle.CCTweakedPlugin
|
||||
import cc.tweaked.gradle.IdeaRunConfigurations
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
plugins {
|
||||
|
@@ -29,7 +29,7 @@ base.archivesName.convention("cc-tweaked-$mcVersion-${project.name}")
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion= CCTweakedPlugin.JAVA_VERSION
|
||||
languageVersion = CCTweakedPlugin.JAVA_VERSION
|
||||
}
|
||||
|
||||
withSourcesJar()
|
||||
@@ -93,6 +93,7 @@ sourceSets.all {
|
||||
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||
check("AssignmentExpression", CheckSeverity.OFF) // I'm a bad person.
|
||||
check("ReferenceEquality", CheckSeverity.OFF)
|
||||
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||
@@ -121,7 +122,6 @@ tasks.compileTestJava {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tasks.withType(JavaCompile::class.java).configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
@@ -170,7 +170,7 @@ tasks.test {
|
||||
|
||||
tasks.withType(JacocoReport::class.java).configureEach {
|
||||
reports.xml.required = true
|
||||
reports.html.required =true
|
||||
reports.html.required = true
|
||||
}
|
||||
|
||||
project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||
@@ -194,30 +194,23 @@ spotless {
|
||||
fun FormatExtension.defaults() {
|
||||
endWithNewline()
|
||||
trimTrailingWhitespace()
|
||||
indentWithSpaces(4)
|
||||
leadingTabsToSpaces(4)
|
||||
}
|
||||
|
||||
java {
|
||||
defaults()
|
||||
importOrder("", "javax|java", "\\#")
|
||||
removeUnusedImports()
|
||||
}
|
||||
|
||||
val ktlintConfig = mapOf(
|
||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
||||
"ktlint_standard_class-naming" to "disabled",
|
||||
"ktlint_standard_function-naming" to "disabled",
|
||||
"ij_kotlin_allow_trailing_comma" to "true",
|
||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
||||
)
|
||||
|
||||
kotlinGradle {
|
||||
defaults()
|
||||
ktlint().editorConfigOverride(ktlintConfig)
|
||||
ktlint()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
defaults()
|
||||
ktlint().editorConfigOverride(ktlintConfig)
|
||||
ktlint()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,10 +5,8 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.AbstractExecTask
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import java.io.File
|
||||
|
||||
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||
@get:OutputDirectory
|
||||
|
@@ -25,7 +25,6 @@ import javax.xml.xpath.XPathFactory
|
||||
* Would be good to PR some (or all) of these changes upstream at some point.
|
||||
*
|
||||
* @see net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
||||
* @see net.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
|
||||
*/
|
||||
internal class IdeaRunConfigurations(project: Project) {
|
||||
private val rootProject = project.rootProject
|
||||
@@ -35,22 +34,6 @@ internal class IdeaRunConfigurations(project: Project) {
|
||||
private val writer = TransformerFactory.newInstance().newTransformer()
|
||||
|
||||
private val ideaDir = rootProject.file(".idea/")
|
||||
private val buildDir: Lazy<String?> = lazy {
|
||||
val ideaMisc = ideaDir.resolve("misc.xml")
|
||||
|
||||
try {
|
||||
val doc = Files.newBufferedReader(ideaMisc.toPath()).use {
|
||||
documentBuilder.parse(InputSource(it))
|
||||
}
|
||||
val node =
|
||||
xpath.evaluate("//component[@name=\"ProjectRootManager\"]/output", doc, XPathConstants.NODE) as Node
|
||||
val attr = node.attributes.getNamedItem("url") as Attr
|
||||
attr.value.removePrefix("file://")
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to find root directory", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun patch() = synchronized(LOCK) {
|
||||
val runConfigDir = ideaDir.resolve("runConfigurations")
|
||||
@@ -58,10 +41,9 @@ internal class IdeaRunConfigurations(project: Project) {
|
||||
|
||||
Files.list(runConfigDir.toPath()).use {
|
||||
for (configuration in it) {
|
||||
val filename = configuration.fileName.toString();
|
||||
val filename = configuration.fileName.toString()
|
||||
when {
|
||||
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
||||
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -72,65 +54,6 @@ internal class IdeaRunConfigurations(project: Project) {
|
||||
setXml("//configuration", "folderName") { "Fabric" }
|
||||
}
|
||||
|
||||
private fun patchForge(path: Path) = withXml(path) {
|
||||
val configId = path.fileName.toString().removePrefix("forge_").removeSuffix(".xml")
|
||||
val sourceSet = forgeConfigs[configId]
|
||||
if (sourceSet == null) {
|
||||
LOGGER.error("[{}] Cannot map run configuration to a known source set", path)
|
||||
return@withXml
|
||||
}
|
||||
|
||||
setXml("//configuration", "folderName") { "Forge" }
|
||||
setXml("//configuration/module", "name") { "${rootProject.name}.forge.$sourceSet" }
|
||||
|
||||
if (buildDir.value == null) return@withXml
|
||||
setXml("//configuration/envs/env[@name=\"MOD_CLASSES\"]", "value") { classpath ->
|
||||
val classes = classpath!!.split(':')
|
||||
val newClasses = mutableListOf<String>()
|
||||
fun appendUnique(x: String) {
|
||||
if (!newClasses.contains(x)) newClasses.add(x)
|
||||
}
|
||||
|
||||
for (entry in classes) {
|
||||
if (!entry.contains("/out/")) {
|
||||
appendUnique(entry)
|
||||
continue
|
||||
}
|
||||
|
||||
val match = CLASSPATH_ENTRY.matchEntire(entry)
|
||||
if (match != null) {
|
||||
val modId = match.groups["modId"]!!.value
|
||||
val proj = match.groups["proj"]!!.value
|
||||
var component = match.groups["component"]!!.value
|
||||
if (component == "production") component = "main"
|
||||
|
||||
appendUnique(forgeModEntry(modId, proj, component))
|
||||
} else {
|
||||
LOGGER.warn("[{}] Unknown classpath entry {}", path, entry)
|
||||
appendUnique(entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure common code is on the classpath
|
||||
for (proj in listOf("common", "common-api")) {
|
||||
for (component in listOf("main", "client")) {
|
||||
appendUnique(forgeModEntry("computercraft", proj, component))
|
||||
}
|
||||
}
|
||||
|
||||
if (newClasses.any { it.startsWith("cctest%%") }) {
|
||||
appendUnique(forgeModEntry("cctest", "core", "testFixtures"))
|
||||
appendUnique(forgeModEntry("cctest", "common", "testFixtures"))
|
||||
appendUnique(forgeModEntry("cctest", "common", "testMod"))
|
||||
}
|
||||
|
||||
newClasses.joinToString(":")
|
||||
}
|
||||
}
|
||||
|
||||
private fun forgeModEntry(mod: String, project: String, component: String) =
|
||||
"$mod%%${buildDir.value}/production/${rootProject.name}.$project.$component"
|
||||
|
||||
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
|
||||
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
||||
if (node == null) {
|
||||
@@ -159,16 +82,5 @@ internal class IdeaRunConfigurations(project: Project) {
|
||||
companion object {
|
||||
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
||||
private val LOCK = Any()
|
||||
|
||||
private val CLASSPATH_ENTRY =
|
||||
Regex("(?<modId>[a-z]+)%%\\\$PROJECT_DIR\\\$/projects/(?<proj>[a-z-]+)/out/(?<component>\\w+)/(?<type>[a-z]+)\$")
|
||||
|
||||
private val forgeConfigs = mapOf(
|
||||
"runClient" to "client",
|
||||
"runData" to "main",
|
||||
"runGameTestServer" to "testMod",
|
||||
"runServer" to "main",
|
||||
"runTestClient" to "testMod",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import groovy.util.Node
|
||||
import groovy.util.NodeList
|
||||
|
||||
object XmlUtil {
|
||||
fun findChild(node: Node, name: String): Node? = when (val child = node.get(name)) {
|
||||
is Node -> child
|
||||
is NodeList -> child.singleOrNull() as Node?
|
||||
else -> null
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ files:
|
||||
de: de_de # German
|
||||
es-ES: es_es # Spanish
|
||||
fr: fr_fr # French
|
||||
hu: hu_hu # Hungarian
|
||||
it: it_it # Italian
|
||||
ja: ja_jp # Japanese
|
||||
ko: ko_kr # Korean
|
||||
|
@@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
|
||||
|
||||
# Mod properties
|
||||
isUnstable=true
|
||||
modVersion=1.115.1
|
||||
modVersion=1.116.1
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.21.1
|
||||
|
@@ -10,7 +10,7 @@
|
||||
fabric-api = "0.102.1+1.21.1"
|
||||
fabric-loader = "0.15.11"
|
||||
neoForge = "21.1.9"
|
||||
neoForgeSpi = "8.0.1"
|
||||
neoMergeTool = "2.0.0"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2024.07.28"
|
||||
parchmentMc = "1.21"
|
||||
@@ -26,7 +26,7 @@ slf4j = "2.0.9"
|
||||
asm = "9.6"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = { strictly = "0.9.5" }
|
||||
cobalt = { strictly = "0.9.6" }
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jspecify = "1.0.0"
|
||||
@@ -58,24 +58,24 @@ junitPlatform = "1.11.4"
|
||||
jmh = "1.37"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.3"
|
||||
checkstyle = "10.21.2"
|
||||
errorProne-core = "2.36.0"
|
||||
cctJavadoc = "1.8.4"
|
||||
checkstyle = "10.23.1"
|
||||
errorProne-core = "2.38.0"
|
||||
errorProne-plugin = "4.1.0"
|
||||
fabric-loom = "1.9.2"
|
||||
fabric-loom = "1.10.4"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.50.0"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-74-gf1551d5"
|
||||
illuaminate = "0.1.0-83-g1131f68"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.8.7"
|
||||
modDevGradle = "2.0.74"
|
||||
nullAway = "0.12.3"
|
||||
modDevGradle = "2.0.95"
|
||||
nullAway = "0.12.7"
|
||||
shadow = "8.3.1"
|
||||
spotless = "6.23.3"
|
||||
spotless = "7.0.2"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.11.0-SQUID.1"
|
||||
vanillaExtract = "0.2.0"
|
||||
teavm = "0.13.0-SQUID.1"
|
||||
vanillaExtract = "0.2.1"
|
||||
versionCatalogUpdate = "0.8.1"
|
||||
|
||||
[libraries]
|
||||
@@ -87,7 +87,7 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
|
||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||
neoForgeSpi = { module = "net.neoforged:neoforgespi", version.ref = "neoForgeSpi" }
|
||||
neoMergeTool = { module = "net.neoforged:mergetool", version.ref = "neoMergeTool" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||
jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" }
|
||||
|
960
package-lock.json
generated
960
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,11 +12,11 @@
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||
"@rollup/plugin-typescript": "^12.0.0",
|
||||
"@rollup/plugin-url": "^8.0.1",
|
||||
"@swc/core": "^1.3.92",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/node": "^24.0.0",
|
||||
"lightningcss": "^1.22.0",
|
||||
"preact-render-to-string": "^6.2.1",
|
||||
"rehype": "^13.0.0",
|
||||
|
@@ -28,6 +28,20 @@ public class ComputerCraftTags {
|
||||
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
||||
public static final TagKey<Item> MONITOR = make("monitor");
|
||||
|
||||
/**
|
||||
* Floppy disks. Both the read/write version, and treasure disks.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*/
|
||||
public static final TagKey<Item> DISKS = make("disks");
|
||||
|
||||
/**
|
||||
* All pocket computers.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*/
|
||||
public static final TagKey<Item> POCKET_COMPUTERS = make("pocket_computers");
|
||||
|
||||
/**
|
||||
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
||||
* {@code turtle.place()}.
|
||||
|
@@ -51,10 +51,10 @@ public abstract class ComponentDetailProvider<T> implements DetailProvider<DataC
|
||||
* This method is always called on the server thread, so it is safe to interact with the world here, but you should
|
||||
* take care to avoid long blocking operations as this will stall the server and other computers.
|
||||
*
|
||||
* @param data The full details to be returned for this item stack. New properties should be added to this map.
|
||||
* @param item The component to provide details for.
|
||||
* @param data The full details to be returned for this item stack. New properties should be added to this map.
|
||||
* @param component The component to provide details for.
|
||||
*/
|
||||
public abstract void provideComponentDetails(Map<? super String, Object> data, T item);
|
||||
public abstract void provideComponentDetails(Map<? super String, Object> data, T component);
|
||||
|
||||
@Override
|
||||
public final void provideDetails(Map<? super String, Object> data, DataComponentHolder holder) {
|
||||
|
@@ -25,7 +25,6 @@ import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -41,7 +40,6 @@ import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||
import net.minecraft.util.FastColor;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
@@ -52,6 +50,8 @@ import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -69,6 +69,8 @@ import java.util.function.Supplier;
|
||||
* @see ModRegistry The common registry for actual game objects.
|
||||
*/
|
||||
public final class ClientRegistry {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClientRegistry.class);
|
||||
|
||||
private ClientRegistry() {
|
||||
}
|
||||
|
||||
@@ -143,10 +145,6 @@ public final class ClientRegistry {
|
||||
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
||||
}
|
||||
|
||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
||||
register.accept(GuiSprites.initialise(minecraft.getTextureManager()));
|
||||
}
|
||||
|
||||
private static final ResourceLocation[] EXTRA_MODELS = {
|
||||
TurtleOverlay.ELF_MODEL,
|
||||
TurtleBlockEntityRenderer.COLOUR_TURTLE_MODEL,
|
||||
@@ -160,7 +158,7 @@ public final class ClientRegistry {
|
||||
|
||||
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
||||
register.accept(
|
||||
(stack, layer) -> layer == 1 ? DiskItem.getColour(stack) : -1,
|
||||
(stack, layer) -> layer == 1 ? DyedItemColor.getOrDefault(stack, Colour.WHITE.getARGB()) : -1,
|
||||
ModRegistry.Items.DISK.get()
|
||||
);
|
||||
|
||||
@@ -192,7 +190,18 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
||||
RenderTypes.registerShaders(resources, load);
|
||||
RenderTypes.registerShaders(resources, (name, create, onLoaded) -> {
|
||||
ShaderInstance shader;
|
||||
try {
|
||||
shader = create.get();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to load {}", name, e);
|
||||
onLoaded.accept(null);
|
||||
return;
|
||||
}
|
||||
|
||||
load.accept(shader, onLoaded);
|
||||
});
|
||||
}
|
||||
|
||||
private record UnclampedPropertyFunction(
|
||||
|
@@ -6,9 +6,7 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -40,14 +38,15 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
|
||||
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
// Draw a border around the terminal
|
||||
var terminal = getTerminal();
|
||||
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
||||
var computerTextures = GuiSprites.getComputerTextures(family);
|
||||
|
||||
ComputerBorderRenderer.render(
|
||||
spriteRenderer, computerTextures,
|
||||
terminal.getX(), terminal.getY(), terminal.getWidth(), terminal.getHeight(), false
|
||||
graphics.blitSprite(
|
||||
computerTextures.border(),
|
||||
terminal.getX() - BORDER, terminal.getY() - BORDER, terminal.getWidth() + BORDER * 2, terminal.getHeight() + BORDER * 2
|
||||
);
|
||||
graphics.blitSprite(
|
||||
Nullability.assertNonNull(computerTextures.sidebar()),
|
||||
leftPos, topPos + sidebarYOffset, AbstractComputerMenu.SIDEBAR_WIDTH, ComputerSidebar.HEIGHT
|
||||
);
|
||||
ComputerSidebar.renderBackground(spriteRenderer, computerTextures, leftPos, topPos + sidebarYOffset);
|
||||
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
||||
}
|
||||
}
|
||||
|
@@ -7,9 +7,6 @@ package dan200.computercraft.client.gui;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.resources.TextureAtlasHolder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -19,10 +16,7 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* Sprite sheet for all GUI texutres in the mod.
|
||||
*/
|
||||
public final class GuiSprites extends TextureAtlasHolder {
|
||||
public static final ResourceLocation SPRITE_SHEET = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "gui");
|
||||
public static final ResourceLocation TEXTURE = SPRITE_SHEET.withPath(x -> "textures/atlas/" + x + ".png");
|
||||
|
||||
public final class GuiSprites {
|
||||
public static final ButtonTextures TURNED_OFF = button("turned_off");
|
||||
public static final ButtonTextures TURNED_ON = button("turned_on");
|
||||
public static final ButtonTextures TERMINATE = button("terminate");
|
||||
@@ -32,6 +26,9 @@ public final class GuiSprites extends TextureAtlasHolder {
|
||||
public static final ComputerTextures COMPUTER_COMMAND = computer("command", false, true);
|
||||
public static final ComputerTextures COMPUTER_COLOUR = computer("colour", true, false);
|
||||
|
||||
private GuiSprites() {
|
||||
}
|
||||
|
||||
private static ButtonTextures button(String name) {
|
||||
return new ButtonTextures(
|
||||
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "buttons/" + name),
|
||||
@@ -47,34 +44,6 @@ public final class GuiSprites extends TextureAtlasHolder {
|
||||
);
|
||||
}
|
||||
|
||||
private static @Nullable GuiSprites instance;
|
||||
|
||||
private GuiSprites(TextureManager textureManager) {
|
||||
super(textureManager, TEXTURE, SPRITE_SHEET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the singleton {@link GuiSprites} instance.
|
||||
*
|
||||
* @param textureManager The current texture manager.
|
||||
* @return The singleton {@link GuiSprites} instance, to register as resource reload listener.
|
||||
*/
|
||||
public static GuiSprites initialise(TextureManager textureManager) {
|
||||
if (instance != null) throw new IllegalStateException("GuiSprites has already been initialised");
|
||||
return instance = new GuiSprites(textureManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a texture on the atlas.
|
||||
*
|
||||
* @param texture The texture to find.
|
||||
* @return The sprite on the atlas.
|
||||
*/
|
||||
public static TextureAtlasSprite get(ResourceLocation texture) {
|
||||
if (instance == null) throw new IllegalStateException("GuiSprites has not been initialised");
|
||||
return instance.getSprite(texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate textures to use for a particular computer family.
|
||||
*
|
||||
|
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/**
|
||||
* Supports for converting/translating key codes.
|
||||
*/
|
||||
public class KeyConverter {
|
||||
/**
|
||||
* GLFW's key events refer to the physical key code, rather than the "actual" key code (with keyboard layout
|
||||
* applied).
|
||||
* <p>
|
||||
* This makes sense for WASD-style input, but is a right pain for keyboard shortcuts — this function attempts to
|
||||
* translate those keys back to their "actual" key code. See also
|
||||
* <a href="https://github.com/glfw/glfw/issues/1502"> this discussion on GLFW's GitHub.</a>
|
||||
*
|
||||
* @param key The current key code.
|
||||
* @param scanCode The current scan code.
|
||||
* @return The translated key code.
|
||||
*/
|
||||
public static int physicalToActual(int key, int scanCode) {
|
||||
var name = GLFW.glfwGetKeyName(key, scanCode);
|
||||
if (name == null || name.length() != 1) return key;
|
||||
|
||||
// If we've got a single character as the key name, treat that as the ASCII value of the key,
|
||||
// and map that back to a key code.
|
||||
var character = name.charAt(0);
|
||||
|
||||
// 0-9 and A-Z map directly to their GLFW key (they're the same ASCII code).
|
||||
if ((character >= '0' && character <= '9') || (character >= 'A' && character <= 'Z')) return character;
|
||||
// a-z map to GLFW_KEY_{A,Z}
|
||||
if (character >= 'a' && character <= 'z') return GLFW.GLFW_KEY_A + (character - 'a');
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
@@ -7,8 +7,7 @@ package dan200.computercraft.client.gui;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||
@@ -64,8 +63,9 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
||||
}
|
||||
|
||||
// Render sidebar
|
||||
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
||||
ComputerSidebar.renderBackground(spriteRenderer, GuiSprites.getComputerTextures(family), leftPos, topPos + sidebarYOffset);
|
||||
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
||||
graphics.blitSprite(
|
||||
Nullability.assertNonNull(GuiSprites.getComputerTextures(family).sidebar()),
|
||||
leftPos, topPos + sidebarYOffset, AbstractComputerMenu.SIDEBAR_WIDTH, ComputerSidebar.HEIGHT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,9 +6,7 @@ package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
||||
import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
@@ -24,12 +22,9 @@ public final class ComputerSidebar {
|
||||
private static final int ICON_MARGIN = 2;
|
||||
|
||||
private static final int CORNERS_BORDER = 3;
|
||||
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
||||
|
||||
private static final int BUTTONS = 2;
|
||||
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
||||
|
||||
private static final int TEX_HEIGHT = 14;
|
||||
public static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
||||
|
||||
private ComputerSidebar() {
|
||||
}
|
||||
@@ -63,14 +58,6 @@ public final class ComputerSidebar {
|
||||
));
|
||||
}
|
||||
|
||||
public static void renderBackground(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y) {
|
||||
var texture = textures.sidebar();
|
||||
if (texture == null) throw new NullPointerException(textures + " has no sidebar texture");
|
||||
var sprite = GuiSprites.get(texture);
|
||||
|
||||
renderer.blitVerticalSliced(sprite, x, y, AbstractComputerMenu.SIDEBAR_WIDTH, HEIGHT, FULL_BORDER, FULL_BORDER, TEX_HEIGHT);
|
||||
}
|
||||
|
||||
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
||||
if (isOn.getAsBoolean()) {
|
||||
input.shutdown();
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import dan200.computercraft.client.gui.KeyConverter;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
@@ -83,7 +84,7 @@ public class TerminalWidget extends AbstractWidget {
|
||||
}
|
||||
|
||||
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
|
||||
switch (key) {
|
||||
switch (KeyConverter.physicalToActual(key, scancode)) {
|
||||
case GLFW.GLFW_KEY_T -> {
|
||||
if (terminateTimer < 0) terminateTimer = 0;
|
||||
}
|
||||
@@ -119,7 +120,7 @@ public class TerminalWidget extends AbstractWidget {
|
||||
computer.keyUp(key);
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
switch (KeyConverter.physicalToActual(key, scancode)) {
|
||||
case GLFW.GLFW_KEY_T -> terminateTimer = -1;
|
||||
case GLFW.GLFW_KEY_R -> rebootTimer = -1;
|
||||
case GLFW.GLFW_KEY_S -> shutdownTimer = -1;
|
||||
|
@@ -8,7 +8,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.integration.RecipeModHelpers;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import mezz.jei.api.IModPlugin;
|
||||
@@ -23,6 +22,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -100,7 +100,7 @@ public class JEIComputerCraft implements IModPlugin {
|
||||
/**
|
||||
* Distinguishes disks by colour.
|
||||
*/
|
||||
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DiskItem.getColour(stack));
|
||||
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DyedItemColor.getOrDefault(stack, -1));
|
||||
|
||||
private static RegistryAccess getRegistryAccess() {
|
||||
return Minecraft.getInstance().level.registryAccess();
|
||||
|
@@ -1,17 +1,14 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
|
||||
import static dan200.computercraft.client.render.SpriteRenderer.u;
|
||||
import static dan200.computercraft.client.render.SpriteRenderer.v;
|
||||
import dan200.computercraft.client.gui.ComputerScreen;
|
||||
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
|
||||
|
||||
/**
|
||||
* Renders the borders of computers, either for a GUI ({@link dan200.computercraft.client.gui.ComputerScreen}) or
|
||||
* Constants for the borders of computers, either for a {@linkplain ComputerScreen GUI} or
|
||||
* {@linkplain PocketItemRenderer in-hand pocket computers}.
|
||||
*/
|
||||
public final class ComputerBorderRenderer {
|
||||
@@ -21,55 +18,13 @@ public final class ComputerBorderRenderer {
|
||||
public static final int MARGIN = 2;
|
||||
|
||||
/**
|
||||
* The width of the terminal border.
|
||||
* The size of the terminal border.
|
||||
* <p>
|
||||
* This is only used for layout of elements within UI. When rendering, the size of the computer's border is
|
||||
* determined by its {@link GuiSpriteScaling}.
|
||||
*/
|
||||
public static final int BORDER = 12;
|
||||
|
||||
public static final int LIGHT_HEIGHT = 8;
|
||||
|
||||
private static final int TEX_SIZE = 36;
|
||||
|
||||
private ComputerBorderRenderer() {
|
||||
}
|
||||
|
||||
public static void render(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y, int width, int height, boolean withLight) {
|
||||
var endX = x + width;
|
||||
var endY = y + height;
|
||||
|
||||
var border = GuiSprites.get(textures.border());
|
||||
|
||||
// Top bar
|
||||
blitBorder(renderer, border, x - BORDER, y - BORDER, 0, 0, BORDER, BORDER);
|
||||
blitBorder(renderer, border, x, y - BORDER, BORDER, 0, width, BORDER);
|
||||
blitBorder(renderer, border, endX, y - BORDER, BORDER * 2, 0, BORDER, BORDER);
|
||||
|
||||
// Vertical bars
|
||||
blitBorder(renderer, border, x - BORDER, y, 0, BORDER, BORDER, height);
|
||||
blitBorder(renderer, border, endX, y, BORDER * 2, BORDER, BORDER, height);
|
||||
|
||||
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
|
||||
// pocket computer's lights).
|
||||
if (withLight) {
|
||||
var pocketBottomTexture = textures.pocketBottom();
|
||||
if (pocketBottomTexture == null) throw new NullPointerException(textures + " has no pocket texture");
|
||||
var pocketBottom = GuiSprites.get(pocketBottomTexture);
|
||||
|
||||
renderer.blitHorizontalSliced(
|
||||
pocketBottom, x - BORDER, endY, width + BORDER * 2, BORDER + LIGHT_HEIGHT,
|
||||
BORDER, BORDER, BORDER * 3
|
||||
);
|
||||
} else {
|
||||
blitBorder(renderer, border, x - BORDER, endY, 0, BORDER * 2, BORDER, BORDER);
|
||||
blitBorder(renderer, border, x, endY, BORDER, BORDER * 2, width, BORDER);
|
||||
blitBorder(renderer, border, endX, endY, BORDER * 2, BORDER * 2, BORDER, BORDER);
|
||||
}
|
||||
}
|
||||
|
||||
private static void blitBorder(SpriteRenderer renderer, TextureAtlasSprite sprite, int x, int y, int u, int v, int width, int height) {
|
||||
renderer.blit(
|
||||
x, y, width, height,
|
||||
u(sprite, u, TEX_SIZE), v(sprite, v, TEX_SIZE),
|
||||
u(sprite, u + BORDER, TEX_SIZE), v(sprite, v + BORDER, TEX_SIZE)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
@@ -59,9 +60,9 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
||||
poseStack.translate(0, -0.125f, 0);
|
||||
|
||||
var item = lectern.getItem();
|
||||
if (item.getItem() instanceof PrintoutItem printout) {
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
|
||||
if (printout.getType() == PrintoutItem.Type.BOOK) {
|
||||
if (item.is(ModRegistry.Items.PRINTED_BOOK.get())) {
|
||||
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
|
||||
} else {
|
||||
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutData.getOrEmpty(item).pages());
|
||||
|
@@ -13,13 +13,16 @@ 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 net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
|
||||
import net.minecraft.util.FastColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
|
||||
@@ -29,6 +32,11 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON
|
||||
public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
public static final PocketItemRenderer INSTANCE = new PocketItemRenderer();
|
||||
|
||||
/**
|
||||
* The height of the pocket computer's light.
|
||||
*/
|
||||
private static final int LIGHT_HEIGHT = 8;
|
||||
|
||||
private PocketItemRenderer() {
|
||||
}
|
||||
|
||||
@@ -83,14 +91,69 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
}
|
||||
|
||||
private static void renderFrame(Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height) {
|
||||
var texture = colour != -1 ? GuiSprites.COMPUTER_COLOUR : GuiSprites.getComputerTextures(family);
|
||||
var textures = colour != -1 ? GuiSprites.COMPUTER_COLOUR : GuiSprites.getComputerTextures(family);
|
||||
var spriteRenderer = new SpriteRenderer(transform, render, 0, light, colour);
|
||||
renderBorder(spriteRenderer, textures, width, height);
|
||||
}
|
||||
|
||||
var r = (colour >>> 16) & 0xFF;
|
||||
var g = (colour >>> 8) & 0xFF;
|
||||
var b = colour & 0xFF;
|
||||
private static void renderBorder(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int width, int height) {
|
||||
var sprites = Minecraft.getInstance().getGuiSprites();
|
||||
|
||||
var spriteRenderer = new SpriteRenderer(transform, render.getBuffer(RenderTypes.GUI_SPRITES), 0, light, r, g, b);
|
||||
ComputerBorderRenderer.render(spriteRenderer, texture, 0, 0, width, height, true);
|
||||
// Find our border, forcing it to be a nine-sliced texture.
|
||||
var borderSprite = sprites.getSprite(textures.border());
|
||||
var borderSlice = getSlice(sprites.getSpriteScaling(borderSprite), DEFAULT_BORDER);
|
||||
var borderBounds = borderSlice.border();
|
||||
|
||||
// And take the separate bottom bit of the pocket computer.
|
||||
var bottomTexture = textures.pocketBottom();
|
||||
if (bottomTexture == null) throw new NullPointerException(textures + " has no pocket texture");
|
||||
var bottomSprite = sprites.getSprite(bottomTexture);
|
||||
var bottomSlice = getSlice(sprites.getSpriteScaling(bottomSprite), DEFAULT_BOTTOM);
|
||||
var bottomBounds = bottomSlice.border();
|
||||
|
||||
// Now draw a nine-sliced texture, by stitching together the top parts of the border with the pocket bottom.
|
||||
|
||||
// Top bar
|
||||
renderer.blit(
|
||||
borderSprite, -borderBounds.left(), -borderBounds.top(), borderBounds.left(), borderBounds.top(),
|
||||
0, 0, borderSlice.width(), borderSlice.height()
|
||||
);
|
||||
renderer.blitTiled(
|
||||
borderSprite, 0, -borderBounds.top(), width, borderBounds.top(),
|
||||
borderBounds.left(), 0, borderSlice.width() - borderBounds.left() - borderBounds.right(), borderBounds.top(),
|
||||
borderSlice.width(), borderSlice.height()
|
||||
);
|
||||
renderer.blit(
|
||||
borderSprite, width, -borderBounds.top(), borderBounds.right(), borderBounds.top(),
|
||||
borderSlice.width() - borderBounds.right(), 0, borderSlice.width(), borderSlice.height()
|
||||
);
|
||||
|
||||
// Vertical bars
|
||||
renderer.blitTiled(
|
||||
borderSprite, -borderBounds.left(), 0, borderBounds.left(), height,
|
||||
0, borderBounds.top(), borderBounds.left(), borderSlice.height() - borderBounds.top() - borderBounds.bottom(),
|
||||
borderSlice.width(), borderSlice.height()
|
||||
);
|
||||
renderer.blitTiled(
|
||||
borderSprite, width, 0, borderBounds.right(), height,
|
||||
borderSlice.width() - borderBounds.right(), borderBounds.top(), borderBounds.right(), borderSlice.height() - borderBounds.top() - borderBounds.bottom(),
|
||||
borderSlice.width(), borderSlice.height()
|
||||
);
|
||||
|
||||
// Bottom
|
||||
renderer.blit(
|
||||
bottomSprite, -bottomBounds.left(), height, bottomBounds.left(), bottomSlice.height(),
|
||||
0, 0, bottomSlice.width(), bottomSlice.height()
|
||||
);
|
||||
renderer.blitTiled(
|
||||
bottomSprite, 0, height, width, bottomSlice.height(),
|
||||
bottomBounds.left(), 0, bottomSlice.width() - bottomBounds.left() - bottomBounds.right(), bottomSlice.height(),
|
||||
bottomSlice.width(), bottomSlice.height()
|
||||
);
|
||||
renderer.blit(
|
||||
bottomSprite, width, height, bottomBounds.right(), bottomSlice.height(),
|
||||
bottomSlice.width() - bottomBounds.right(), 0, bottomSlice.width(), bottomSlice.height()
|
||||
);
|
||||
}
|
||||
|
||||
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
||||
@@ -101,4 +164,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
FastColor.ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
|
||||
);
|
||||
}
|
||||
|
||||
private static final GuiSpriteScaling.NineSlice DEFAULT_BORDER = new GuiSpriteScaling.NineSlice(
|
||||
36, 36, new GuiSpriteScaling.NineSlice.Border(12, 12, 12, 12)
|
||||
);
|
||||
|
||||
private static final GuiSpriteScaling.NineSlice DEFAULT_BOTTOM = new GuiSpriteScaling.NineSlice(
|
||||
36, 20, new GuiSpriteScaling.NineSlice.Border(12, 0, 12, 0)
|
||||
);
|
||||
|
||||
private static GuiSpriteScaling.NineSlice getSlice(GuiSpriteScaling scaling, GuiSpriteScaling.NineSlice fallback) {
|
||||
return scaling instanceof GuiSpriteScaling.NineSlice slice ? slice : fallback;
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||
@@ -53,7 +52,7 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
|
||||
var pageData = stack.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
||||
|
||||
var pages = pageData.pages();
|
||||
var book = ((PrintoutItem) stack.getItem()).getType() == PrintoutItem.Type.BOOK;
|
||||
var book = stack.is(ModRegistry.Items.PRINTED_BOOK.get());
|
||||
|
||||
double width = LINE_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
||||
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.client.render;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.render.monitor.MonitorTextureBufferShader;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
@@ -16,11 +15,11 @@ import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||
import org.apache.commons.io.function.IOSupplier;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -54,11 +53,6 @@ public class RenderTypes {
|
||||
*/
|
||||
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text(ResourceLocation.fromNamespaceAndPath("computercraft", "textures/gui/printout.png"));
|
||||
|
||||
/**
|
||||
* Render type for {@linkplain GuiSprites GUI sprites}.
|
||||
*/
|
||||
public static final RenderType GUI_SPRITES = RenderType.text(GuiSprites.TEXTURE);
|
||||
|
||||
public static MonitorTextureBufferShader getMonitorTextureBufferShader() {
|
||||
if (monitorTboShader == null) throw new NullPointerException("MonitorTboShader has not been registered");
|
||||
return monitorTboShader;
|
||||
@@ -68,9 +62,12 @@ public class RenderTypes {
|
||||
return Objects.requireNonNull(GameRenderer.getRendertypeTextShader(), "Text shader has not been registered");
|
||||
}
|
||||
|
||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
||||
load.accept(
|
||||
new MonitorTextureBufferShader(
|
||||
public interface ShaderLoader {
|
||||
void tryLoad(String name, IOSupplier<ShaderInstance> create, Consumer<@Nullable ShaderInstance> accept) throws IOException;
|
||||
}
|
||||
|
||||
public static void registerShaders(ResourceProvider resources, ShaderLoader load) throws IOException {
|
||||
load.tryLoad("monitor shader", () -> new MonitorTextureBufferShader(
|
||||
resources,
|
||||
ComputerCraftAPI.MOD_ID + "/monitor_tbo",
|
||||
MONITOR_TBO.format()
|
||||
|
@@ -6,129 +6,69 @@ package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
/**
|
||||
* A {@link GuiGraphics}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
|
||||
* renderer).
|
||||
* A {@link GuiGraphics}-equivalent renders to a {@link VertexConsumer}. This is suitable for rendering outside of a
|
||||
* GUI, such as part of an entity renderer.
|
||||
* <p>
|
||||
* This batches all render calls together, though requires that all {@link TextureAtlasSprite}s are on the same sprite
|
||||
* sheet.
|
||||
*/
|
||||
public class SpriteRenderer {
|
||||
public static final ResourceLocation TEXTURE = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png");
|
||||
|
||||
private final Matrix4f transform;
|
||||
private final VertexConsumer builder;
|
||||
private final MultiBufferSource buffers;
|
||||
private final int light;
|
||||
private final int z;
|
||||
private final int r, g, b;
|
||||
private final int colour;
|
||||
|
||||
public SpriteRenderer(Matrix4f transform, VertexConsumer builder, int z, int light, int r, int g, int b) {
|
||||
public SpriteRenderer(Matrix4f transform, MultiBufferSource buffers, int z, int light, int colour) {
|
||||
this.transform = transform;
|
||||
this.builder = builder;
|
||||
this.buffers = buffers;
|
||||
this.z = z;
|
||||
this.light = light;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
public void blit(TextureAtlasSprite sprite, int x0, int y0, int width, int height, int spriteX, int spriteY, int spriteWidth, int spriteHeight) {
|
||||
if (width == 0 || height == 0) return;
|
||||
|
||||
var x1 = x0 + width;
|
||||
var y1 = y0 + height;
|
||||
var u0 = sprite.getU((float) spriteX / spriteWidth);
|
||||
var u1 = sprite.getU((float) (spriteX + width) / spriteWidth);
|
||||
var v0 = sprite.getV((float) spriteY / spriteHeight);
|
||||
var v1 = sprite.getV((float) (spriteY + height) / spriteHeight);
|
||||
|
||||
var vertices = buffers.getBuffer(RenderType.text(sprite.atlasLocation()));
|
||||
vertices.addVertex(transform, x0, y1, z).setColor(colour).setUv(u0, v1).setLight(light);
|
||||
vertices.addVertex(transform, x1, y1, z).setColor(colour).setUv(u1, v1).setLight(light);
|
||||
vertices.addVertex(transform, x1, y0, z).setColor(colour).setUv(u1, v0).setLight(light);
|
||||
vertices.addVertex(transform, x0, y0, z).setColor(colour).setUv(u0, v0).setLight(light);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a single sprite.
|
||||
*
|
||||
* @param sprite The texture to draw.
|
||||
* @param x The x position of the rectangle we'll draw.
|
||||
* @param y The x position of the rectangle we'll draw.
|
||||
* @param width The width of the rectangle we'll draw.
|
||||
* @param height The height of the rectangle we'll draw.
|
||||
*/
|
||||
public void blit(TextureAtlasSprite sprite, int x, int y, int width, int height) {
|
||||
blit(x, y, width, height, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1());
|
||||
}
|
||||
public void blitTiled(
|
||||
TextureAtlasSprite sprite,
|
||||
int x, int y, int width, int height,
|
||||
int tileX, int tileY, int tileWidth, int tileHeight, int spriteWidth, int spriteHeight
|
||||
) {
|
||||
if (width <= 0 || height <= 0) return;
|
||||
if (tileWidth <= 0 || tileHeight <= 0) {
|
||||
throw new IllegalArgumentException("Tiled sprite texture size must be positive, got " + tileWidth + "x" + tileHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiGraphics#blitNineSliced},
|
||||
* the middle texture is stretched rather than repeated.
|
||||
*
|
||||
* @param sprite The texture to draw.
|
||||
* @param x The x position of the rectangle we'll draw.
|
||||
* @param y The x position of the rectangle we'll draw.
|
||||
* @param width The width of the rectangle we'll draw.
|
||||
* @param height The height of the rectangle we'll draw.
|
||||
* @param leftBorder The width of the left border.
|
||||
* @param rightBorder The width of the right border.
|
||||
* @param textureWidth The width of the whole texture.
|
||||
*/
|
||||
public void blitHorizontalSliced(TextureAtlasSprite sprite, int x, int y, int width, int height, int leftBorder, int rightBorder, int textureWidth) {
|
||||
// TODO(1.20.2)/TODO(1.21.0): Drive this from mcmeta files, like vanilla does.
|
||||
if (width < leftBorder + rightBorder) throw new IllegalArgumentException("width is less than two borders");
|
||||
|
||||
var centerStart = SpriteRenderer.u(sprite, leftBorder, textureWidth);
|
||||
var centerEnd = SpriteRenderer.u(sprite, textureWidth - rightBorder, textureWidth);
|
||||
|
||||
blit(x, y, leftBorder, height, sprite.getU0(), sprite.getV0(), centerStart, sprite.getV1());
|
||||
blit(x + leftBorder, y, width - leftBorder - rightBorder, height, centerStart, sprite.getV0(), centerEnd, sprite.getV1());
|
||||
blit(x + width - rightBorder, y, rightBorder, height, centerEnd, sprite.getV0(), sprite.getU1(), sprite.getV1());
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiGraphics#blitNineSliced},
|
||||
* the middle texture is stretched rather than repeated.
|
||||
*
|
||||
* @param sprite The texture to draw.
|
||||
* @param x The x position of the rectangle we'll draw.
|
||||
* @param y The x position of the rectangle we'll draw.
|
||||
* @param width The width of the rectangle we'll draw.
|
||||
* @param height The height of the rectangle we'll draw.
|
||||
* @param topBorder The height of the top border.
|
||||
* @param bottomBorder The height of the bottom border.
|
||||
* @param textureHeight The height of the whole texture.
|
||||
*/
|
||||
public void blitVerticalSliced(TextureAtlasSprite sprite, int x, int y, int width, int height, int topBorder, int bottomBorder, int textureHeight) {
|
||||
// TODO(1.20.2)/TODO(1.21.0): Drive this from mcmeta files, like vanilla does.
|
||||
if (width < topBorder + bottomBorder) throw new IllegalArgumentException("height is less than two borders");
|
||||
|
||||
var centerStart = SpriteRenderer.v(sprite, topBorder, textureHeight);
|
||||
var centerEnd = SpriteRenderer.v(sprite, textureHeight - bottomBorder, textureHeight);
|
||||
|
||||
blit(x, y, width, topBorder, sprite.getU0(), sprite.getV0(), sprite.getU1(), centerStart);
|
||||
blit(x, y + topBorder, width, height - topBorder - bottomBorder, sprite.getU0(), centerStart, sprite.getU1(), centerEnd);
|
||||
blit(x, y + height - bottomBorder, width, bottomBorder, sprite.getU0(), centerEnd, sprite.getU1(), sprite.getV1());
|
||||
}
|
||||
|
||||
/**
|
||||
* The low-level blit function, used to render a portion of the sprite sheet. Unlike other functions, this takes uvs rather than a single sprite.
|
||||
*
|
||||
* @param x The x position of the rectangle we'll draw.
|
||||
* @param y The x position of the rectangle we'll draw.
|
||||
* @param width The width of the rectangle we'll draw.
|
||||
* @param height The height of the rectangle we'll draw.
|
||||
* @param u0 The first U coordinate.
|
||||
* @param v0 The first V coordinate.
|
||||
* @param u1 The second U coordinate.
|
||||
* @param v1 The second V coordinate.
|
||||
*/
|
||||
public void blit(
|
||||
int x, int y, int width, int height, float u0, float v0, float u1, float v1) {
|
||||
builder.addVertex(transform, x, y + height, z).setColor(r, g, b, 255).setUv(u0, v1).setLight(light);
|
||||
builder.addVertex(transform, x + width, y + height, z).setColor(r, g, b, 255).setUv(u1, v1).setLight(light);
|
||||
builder.addVertex(transform, x + width, y, z).setColor(r, g, b, 255).setUv(u1, v0).setLight(light);
|
||||
builder.addVertex(transform, x, y, z).setColor(r, g, b, 255).setUv(u0, v0).setLight(light);
|
||||
}
|
||||
|
||||
public static float u(TextureAtlasSprite sprite, int x, int width) {
|
||||
return sprite.getU((float) x / width);
|
||||
}
|
||||
|
||||
public static float v(TextureAtlasSprite sprite, int y, int height) {
|
||||
return sprite.getV((float) y / height);
|
||||
for (var xOffset = 0; xOffset < width; xOffset += tileWidth) {
|
||||
var sliceWidth = Math.min(tileWidth, width - xOffset);
|
||||
for (var yOffset = 0; yOffset < height; yOffset += tileHeight) {
|
||||
var sliceHeight = Math.min(tileHeight, height - yOffset);
|
||||
blit(sprite, x + xOffset, y + yOffset, sliceWidth, sliceHeight, tileX, tileY, spriteWidth, spriteHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -62,8 +62,8 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
var matrix = transform.last().pose();
|
||||
var opacity = (int) (mc.options.getBackgroundOpacity(0.25f) * 255) << 24;
|
||||
var width = -font.width(label) / 2.0f;
|
||||
font.drawInBatch(label, width, (float) 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
|
||||
font.drawInBatch(label, width, (float) 0, CommonColors.WHITE, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
|
||||
font.drawInBatch(label, width, 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
|
||||
font.drawInBatch(label, width, 0, CommonColors.WHITE, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
@@ -76,7 +76,7 @@ public final class DataProviders {
|
||||
LecternPocketModel.TEXTURE_NORMAL, LecternPocketModel.TEXTURE_ADVANCED,
|
||||
LecternPocketModel.TEXTURE_COLOUR, LecternPocketModel.TEXTURE_FRAME, LecternPocketModel.TEXTURE_LIGHT
|
||||
)));
|
||||
out.accept(GuiSprites.SPRITE_SHEET, makeSprites(
|
||||
out.accept(ResourceLocation.withDefaultNamespace("gui"), makeSprites(
|
||||
// Computers
|
||||
GuiSprites.COMPUTER_NORMAL.textures(),
|
||||
GuiSprites.COMPUTER_ADVANCED.textures(),
|
||||
@@ -85,6 +85,8 @@ public final class DataProviders {
|
||||
));
|
||||
});
|
||||
|
||||
generator.add(ResourceMetadataProvider::new);
|
||||
|
||||
generator.add(pack -> new ExtraModelsProvider(pack, fullRegistries) {
|
||||
@Override
|
||||
public Stream<ResourceLocation> getModels(HolderLookup.Provider registries) {
|
||||
|
@@ -108,6 +108,8 @@ public final class LanguageProvider implements DataProvider {
|
||||
add(ComputerCraftTags.Items.TURTLE, "Turtles");
|
||||
add(ComputerCraftTags.Items.WIRED_MODEM, "Wired modems");
|
||||
add(ComputerCraftTags.Items.MONITOR, "Monitors");
|
||||
add(ComputerCraftTags.Items.DISKS, "Disks");
|
||||
add(ComputerCraftTags.Items.POCKET_COMPUTERS, "Pocket Computers");
|
||||
add(ComputerCraftTags.Items.DYEABLE, "Dyable items");
|
||||
add(ComputerCraftTags.Items.TURTLE_CAN_PLACE, "Turtle-placeable items");
|
||||
|
||||
@@ -187,7 +189,6 @@ public final class LanguageProvider implements DataProvider {
|
||||
// Metrics
|
||||
add(Metrics.COMPUTER_TASKS, "Tasks");
|
||||
add(Metrics.SERVER_TASKS, "Server tasks");
|
||||
add(Metrics.JAVA_ALLOCATION, "Java Allocations");
|
||||
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
|
||||
add(Metrics.FS_OPS, "Filesystem operations");
|
||||
add(Metrics.HTTP_REQUESTS, "HTTP requests");
|
||||
|
@@ -0,0 +1,110 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import net.minecraft.client.resources.metadata.gui.GuiMetadataSection;
|
||||
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.metadata.PackMetadataGenerator;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.metadata.MetadataSectionType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Similar to {@link PackMetadataGenerator}, but for individual resources.
|
||||
*/
|
||||
final class ResourceMetadataProvider implements DataProvider {
|
||||
private final PackOutput output;
|
||||
|
||||
ResourceMetadataProvider(PackOutput output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
private void register(Builder builder) {
|
||||
for (var computerTextures : List.of(
|
||||
GuiSprites.COMPUTER_ADVANCED,
|
||||
GuiSprites.COMPUTER_COLOUR,
|
||||
GuiSprites.COMPUTER_COMMAND,
|
||||
GuiSprites.COMPUTER_NORMAL
|
||||
)) {
|
||||
builder.texture(computerTextures.border()).add(GuiMetadataSection.TYPE, new GuiMetadataSection(
|
||||
new GuiSpriteScaling.NineSlice(36, 36, simpleNineSlicedBorder(12))
|
||||
));
|
||||
|
||||
var sidebar = computerTextures.sidebar();
|
||||
if (sidebar != null) {
|
||||
builder.texture(sidebar).add(GuiMetadataSection.TYPE, new GuiMetadataSection(
|
||||
new GuiSpriteScaling.NineSlice(17, 14, new GuiSpriteScaling.NineSlice.Border(3, 4, 0, 3))
|
||||
));
|
||||
}
|
||||
|
||||
var pocketBottom = computerTextures.pocketBottom();
|
||||
if (pocketBottom != null) {
|
||||
builder.texture(pocketBottom).add(GuiMetadataSection.TYPE, new GuiMetadataSection(
|
||||
new GuiSpriteScaling.NineSlice(36, 20, new GuiSpriteScaling.NineSlice.Border(12, 0, 12, 0))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static GuiSpriteScaling.NineSlice.Border simpleNineSlicedBorder(int size) {
|
||||
return new GuiSpriteScaling.NineSlice.Border(size, size, size, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(CachedOutput cachedOutput) {
|
||||
var builder = new Builder();
|
||||
register(builder);
|
||||
|
||||
var outputPath = output.getOutputFolder(PackOutput.Target.RESOURCE_PACK);
|
||||
return CompletableFuture.allOf(builder.metadata.entrySet().stream().map(entry -> {
|
||||
var json = new JsonObject();
|
||||
entry.getValue().elements.forEach((name, element) -> json.add(name, element.get()));
|
||||
return DataProvider.saveStable(cachedOutput, json, outputPath.resolve(entry.getKey().getNamespace()).resolve(entry.getKey().getPath() + ".mcmeta"));
|
||||
}).toArray(CompletableFuture[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Resource Metadata";
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for a set of {@code mcmeta} files.
|
||||
*/
|
||||
private static final class Builder {
|
||||
private final Map<ResourceLocation, FileMetadata> metadata = new HashMap<>();
|
||||
|
||||
FileMetadata texture(ResourceLocation texture) {
|
||||
return file(texture.withPrefix("textures/").withSuffix(".png"));
|
||||
}
|
||||
|
||||
FileMetadata file(ResourceLocation path) {
|
||||
return metadata.computeIfAbsent(path, p -> new FileMetadata());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for a given file's {@code mcmeta} file.
|
||||
*/
|
||||
private static final class FileMetadata {
|
||||
private final Map<String, Supplier<JsonElement>> elements = new HashMap<>();
|
||||
|
||||
<T> FileMetadata add(MetadataSectionType<T> type, T value) {
|
||||
elements.put(type.getMetadataSectionName(), () -> type.toJson(value));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@@ -96,6 +96,8 @@ class TagProvider {
|
||||
tags.copy(ComputerCraftTags.Blocks.TURTLE, ComputerCraftTags.Items.TURTLE);
|
||||
tags.tag(ComputerCraftTags.Items.WIRED_MODEM).add(ModRegistry.Items.WIRED_MODEM.get(), ModRegistry.Items.WIRED_MODEM_FULL.get());
|
||||
tags.copy(ComputerCraftTags.Blocks.MONITOR, ComputerCraftTags.Items.MONITOR);
|
||||
tags.tag(ComputerCraftTags.Items.DISKS).add(ModRegistry.Items.DISK.get(), ModRegistry.Items.TREASURE_DISK.get());
|
||||
tags.tag(ComputerCraftTags.Items.POCKET_COMPUTERS).add(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
||||
|
||||
tags.tag(ComputerCraftTags.Items.DYEABLE)
|
||||
.addTag(ComputerCraftTags.Items.TURTLE)
|
||||
|
@@ -205,8 +205,10 @@
|
||||
"item.computercraft.treasure_disk": "Floppy Disk",
|
||||
"itemGroup.computercraft": "ComputerCraft",
|
||||
"tag.item.computercraft.computer": "Computers",
|
||||
"tag.item.computercraft.disks": "Disks",
|
||||
"tag.item.computercraft.dyeable": "Dyable items",
|
||||
"tag.item.computercraft.monitor": "Monitors",
|
||||
"tag.item.computercraft.pocket_computers": "Pocket Computers",
|
||||
"tag.item.computercraft.turtle": "Turtles",
|
||||
"tag.item.computercraft.turtle_can_place": "Turtle-placeable items",
|
||||
"tag.item.computercraft.wired_modem": "Wired modems",
|
||||
@@ -217,7 +219,6 @@
|
||||
"tracking_field.computercraft.http_download.name": "HTTP download",
|
||||
"tracking_field.computercraft.http_requests.name": "HTTP requests",
|
||||
"tracking_field.computercraft.http_upload.name": "HTTP upload",
|
||||
"tracking_field.computercraft.java_allocation.name": "Java Allocations",
|
||||
"tracking_field.computercraft.max": "%s (max)",
|
||||
"tracking_field.computercraft.peripheral.name": "Peripheral calls",
|
||||
"tracking_field.computercraft.server_tasks.name": "Server tasks",
|
||||
|
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_advanced.png.mcmeta
generated
Normal file
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_advanced.png.mcmeta
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": 12,
|
||||
"height": 36,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_colour.png.mcmeta
generated
Normal file
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_colour.png.mcmeta
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": 12,
|
||||
"height": 36,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_command.png.mcmeta
generated
Normal file
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_command.png.mcmeta
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": 12,
|
||||
"height": 36,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_normal.png.mcmeta
generated
Normal file
10
projects/common/src/generated/resources/assets/computercraft/textures/gui/border_normal.png.mcmeta
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": 12,
|
||||
"height": 36,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": {
|
||||
"bottom": 0,
|
||||
"left": 12,
|
||||
"right": 12,
|
||||
"top": 0
|
||||
},
|
||||
"height": 20,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": {
|
||||
"bottom": 0,
|
||||
"left": 12,
|
||||
"right": 12,
|
||||
"top": 0
|
||||
},
|
||||
"height": 20,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": {
|
||||
"bottom": 0,
|
||||
"left": 12,
|
||||
"right": 12,
|
||||
"top": 0
|
||||
},
|
||||
"height": 20,
|
||||
"width": 36
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": {
|
||||
"bottom": 3,
|
||||
"left": 3,
|
||||
"right": 0,
|
||||
"top": 4
|
||||
},
|
||||
"height": 14,
|
||||
"width": 17
|
||||
}
|
||||
}
|
||||
}
|
15
projects/common/src/generated/resources/assets/computercraft/textures/gui/sidebar_command.png.mcmeta
generated
Normal file
15
projects/common/src/generated/resources/assets/computercraft/textures/gui/sidebar_command.png.mcmeta
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": {
|
||||
"bottom": 3,
|
||||
"left": 3,
|
||||
"right": 0,
|
||||
"top": 4
|
||||
},
|
||||
"height": 14,
|
||||
"width": 17
|
||||
}
|
||||
}
|
||||
}
|
15
projects/common/src/generated/resources/assets/computercraft/textures/gui/sidebar_normal.png.mcmeta
generated
Normal file
15
projects/common/src/generated/resources/assets/computercraft/textures/gui/sidebar_normal.png.mcmeta
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"gui": {
|
||||
"scaling": {
|
||||
"type": "nine_slice",
|
||||
"border": {
|
||||
"bottom": 3,
|
||||
"left": 3,
|
||||
"right": 0,
|
||||
"top": 4
|
||||
},
|
||||
"height": 14,
|
||||
"width": 17
|
||||
}
|
||||
}
|
||||
}
|
1
projects/common/src/generated/resources/data/computercraft/tags/item/disks.json
generated
Normal file
1
projects/common/src/generated/resources/data/computercraft/tags/item/disks.json
generated
Normal file
@@ -0,0 +1 @@
|
||||
{"values": ["computercraft:disk", "computercraft:treasure_disk"]}
|
1
projects/common/src/generated/resources/data/computercraft/tags/item/pocket_computers.json
generated
Normal file
1
projects/common/src/generated/resources/data/computercraft/tags/item/pocket_computers.json
generated
Normal file
@@ -0,0 +1 @@
|
||||
{"values": ["computercraft:pocket_computer_normal", "computercraft:pocket_computer_advanced"]}
|
@@ -432,8 +432,8 @@ final class WiredNetworkImpl {
|
||||
}
|
||||
|
||||
private static WiredNodeImpl checkNode(WiredNode node) {
|
||||
if (node instanceof WiredNodeImpl) {
|
||||
return (WiredNodeImpl) node;
|
||||
if (node instanceof WiredNodeImpl n) {
|
||||
return n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown implementation of IWiredNode: " + node);
|
||||
}
|
||||
|
@@ -13,7 +13,10 @@ import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
|
||||
import dan200.computercraft.shared.util.DropConsumer;
|
||||
import dan200.computercraft.shared.util.TickScheduler;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -25,9 +28,8 @@ import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.item.component.TooltipProvider;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
@@ -40,8 +42,10 @@ import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Event listeners for server/common code.
|
||||
@@ -163,4 +167,42 @@ public final class CommonHooks {
|
||||
out.accept(ModRegistry.Items.COMPUTER_COMMAND.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static void onItemTooltip(ItemStack stack, Item.TooltipContext context, TooltipFlag flags, List<Component> out) {
|
||||
var appender = new TooltipAppender(out);
|
||||
addToTooltip(stack, ModRegistry.DataComponents.PRINTOUT.get(), context, appender, flags);
|
||||
addToTooltip(stack, ModRegistry.DataComponents.TREASURE_DISK.get(), context, appender, flags);
|
||||
|
||||
// Disk and computer IDs require some conditional logic, so we don't bother using TooltipProvider.
|
||||
|
||||
var diskId = stack.get(ModRegistry.DataComponents.DISK_ID.get());
|
||||
if (diskId != null && flags.isAdvanced()) diskId.addToTooltip("gui.computercraft.tooltip.disk_id", appender);
|
||||
|
||||
var computerId = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
|
||||
if (computerId != null && (flags.isAdvanced() || !stack.has(DataComponents.CUSTOM_NAME))) {
|
||||
computerId.addToTooltip("gui.computercraft.tooltip.computer_id", appender);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts additional tooltip items directly after the custom name, rather than at the very end.
|
||||
*/
|
||||
private static final class TooltipAppender implements Consumer<Component> {
|
||||
private final List<Component> out;
|
||||
private int index = 1;
|
||||
|
||||
private TooltipAppender(List<Component> out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Component component) {
|
||||
out.add(index++, component);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends TooltipProvider> void addToTooltip(ItemStack stack, DataComponentType<T> component, Item.TooltipContext context, Consumer<Component> out, TooltipFlag flags) {
|
||||
var provider = stack.get(component);
|
||||
if (provider != null) provider.addToTooltip(context, out, flags);
|
||||
}
|
||||
}
|
||||
|
@@ -34,9 +34,8 @@ import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.TerminalSize;
|
||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
||||
import dan200.computercraft.shared.computer.items.ServerComputerReference;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||
@@ -112,10 +111,7 @@ import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
@@ -262,9 +258,9 @@ public final class ModRegistry {
|
||||
return REGISTRY.register(parent.id().getPath(), () -> supplier.apply(parent.get(), properties()));
|
||||
}
|
||||
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_NORMAL = ofBlock(Blocks.COMPUTER_NORMAL, ComputerItem::new);
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, ComputerItem::new);
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, CommandComputerItem::new);
|
||||
public static final RegistryEntry<BlockItem> COMPUTER_NORMAL = ofBlock(Blocks.COMPUTER_NORMAL, BlockItem::new);
|
||||
public static final RegistryEntry<BlockItem> COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, BlockItem::new);
|
||||
public static final RegistryEntry<GameMasterBlockItem> COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, GameMasterBlockItem::new);
|
||||
|
||||
public static final RegistryEntry<PocketComputerItem> POCKET_COMPUTER_NORMAL = REGISTRY.register("pocket_computer_normal",
|
||||
() -> new PocketComputerItem(properties().stacksTo(1), ComputerFamily.NORMAL));
|
||||
@@ -276,19 +272,19 @@ public final class ModRegistry {
|
||||
|
||||
public static final RegistryEntry<DiskItem> DISK =
|
||||
REGISTRY.register("disk", () -> new DiskItem(properties().stacksTo(1)));
|
||||
public static final RegistryEntry<TreasureDiskItem> TREASURE_DISK =
|
||||
REGISTRY.register("treasure_disk", () -> new TreasureDiskItem(properties().stacksTo(1)));
|
||||
public static final RegistryEntry<DiskItem> TREASURE_DISK =
|
||||
REGISTRY.register("treasure_disk", () -> new DiskItem(properties().stacksTo(1)));
|
||||
|
||||
private static Item.Properties printoutProperties() {
|
||||
return properties().stacksTo(1).component(DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
||||
}
|
||||
|
||||
public static final RegistryEntry<PrintoutItem> PRINTED_PAGE = REGISTRY.register("printed_page",
|
||||
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.PAGE));
|
||||
() -> new PrintoutItem(printoutProperties()));
|
||||
public static final RegistryEntry<PrintoutItem> PRINTED_PAGES = REGISTRY.register("printed_pages",
|
||||
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.PAGES));
|
||||
() -> new PrintoutItem(printoutProperties()));
|
||||
public static final RegistryEntry<PrintoutItem> PRINTED_BOOK = REGISTRY.register("printed_book",
|
||||
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.BOOK));
|
||||
() -> new PrintoutItem(printoutProperties()));
|
||||
|
||||
public static final RegistryEntry<BlockItem> SPEAKER = ofBlock(Blocks.SPEAKER, BlockItem::new);
|
||||
public static final RegistryEntry<BlockItem> DISK_DRIVE = ofBlock(Blocks.DISK_DRIVE, BlockItem::new);
|
||||
@@ -315,9 +311,6 @@ public final class ModRegistry {
|
||||
|
||||
/**
|
||||
* The id of a computer.
|
||||
*
|
||||
* @see ComputerItem
|
||||
* @see PocketComputerItem
|
||||
*/
|
||||
public static final RegistryEntry<DataComponentType<NonNegativeId>> COMPUTER_ID = register("computer_id", b -> b
|
||||
.persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
|
||||
@@ -325,15 +318,18 @@ public final class ModRegistry {
|
||||
|
||||
/**
|
||||
* The storage capacity of a computer or disk.
|
||||
*
|
||||
* @see ComputerItem
|
||||
* @see PocketComputerItem
|
||||
* @see DiskItem
|
||||
*/
|
||||
public static final RegistryEntry<DataComponentType<StorageCapacity>> STORAGE_CAPACITY = register("storage_capacity", b -> b
|
||||
.persistent(StorageCapacity.CODEC).networkSynchronized(StorageCapacity.STREAM_CODEC)
|
||||
);
|
||||
|
||||
/**
|
||||
* The terminal size of a computer.
|
||||
*/
|
||||
public static final RegistryEntry<DataComponentType<TerminalSize>> TERMINAL_SIZE = register("terminal_size", b -> b
|
||||
.persistent(TerminalSize.CODEC).networkSynchronized(TerminalSize.STREAM_CODEC)
|
||||
);
|
||||
|
||||
/**
|
||||
* The left upgrade of a turtle.
|
||||
*
|
||||
@@ -397,9 +393,6 @@ public final class ModRegistry {
|
||||
|
||||
/**
|
||||
* Information about a treasure disk's mount.
|
||||
*
|
||||
* @see TreasureDiskItem
|
||||
* @see TreasureDisk
|
||||
*/
|
||||
public static final RegistryEntry<DataComponentType<TreasureDisk>> TREASURE_DISK = register("treasure_disk", b -> b
|
||||
.persistent(TreasureDisk.CODEC).networkSynchronized(TreasureDisk.STREAM_CODEC)
|
||||
@@ -407,8 +400,6 @@ public final class ModRegistry {
|
||||
|
||||
/**
|
||||
* The id of a disk.
|
||||
*
|
||||
* @see DiskItem
|
||||
*/
|
||||
public static final RegistryEntry<DataComponentType<NonNegativeId>> DISK_ID = register("disk_id", b -> b
|
||||
.persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
|
||||
|
@@ -50,6 +50,12 @@ public enum UserLevel implements Predicate<CommandSourceStack> {
|
||||
|
||||
public static boolean isOwner(CommandSourceStack source) {
|
||||
var server = source.getServer();
|
||||
|
||||
// While CommandSourceStack.getServer is non-nullable, that's a lie for permission checks. When loading
|
||||
// .mcfunction files, ServerFunctionLibrary constructs an instance with an empty server. In that case, return
|
||||
// false — we don't want to treat functions as an owner!
|
||||
if (server == null) return false;
|
||||
|
||||
var player = source.getPlayer();
|
||||
return server.isDedicatedServer()
|
||||
? source.getEntity() == null && source.hasPermission(4) && source.getTextName().equals("Server")
|
||||
|
@@ -66,8 +66,8 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
||||
public LiteralArgumentBuilder<CommandSourceStack> then(final ArgumentBuilder<CommandSourceStack, ?> argument) {
|
||||
if (getRedirect() != null) throw new IllegalStateException("Cannot add children to a redirected node");
|
||||
|
||||
if (argument instanceof HelpingArgumentBuilder) {
|
||||
children.add((HelpingArgumentBuilder) argument);
|
||||
if (argument instanceof HelpingArgumentBuilder child) {
|
||||
children.add(child);
|
||||
} else if (argument instanceof LiteralArgumentBuilder) {
|
||||
super.then(argument);
|
||||
} else {
|
||||
|
@@ -4,14 +4,15 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.component.DataComponentMap;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -64,12 +65,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
return computer.getRedstoneOutput(localSide);
|
||||
}
|
||||
|
||||
private ItemStack getItem(AbstractComputerBlockEntity tile) {
|
||||
var stack = new ItemStack(this);
|
||||
stack.applyComponents(tile.collectComponents());
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {
|
||||
return getDirectSignal(state, world, pos, incomingSide);
|
||||
@@ -89,13 +84,11 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
|
||||
@Override
|
||||
public ItemStack getCloneItemStack(LevelReader world, BlockPos pos, BlockState state) {
|
||||
var tile = world.getBlockEntity(pos);
|
||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
||||
var result = getItem(computer);
|
||||
if (!result.isEmpty()) return result;
|
||||
var stack = super.getCloneItemStack(world, pos, state);
|
||||
if (world.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) {
|
||||
stack.applyComponents(computer.collectComponents());
|
||||
}
|
||||
|
||||
return super.getCloneItemStack(world, pos, state);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +114,10 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
var serverComputer = computer.createServerComputer();
|
||||
serverComputer.turnOn();
|
||||
|
||||
PlatformHelper.get().openMenu(player, computer.getName(), computer, new ComputerContainerData(serverComputer, getItem(computer)));
|
||||
var stack = new ItemStack(this);
|
||||
stack.applyComponents(Util.make(DataComponentMap.builder(), computer::collectSafeComponents).build());
|
||||
|
||||
PlatformHelper.get().openMenu(player, computer.getName(), computer, new ComputerContainerData(serverComputer, stack));
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
@@ -135,12 +131,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
if (be instanceof AbstractComputerBlockEntity computer) computer.neighborChanged(neighbourPos);
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public final void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbour) {
|
||||
var be = world.getBlockEntity(pos);
|
||||
if (be instanceof AbstractComputerBlockEntity computer) computer.neighborChanged(neighbour);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||
var be = level.getBlockEntity(pos);
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
@@ -187,10 +188,20 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
@Override
|
||||
protected void collectImplicitComponents(DataComponentMap.Builder builder) {
|
||||
super.collectImplicitComponents(builder);
|
||||
collectSafeComponents(builder);
|
||||
if (lockCode != LockCode.NO_LOCK) builder.set(DataComponents.LOCK, lockCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect components that are safe to share with the client.
|
||||
*
|
||||
* @param builder The component builder.
|
||||
*/
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
protected void collectSafeComponents(DataComponentMap.Builder builder) {
|
||||
builder.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.of(computerID));
|
||||
builder.set(DataComponents.CUSTOM_NAME, label == null ? null : Component.literal(label));
|
||||
builder.set(ModRegistry.DataComponents.STORAGE_CAPACITY.get(), storageCapacity > 0 ? new StorageCapacity(storageCapacity) : null);
|
||||
if (lockCode != LockCode.NO_LOCK) builder.set(DataComponents.LOCK, lockCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -16,7 +16,6 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
* permission.
|
||||
*
|
||||
* @param <T> The type of the computer block entity.
|
||||
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
|
||||
*/
|
||||
public class CommandComputerBlock<T extends ComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
|
||||
private static final MapCodec<CommandComputerBlock<?>> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
|
@@ -10,10 +10,15 @@ import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.TerminalSize;
|
||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.component.DataComponentMap;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@@ -23,17 +28,66 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class ComputerBlockEntity extends AbstractComputerBlockEntity {
|
||||
private static final String NBT_TERMINAL_SIZE = "TerminalSize";
|
||||
|
||||
private @Nullable TerminalSize terminalSize;
|
||||
|
||||
private @Nullable IPeripheral peripheral;
|
||||
|
||||
public ComputerBlockEntity(BlockEntityType<? extends ComputerBlockEntity> type, BlockPos pos, BlockState state, ComputerFamily family) {
|
||||
super(type, pos, state, family);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadServer(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||
super.loadServer(nbt, registries);
|
||||
terminalSize = NBTUtil.decodeFrom(TerminalSize.CODEC, registries, nbt, NBT_TERMINAL_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||
super.saveAdditional(nbt, registries);
|
||||
NBTUtil.encodeTo(TerminalSize.CODEC, registries, nbt, NBT_TERMINAL_SIZE, terminalSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadClient(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||
super.loadClient(nbt, registries);
|
||||
terminalSize = NBTUtil.decodeFrom(TerminalSize.CODEC, registries, nbt, NBT_TERMINAL_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
|
||||
var tag = super.getUpdateTag(registries);
|
||||
NBTUtil.encodeTo(TerminalSize.CODEC, registries, tag, NBT_TERMINAL_SIZE, terminalSize);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyImplicitComponents(DataComponentInput component) {
|
||||
super.applyImplicitComponents(component);
|
||||
terminalSize = component.get(ModRegistry.DataComponents.TERMINAL_SIZE.get());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void collectSafeComponents(DataComponentMap.Builder builder) {
|
||||
super.collectSafeComponents(builder);
|
||||
builder.set(ModRegistry.DataComponents.TERMINAL_SIZE.get(), terminalSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void removeComponentsFromTag(CompoundTag tag) {
|
||||
super.removeComponentsFromTag(tag);
|
||||
tag.remove(NBT_TERMINAL_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerComputer createComputer(int id) {
|
||||
return new ServerComputer((ServerLevel) getLevel(), getBlockPos(), ServerComputer.properties(id, getFamily())
|
||||
.label(getLabel())
|
||||
.terminalSize(ConfigSpec.computerTermWidth.get(), ConfigSpec.computerTermHeight.get())
|
||||
.terminalSize(terminalSize != null ? terminalSize : new TerminalSize(ConfigSpec.computerTermWidth.get(), ConfigSpec.computerTermHeight.get()))
|
||||
.storageCapacity(storageCapacity)
|
||||
);
|
||||
}
|
||||
|
@@ -295,10 +295,9 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
|
||||
return this;
|
||||
}
|
||||
|
||||
public Properties terminalSize(int width, int height) {
|
||||
if (width <= 0 || height <= 0) throw new IllegalArgumentException("Terminal size must be positive");
|
||||
this.terminalWidth = width;
|
||||
this.terminalHeight = height;
|
||||
public Properties terminalSize(TerminalSize size) {
|
||||
this.terminalWidth = size.width();
|
||||
this.terminalHeight = size.height();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.computer.core;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
|
||||
/**
|
||||
* The size of a computer terminal.
|
||||
*
|
||||
* @param width The terminal's width.
|
||||
* @param height The terminal's height.
|
||||
*/
|
||||
public record TerminalSize(int width, int height) {
|
||||
public static final int MAX_SIZE = 255;
|
||||
public static final Codec<TerminalSize> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
ExtraCodecs.intRange(1, MAX_SIZE).fieldOf("width").forGetter(TerminalSize::width),
|
||||
ExtraCodecs.intRange(1, MAX_SIZE).fieldOf("height").forGetter(TerminalSize::height)
|
||||
).apply(instance, TerminalSize::new));
|
||||
|
||||
public static final StreamCodec<ByteBuf, TerminalSize> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT, TerminalSize::width,
|
||||
ByteBufCodecs.VAR_INT, TerminalSize::height,
|
||||
TerminalSize::new
|
||||
);
|
||||
|
||||
public TerminalSize {
|
||||
checkBounds("width", width);
|
||||
checkBounds("height", height);
|
||||
}
|
||||
|
||||
private static void checkBounds(String name, int value) {
|
||||
if (value < 1 || value > MAX_SIZE) {
|
||||
throw new IllegalArgumentException(name + " must be between 1 and " + MAX_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.computer.items;
|
||||
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ComputerItem} which prevents players placing it without permission.
|
||||
*
|
||||
* @see net.minecraft.world.item.GameMasterBlockItem
|
||||
* @see dan200.computercraft.shared.computer.blocks.CommandComputerBlock
|
||||
*/
|
||||
public class CommandComputerItem extends ComputerItem {
|
||||
public CommandComputerItem(ComputerBlock<?> block, Properties settings) {
|
||||
super(block, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable BlockState getPlacementState(BlockPlaceContext context) {
|
||||
// Prohibit players placing this block in survival or when not opped.
|
||||
var player = context.getPlayer();
|
||||
return player != null && !player.canUseGameMasterBlocks() ? null : super.getPlacementState(context);
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
package dan200.computercraft.shared.computer.items;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ComputerItem extends BlockItem {
|
||||
public ComputerItem(AbstractComputerBlock<?> block, Properties settings) {
|
||||
super(block, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||
if (options.isAdvanced() || !stack.has(DataComponents.CUSTOM_NAME)) {
|
||||
var id = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
|
||||
if (id != null) {
|
||||
list.add(Component.translatable("gui.computercraft.tooltip.computer_id", id.id())
|
||||
.withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -74,7 +74,7 @@ public class ServerInputState<T extends AbstractContainerMenu & ComputerMenu> im
|
||||
}
|
||||
|
||||
private static boolean isValidClipboard(ByteBuffer buffer) {
|
||||
for (int i = buffer.remaining(), max = buffer.limit(); i < max; i++) {
|
||||
for (int i = buffer.position(), max = buffer.limit(); i < max; i++) {
|
||||
if (!StringUtil.isTypableChar(buffer.get(i))) return false;
|
||||
}
|
||||
return true;
|
||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||
import dan200.computercraft.core.apis.http.options.ProxyType;
|
||||
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
|
||||
import dan200.computercraft.shared.computer.core.TerminalSize;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -344,13 +345,13 @@ public final class ConfigSpec {
|
||||
.push("term_sizes");
|
||||
|
||||
builder.comment("Terminal size of computers.").push("computer");
|
||||
computerTermWidth = builder.comment("Width of computer terminal").defineInRange("width", Config.DEFAULT_COMPUTER_TERM_WIDTH, 1, 255);
|
||||
computerTermHeight = builder.comment("Height of computer terminal").defineInRange("height", Config.DEFAULT_COMPUTER_TERM_HEIGHT, 1, 255);
|
||||
computerTermWidth = builder.comment("Width of computer terminal").defineInRange("width", Config.DEFAULT_COMPUTER_TERM_WIDTH, 1, TerminalSize.MAX_SIZE);
|
||||
computerTermHeight = builder.comment("Height of computer terminal").defineInRange("height", Config.DEFAULT_COMPUTER_TERM_HEIGHT, 1, TerminalSize.MAX_SIZE);
|
||||
builder.pop();
|
||||
|
||||
builder.comment("Terminal size of pocket computers.").push("pocket_computer");
|
||||
pocketTermWidth = builder.comment("Width of pocket computer terminal").defineInRange("width", Config.DEFAULT_POCKET_TERM_WIDTH, 1, 255);
|
||||
pocketTermHeight = builder.comment("Height of pocket computer terminal").defineInRange("height", Config.DEFAULT_POCKET_TERM_HEIGHT, 1, 255);
|
||||
pocketTermWidth = builder.comment("Width of pocket computer terminal").defineInRange("width", Config.DEFAULT_POCKET_TERM_WIDTH, 1, TerminalSize.MAX_SIZE);
|
||||
pocketTermHeight = builder.comment("Height of pocket computer terminal").defineInRange("height", Config.DEFAULT_POCKET_TERM_HEIGHT, 1, TerminalSize.MAX_SIZE);
|
||||
builder.pop();
|
||||
|
||||
builder.comment("Maximum size of monitors (in blocks).").push("monitor");
|
||||
|
@@ -8,9 +8,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||
import dan200.computercraft.shared.util.NonNegativeId;
|
||||
import dan200.computercraft.shared.util.StorageCapacity;
|
||||
@@ -27,12 +25,12 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public final class MountMedia implements IMedia {
|
||||
/**
|
||||
* A {@link MountMedia} implementation for {@linkplain ComputerItem computers}.
|
||||
* A {@link MountMedia} implementation for {@linkplain ModRegistry.DataComponents#COMPUTER_ID computers}.
|
||||
*/
|
||||
public static final IMedia COMPUTER = new MountMedia("computer", ModRegistry.DataComponents.COMPUTER_ID, false, ConfigSpec.computerSpaceLimit);
|
||||
|
||||
/**
|
||||
* A {@link MountMedia} implementation for {@linkplain DiskItem disks}.
|
||||
* A {@link MountMedia} implementation for {@linkplain ModRegistry.Items#DISK disks}.
|
||||
*/
|
||||
public static final IMedia DISK = new MountMedia("disk", ModRegistry.DataComponents.DISK_ID, true, ConfigSpec.floppySpaceLimit);
|
||||
|
||||
|
@@ -1,51 +1,24 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.media.items;
|
||||
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.util.NonNegativeId;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
|
||||
/**
|
||||
* An item that can be shift-right-clicked into a {@link DiskDriveBlock}.
|
||||
*/
|
||||
public class DiskItem extends Item {
|
||||
public DiskItem(Properties settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||
if (options.isAdvanced()) {
|
||||
var id = stack.get(ModRegistry.DataComponents.DISK_ID.get());
|
||||
if (id != null) {
|
||||
list.add(Component.translatable("gui.computercraft.tooltip.disk_id", id.id())
|
||||
.withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public boolean doesSneakBypassUse(ItemStack stack, LevelReader world, BlockPos pos, Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getDiskID(ItemStack stack) {
|
||||
return NonNegativeId.getId(stack.get(ModRegistry.DataComponents.DISK_ID.get()));
|
||||
}
|
||||
|
||||
public static int getColour(ItemStack stack) {
|
||||
return DyedItemColor.getOrDefault(stack, Colour.WHITE.getARGB());
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
return DiskDriveBlock.defaultUseItemOn(context);
|
||||
}
|
||||
}
|
||||
|
@@ -12,11 +12,16 @@ import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.core.component.DataComponentHolder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.component.TooltipProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@@ -27,7 +32,7 @@ import java.util.stream.Stream;
|
||||
* @see PrintoutItem
|
||||
* @see dan200.computercraft.shared.ModRegistry.DataComponents#PRINTOUT
|
||||
*/
|
||||
public record PrintoutData(String title, List<Line> lines) implements PrintoutContents {
|
||||
public record PrintoutData(String title, List<Line> lines) implements PrintoutContents, TooltipProvider {
|
||||
public static final int LINE_LENGTH = 25;
|
||||
public static final int LINES_PER_PAGE = 21;
|
||||
public static final int MAX_PAGES = 16;
|
||||
@@ -71,6 +76,11 @@ public record PrintoutData(String title, List<Line> lines) implements PrintoutCo
|
||||
PrintoutData::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public void addToTooltip(Item.TooltipContext context, Consumer<Component> out, TooltipFlag flag) {
|
||||
if (!title().isEmpty()) out.accept(Component.literal(title()));
|
||||
}
|
||||
|
||||
/**
|
||||
* A single line on our printed pages.
|
||||
*
|
||||
|
@@ -14,29 +14,11 @@ 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.level.Level;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PrintoutItem extends Item {
|
||||
public enum Type {
|
||||
PAGE,
|
||||
PAGES,
|
||||
BOOK
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
||||
public PrintoutItem(Properties settings, Type type) {
|
||||
public PrintoutItem(Properties settings) {
|
||||
super(settings);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||
var title = PrintoutData.getOrEmpty(stack).title();
|
||||
if (!title.isEmpty()) list.add(Component.literal(title));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,8 +31,4 @@ public class PrintoutItem extends Item {
|
||||
}
|
||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack);
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
@@ -9,17 +9,25 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import net.minecraft.core.component.DataComponentHolder;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.component.TooltipProvider;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Stores information about a {@linkplain TreasureDiskItem treasure disk's} mount.
|
||||
* Stores information about a {@linkplain ModRegistry.Items#TREASURE_DISK treasure disk's} mount.
|
||||
*
|
||||
* @param name The name/title of the disk.
|
||||
* @param path The subpath to the resource
|
||||
* @see ModRegistry.DataComponents#TREASURE_DISK
|
||||
*/
|
||||
public record TreasureDisk(String name, String path) {
|
||||
public record TreasureDisk(String name, String path) implements TooltipProvider {
|
||||
public static final TreasureDisk UNDEFINED = new TreasureDisk("'missingno' by how did you get this anyway?", "undefined");
|
||||
|
||||
public static final Codec<TreasureDisk> CODEC = RecordCodecBuilder.create(i -> i.group(
|
||||
Codec.STRING.fieldOf("name").forGetter(TreasureDisk::name),
|
||||
Codec.STRING.fieldOf("path").forGetter(TreasureDisk::path)
|
||||
@@ -32,7 +40,11 @@ public record TreasureDisk(String name, String path) {
|
||||
);
|
||||
|
||||
public static String getTitle(DataComponentHolder holder) {
|
||||
var nbt = holder.get(ModRegistry.DataComponents.TREASURE_DISK.get());
|
||||
return nbt != null ? nbt.name() : "'missingno' by how did you get this anyway?";
|
||||
return holder.getOrDefault(ModRegistry.DataComponents.TREASURE_DISK.get(), UNDEFINED).name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToTooltip(Item.TooltipContext context, Consumer<Component> out, TooltipFlag flags) {
|
||||
out.accept(Component.literal(name()));
|
||||
}
|
||||
}
|
||||
|
@@ -1,32 +0,0 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
package dan200.computercraft.shared.media.items;
|
||||
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
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.level.LevelReader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TreasureDiskItem extends Item {
|
||||
public TreasureDiskItem(Properties settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag tooltipOptions) {
|
||||
list.add(Component.literal(TreasureDisk.getTitle(stack)));
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public boolean doesSneakBypassUse(ItemStack stack, LevelReader world, BlockPos pos, Player player) {
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -17,7 +17,7 @@ import org.jspecify.annotations.Nullable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An {@link IMedia} instance for {@linkplain TreasureDiskItem treasure disks}.
|
||||
* An {@link IMedia} instance for {@linkplain ModRegistry.Items#TREASURE_DISK treasure disks}.
|
||||
*/
|
||||
public final class TreasureDiskMedia implements IMedia {
|
||||
public static final IMedia INSTANCE = new TreasureDiskMedia();
|
||||
|
@@ -7,13 +7,11 @@ package dan200.computercraft.shared.peripheral.diskdrive;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.HorizontalContainerBlock;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.ItemInteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
@@ -23,7 +21,6 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class DiskDriveBlock extends HorizontalContainerBlock {
|
||||
@@ -50,19 +47,24 @@ public class DiskDriveBlock extends HorizontalContainerBlock {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (player.isCrouching() && level.getBlockEntity(pos) instanceof DiskDriveBlockEntity drive) {
|
||||
// Try to put a disk into the drive
|
||||
if (stack.isEmpty()) return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION;
|
||||
|
||||
if (!level.isClientSide && drive.getDiskStack().isEmpty() && PlatformHelper.get().getMedia(stack) != null) {
|
||||
drive.setDiskStack(stack.split(1));
|
||||
/**
|
||||
* A default implementation of {@link Item#useOn(UseOnContext)} for items that can be placed into a drive.
|
||||
*
|
||||
* @param context The context of this item usage action.
|
||||
* @return Whether the item was placed or not.
|
||||
*/
|
||||
public static InteractionResult defaultUseItemOn(UseOnContext context) {
|
||||
var level = context.getLevel();
|
||||
var blockPos = context.getClickedPos();
|
||||
var blockState = level.getBlockState(blockPos);
|
||||
if (blockState.is(ModRegistry.Blocks.DISK_DRIVE.get()) && blockState.getValue(STATE) == DiskDriveState.EMPTY) {
|
||||
if (!level.isClientSide && level.getBlockEntity(blockPos) instanceof DiskDriveBlockEntity drive && drive.getDiskStack().isEmpty()) {
|
||||
drive.setDiskStack(context.getItemInHand().split(1));
|
||||
}
|
||||
return ItemInteractionResult.sidedSuccess(level.isClientSide);
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
|
||||
return super.useItemOn(stack, state, level, pos, player, hand, hit);
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@@ -9,7 +9,7 @@ import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.util.StringUtil;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -170,8 +170,8 @@ public class DiskDrivePeripheral implements IPeripheral {
|
||||
*/
|
||||
@LuaFunction
|
||||
public final @Nullable Object @Nullable [] getDiskID() {
|
||||
var disk = diskDrive.getMedia().stack();
|
||||
return disk.getItem() instanceof DiskItem ? new Object[]{ DiskItem.getDiskID(disk) } : null;
|
||||
var id = diskDrive.getMedia().stack().get(ModRegistry.DataComponents.DISK_ID.get());
|
||||
return id != null ? new Object[]{ id.id() } : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -41,9 +41,9 @@ public abstract class AbstractFluidMethods<T> implements GenericPeripheral {
|
||||
* The returned table is sparse, and so empty tanks will be `nil` - it is recommended to loop over using [`pairs`]
|
||||
* rather than [`ipairs`].
|
||||
*
|
||||
* @param fluids The current fluid handler.
|
||||
* @param fluids The current fluid storage.
|
||||
* @return All tanks.
|
||||
* @cc.treturn { (table|nil)... } All tanks in this fluid storage.
|
||||
* @cc.treturn { (table|nil)... } Basic information about all fluids in this fluid storage.
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public abstract Map<Integer, Map<String, ?>> tanks(T fluids);
|
||||
|
@@ -56,8 +56,8 @@ public abstract class AbstractInventoryMethods<T> implements GenericPeripheral {
|
||||
* rather than [`ipairs`].
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @return All items in this inventory.
|
||||
* @cc.treturn { (table|nil)... } All items in this inventory.
|
||||
* @return Basic information about all items in this inventory.
|
||||
* @cc.treturn { (table|nil)... } Basic information about all items in this inventory.
|
||||
* @cc.usage Find an adjacent chest and print all items in it.
|
||||
*
|
||||
* <pre>{@code
|
||||
@@ -86,9 +86,8 @@ public abstract class AbstractInventoryMethods<T> implements GenericPeripheral {
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @param slot The slot to get information about.
|
||||
* @return Information about the item in this slot, or {@code nil} if not present.
|
||||
* @return Information about the item in this slot, or {@code nil} if it is empty.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @cc.treturn table Information about the item in this slot, or {@code nil} if not present.
|
||||
* @cc.usage Print some information about the first in a chest.
|
||||
*
|
||||
* <pre>{@code
|
||||
@@ -106,7 +105,7 @@ public abstract class AbstractInventoryMethods<T> implements GenericPeripheral {
|
||||
*/
|
||||
@Nullable
|
||||
@LuaFunction(mainThread = true)
|
||||
public abstract Map<String, ?> getItemDetail(T inventory, int slot) throws LuaException;
|
||||
public abstract Map<?, ?> getItemDetail(T inventory, int slot) throws LuaException;
|
||||
|
||||
/**
|
||||
* Get the maximum number of items which can be stored in this slot.
|
||||
|
@@ -252,11 +252,6 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
|
||||
if (world.getBlockEntity(pos) instanceof CableBlockEntity modem) modem.neighborChanged(neighbourPos);
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public final void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbour) {
|
||||
if (world.getBlockEntity(pos) instanceof CableBlockEntity modem) modem.neighborChanged(neighbour);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rand) {
|
||||
if (world.getBlockEntity(pos) instanceof CableBlockEntity modem) modem.blockTick();
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.shared.peripheral.modem.wired;
|
||||
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -14,7 +13,6 @@ import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
@@ -62,13 +60,6 @@ public class WiredModemFullBlock extends Block implements EntityBlock {
|
||||
}
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public final void onNeighborChange(BlockState state, LevelReader level, BlockPos pos, BlockPos neighbour) {
|
||||
if (state.getValue(PERIPHERAL_ON) && level.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) {
|
||||
modem.neighborChanged(neighbour);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rand) {
|
||||
if (world.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) modem.blockTick();
|
||||
|
@@ -39,7 +39,7 @@ public class WirelessModemBlockEntity extends BlockEntity {
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable IPeripheral other) {
|
||||
return this == other || (other instanceof Peripheral && entity == ((Peripheral) other).entity);
|
||||
return this == other || (other instanceof Peripheral o && entity == o.entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -40,7 +40,6 @@ import org.jspecify.annotations.Nullable;
|
||||
* monitor.setCursorPos(1, 1)
|
||||
* monitor.write("Hello, world!")
|
||||
* }</pre>
|
||||
*
|
||||
* @cc.see monitor_resize Queued when a monitor is resized.
|
||||
* @cc.see monitor_touch Queued when an advanced monitor is clicked.
|
||||
*/
|
||||
@@ -95,7 +94,7 @@ public class MonitorPeripheral extends TermMethods implements IPeripheral {
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable IPeripheral other) {
|
||||
return other instanceof MonitorPeripheral && monitor == ((MonitorPeripheral) other).monitor;
|
||||
return other instanceof MonitorPeripheral o && monitor == o.monitor;
|
||||
}
|
||||
|
||||
private ServerMonitor getMonitor() throws LuaException {
|
||||
|
@@ -58,11 +58,10 @@ public final class MonitorWatcher {
|
||||
if (monitor == null) continue;
|
||||
|
||||
var pos = tile.getBlockPos();
|
||||
var world = tile.getLevel();
|
||||
if (!(world instanceof ServerLevel)) continue;
|
||||
if (!(tile.getLevel() instanceof ServerLevel level)) continue;
|
||||
|
||||
var chunk = world.getChunkAt(pos);
|
||||
if (((ServerLevel) world).getChunkSource().chunkMap.getPlayers(chunk.getPos(), false).isEmpty()) {
|
||||
var chunk = level.getChunkAt(pos);
|
||||
if (level.getChunkSource().chunkMap.getPlayers(chunk.getPos(), false).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -170,8 +170,7 @@ public final class PrinterBlockEntity extends AbstractContainerBlockEntity imple
|
||||
}
|
||||
|
||||
static boolean isPaper(ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
return item == Items.PAPER || item == ModRegistry.Items.PRINTED_PAGE.get();
|
||||
return stack.is(Items.PAPER) || stack.is(ModRegistry.Items.PRINTED_PAGE.get());
|
||||
}
|
||||
|
||||
private boolean canInputPage() {
|
||||
|
@@ -9,6 +9,7 @@ import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -47,13 +48,18 @@ public class SpeakerBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeakerPosition getPosition() {
|
||||
protected ServerLevel getLevel() {
|
||||
return (ServerLevel) speaker.getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpeakerPosition getPosition() {
|
||||
return SpeakerPosition.of(speaker.getLevel(), Vec3.atCenterOf(speaker.getBlockPos()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable IPeripheral other) {
|
||||
return this == other || (other instanceof Peripheral && speaker == ((Peripheral) other).speaker);
|
||||
return this == other || (other instanceof Peripheral o && speaker == o.speaker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,10 @@ import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import org.jspecify.annotations.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;
|
||||
|
||||
@@ -155,7 +158,9 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract SpeakerPosition getPosition();
|
||||
protected abstract ServerLevel getLevel();
|
||||
|
||||
protected abstract SpeakerPosition getPosition();
|
||||
|
||||
public UUID getSource() {
|
||||
return source;
|
||||
@@ -255,8 +260,8 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
// Prevent playing music discs.
|
||||
var soundEvent = BuiltInRegistries.SOUND_EVENT.get(identifier);
|
||||
// TODO: Build a set of sound events at server startup, and cache this.
|
||||
var level = Objects.requireNonNull(getPosition().level());
|
||||
if (soundEvent != null && level.registryAccess().registry(Registries.JUKEBOX_SONG).orElseThrow().stream().anyMatch(x -> x.soundEvent().value() == soundEvent)) {
|
||||
var level = getLevel();
|
||||
if (soundEvent != null && level.registryAccess().registryOrThrow(Registries.JUKEBOX_SONG).stream().anyMatch(x -> x.soundEvent().value() == soundEvent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -20,10 +20,8 @@ public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral {
|
||||
super.detach(computer);
|
||||
|
||||
// We could be in the process of shutting down the server, so we can't send packets in this case.
|
||||
var level = getPosition().level();
|
||||
if (level == null) return;
|
||||
var server = level.getServer();
|
||||
if (server == null || server.isStopped()) return;
|
||||
var server = getLevel().getServer();
|
||||
if (server.isStopped()) return;
|
||||
|
||||
ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server);
|
||||
}
|
||||
|
@@ -41,11 +41,12 @@ public final class PocketBrain implements IPocketAccess {
|
||||
private int colour = -1;
|
||||
private int lightColour = -1;
|
||||
|
||||
public PocketBrain(PocketHolder holder, @Nullable UpgradeData<IPocketUpgrade> upgrade, ServerComputer.Properties properties) {
|
||||
public PocketBrain(PocketHolder holder, @Nullable UpgradeData<IPocketUpgrade> upgrade, int colour, ServerComputer.Properties properties) {
|
||||
this.computer = new PocketServerComputer(this, holder, properties);
|
||||
this.holder = holder;
|
||||
this.position = holder.pos();
|
||||
this.upgrade = upgrade;
|
||||
this.colour = colour;
|
||||
invalidatePeripheral();
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.shared.pocket.core;
|
||||
import dan200.computercraft.api.component.ComputerComponents;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
@@ -40,10 +39,7 @@ public final class PocketServerComputer extends ServerComputer {
|
||||
private Set<ServerPlayer> tracking = Set.of();
|
||||
|
||||
PocketServerComputer(PocketBrain brain, PocketHolder holder, ServerComputer.Properties properties) {
|
||||
super(holder.level(), holder.blockPos(), properties
|
||||
.terminalSize(ConfigSpec.pocketTermWidth.get(), ConfigSpec.pocketTermHeight.get())
|
||||
.addComponent(ComputerComponents.POCKET, brain)
|
||||
);
|
||||
super(holder.level(), holder.blockPos(), properties.addComponent(ComputerComponents.POCKET, brain));
|
||||
this.brain = brain;
|
||||
}
|
||||
|
||||
|
@@ -11,19 +11,16 @@ import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.computer.core.*;
|
||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||
import dan200.computercraft.shared.computer.items.ServerComputerReference;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
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.util.*;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -36,11 +33,10 @@ import net.minecraft.world.entity.item.ItemEntity;
|
||||
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.component.DyedItemColor;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PocketComputerItem extends Item {
|
||||
@@ -184,18 +180,6 @@ public class PocketComputerItem extends Item {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag flag) {
|
||||
if (flag.isAdvanced() || getLabel(stack) == null) {
|
||||
var id = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
|
||||
if (id != null) {
|
||||
list.add(Component.translatable("gui.computercraft.tooltip.computer_id", id.id())
|
||||
.withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ForgeOverride
|
||||
public String getCreatorModId(ItemStack stack) {
|
||||
@@ -217,10 +201,14 @@ public class PocketComputerItem extends Item {
|
||||
|
||||
var computerID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.COMPUTER_ID.get(), IDAssigner.COMPUTER);
|
||||
var brain = new PocketBrain(
|
||||
holder, getUpgradeWithData(stack),
|
||||
holder, getUpgradeWithData(stack), DyedItemColor.getOrDefault(stack, -1),
|
||||
ServerComputer.properties(computerID, getFamily())
|
||||
.label(getLabel(stack))
|
||||
.storageCapacity(StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), -1))
|
||||
.terminalSize(stack.getOrDefault(
|
||||
ModRegistry.DataComponents.TERMINAL_SIZE.get(),
|
||||
new TerminalSize(ConfigSpec.pocketTermWidth.get(), ConfigSpec.pocketTermHeight.get())
|
||||
))
|
||||
);
|
||||
var computer = brain.computer();
|
||||
|
||||
@@ -256,10 +244,14 @@ public class PocketComputerItem extends Item {
|
||||
// item. However, if we've just crafted the computer with an upgrade, we should sync the other way, and update
|
||||
// the computer.
|
||||
var server = level.getServer();
|
||||
if (server != null) {
|
||||
var computer = getServerComputer(server, stack);
|
||||
if (computer != null) computer.getBrain().setUpgrade(getUpgradeWithData(stack));
|
||||
}
|
||||
if (server == null) return;
|
||||
|
||||
var computer = getServerComputer(server, stack);
|
||||
if (computer == null) return;
|
||||
|
||||
var brain = computer.getBrain();
|
||||
brain.setUpgrade(getUpgradeWithData(stack));
|
||||
brain.setColour(DyedItemColor.getOrDefault(stack, -1));
|
||||
}
|
||||
|
||||
public ComputerFamily getFamily() {
|
||||
|
@@ -26,8 +26,7 @@ public class PocketSpeaker extends AbstractPocketUpgrade {
|
||||
|
||||
@Override
|
||||
public void update(IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||
if (!(peripheral instanceof PocketSpeakerPeripheral)) return;
|
||||
((PocketSpeakerPeripheral) peripheral).update();
|
||||
if (peripheral instanceof PocketSpeakerPeripheral speaker) speaker.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -8,17 +8,23 @@ 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.server.level.ServerLevel;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||
final class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||
private final IPocketAccess access;
|
||||
|
||||
public PocketSpeakerPeripheral(IPocketAccess access) {
|
||||
PocketSpeakerPeripheral(IPocketAccess access) {
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeakerPosition getPosition() {
|
||||
protected ServerLevel getLevel() {
|
||||
return access.getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpeakerPosition getPosition() {
|
||||
var entity = access.getEntity();
|
||||
return entity == null ? SpeakerPosition.of(access.getLevel(), access.getPosition()) : SpeakerPosition.of(entity);
|
||||
}
|
||||
|
@@ -17,8 +17,8 @@ import net.minecraft.world.item.crafting.CraftingInput;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@@ -13,7 +13,9 @@ import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.AbstractInventoryMethods;
|
||||
import dan200.computercraft.shared.turtle.core.*;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -659,6 +661,7 @@ public class TurtleAPI implements ILuaAPI {
|
||||
* @cc.treturn [2] string The reason equipping this item failed.
|
||||
* @cc.since 1.6
|
||||
* @see #equipRight()
|
||||
* @see #getEquippedLeft()
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult equipLeft() {
|
||||
@@ -678,12 +681,45 @@ public class TurtleAPI implements ILuaAPI {
|
||||
* @cc.treturn [2] string The reason equipping this item failed.
|
||||
* @cc.since 1.6
|
||||
* @see #equipLeft()
|
||||
* @see #getEquippedRight()
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult equipRight() {
|
||||
return trackCommand(new TurtleEquipCommand(TurtleSide.RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upgrade currently equipped on the left of the turtle.
|
||||
* <p>
|
||||
* This returns information about the currently equipped item, in the same form as
|
||||
* {@link #getItemDetail(ILuaContext, Optional, Optional)}.
|
||||
*
|
||||
* @return Information about the currently equipped item, or {@code nil} if no upgrade is equipped.
|
||||
* @see #equipLeft()
|
||||
* @cc.since 1.116.0
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public final @Nullable Map<?, ?> getEquippedLeft() {
|
||||
var upgrade = turtle.getUpgradeWithData(TurtleSide.LEFT);
|
||||
return upgrade == null ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(upgrade.getUpgradeItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upgrade currently equipped on the right of the turtle.
|
||||
* <p>
|
||||
* This returns information about the currently equipped item, in the same form as
|
||||
* {@link #getItemDetail(ILuaContext, Optional, Optional)}.
|
||||
*
|
||||
* @return Information about the currently equipped item, or {@code nil} if no upgrade is equipped.
|
||||
* @see #equipRight()
|
||||
* @cc.since 1.116.0
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public final @Nullable Map<?, ?> getEquippedRight() {
|
||||
var upgrade = turtle.getUpgradeWithData(TurtleSide.RIGHT);
|
||||
return upgrade == null ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(upgrade.getUpgradeItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the block in front of the turtle.
|
||||
*
|
||||
@@ -745,7 +781,7 @@ public class TurtleAPI implements ILuaAPI {
|
||||
* more information about the item at the cost of taking longer to run.
|
||||
* @return The command result.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @cc.treturn nil|table Information about the given slot, or {@code nil} if it is empty.
|
||||
* @cc.treturn nil|table Information about the item in this slot, or {@code nil} if it is empty.
|
||||
* @cc.since 1.64
|
||||
* @cc.changed 1.90.0 Added detailed parameter.
|
||||
* @cc.usage Print the current slot, assuming it contains 13 dirt.
|
||||
|
@@ -18,6 +18,7 @@ import dan200.computercraft.shared.computer.blocks.ComputerPeripheral;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.TerminalSize;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.container.BasicContainer;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
@@ -81,7 +82,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
protected ServerComputer createComputer(int id) {
|
||||
var computer = new ServerComputer((ServerLevel) getLevel(), getBlockPos(), ServerComputer.properties(id, getFamily())
|
||||
.label(getLabel())
|
||||
.terminalSize(Config.TURTLE_TERM_WIDTH, Config.TURTLE_TERM_HEIGHT)
|
||||
.terminalSize(new TerminalSize(Config.TURTLE_TERM_WIDTH, Config.TURTLE_TERM_HEIGHT))
|
||||
.storageCapacity(storageCapacity)
|
||||
.addComponent(ComputerComponents.TURTLE, brain)
|
||||
);
|
||||
@@ -169,8 +170,8 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectImplicitComponents(DataComponentMap.Builder builder) {
|
||||
super.collectImplicitComponents(builder);
|
||||
protected void collectSafeComponents(DataComponentMap.Builder builder) {
|
||||
super.collectSafeComponents(builder);
|
||||
|
||||
builder.set(DataComponents.DYED_COLOR, brain.getColour() == -1 ? null : new DyedItemColor(brain.getColour(), false));
|
||||
builder.set(ModRegistry.DataComponents.OVERLAY.get(), brain.getOverlay());
|
||||
|
@@ -62,14 +62,13 @@ public class TurtleDropCommand implements TurtleCommand {
|
||||
}
|
||||
}
|
||||
|
||||
switch (transferred) {
|
||||
case ContainerTransfer.NO_SPACE:
|
||||
return TurtleCommandResult.failure("No space for items");
|
||||
case ContainerTransfer.NO_ITEMS:
|
||||
return TurtleCommandResult.failure("No items to drop");
|
||||
default:
|
||||
return switch (transferred) {
|
||||
case ContainerTransfer.NO_SPACE -> TurtleCommandResult.failure("No space for items");
|
||||
case ContainerTransfer.NO_ITEMS -> TurtleCommandResult.failure("No items to drop");
|
||||
default -> {
|
||||
turtle.playAnimation(TurtleAnimation.WAIT);
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
yield TurtleCommandResult.success();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -50,15 +50,14 @@ public class TurtleSuckCommand implements TurtleCommand {
|
||||
if (inventory != null) {
|
||||
// Take from inventory of thing in front
|
||||
var transferred = inventory.moveTo(TurtleUtil.getOffsetInventory(turtle), quantity);
|
||||
switch (transferred) {
|
||||
case ContainerTransfer.NO_SPACE:
|
||||
return TurtleCommandResult.failure("No space for items");
|
||||
case ContainerTransfer.NO_ITEMS:
|
||||
return TurtleCommandResult.failure("No items to take");
|
||||
default:
|
||||
return switch (transferred) {
|
||||
case ContainerTransfer.NO_SPACE -> TurtleCommandResult.failure("No space for items");
|
||||
case ContainerTransfer.NO_ITEMS -> TurtleCommandResult.failure("No items to take");
|
||||
default -> {
|
||||
turtle.playAnimation(TurtleAnimation.WAIT);
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
yield TurtleCommandResult.success();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// Suck up loose items off the ground
|
||||
var aabb = new AABB(
|
||||
|
@@ -11,18 +11,18 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||
import net.minecraft.core.cauldron.CauldronInteraction;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.ItemInteractionResult;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.LayeredCauldronBlock;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class TurtleItem extends ComputerItem {
|
||||
public class TurtleItem extends BlockItem {
|
||||
public TurtleItem(TurtleBlock block, Properties settings) {
|
||||
super(block, settings);
|
||||
}
|
||||
|
@@ -109,7 +109,9 @@ public final class TurtleUpgradeRecipe extends CustomRecipe {
|
||||
TurtleItem.getUpgradeWithData(turtle, TurtleSide.RIGHT),
|
||||
};
|
||||
|
||||
// Get the upgrades for the new items
|
||||
// Get the upgrades for the new items.
|
||||
// Note: because the turtle is facing towards us, the directions are flipped. Items placed to the left
|
||||
// of the turtle, are equipped on its right (and vice versa).
|
||||
var items = new ItemStack[]{ rightItem, leftItem };
|
||||
for (var i = 0; i < 2; i++) {
|
||||
if (!items[i].isEmpty()) {
|
||||
|
@@ -13,6 +13,7 @@ import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
@@ -26,7 +27,12 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeakerPosition getPosition() {
|
||||
protected ServerLevel getLevel() {
|
||||
return (ServerLevel) turtle.getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpeakerPosition getPosition() {
|
||||
return SpeakerPosition.of(turtle.getLevel(), Vec3.atCenterOf(turtle.getPosition()));
|
||||
}
|
||||
|
||||
|
@@ -256,8 +256,8 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
EnchantmentHelper.doPostAttackEffects(player.serverLevel(), entity, source);
|
||||
|
||||
// Damage the original item stack.
|
||||
if (!tool.isEmpty() && entity instanceof LivingEntity && didHurt) {
|
||||
tool.postHurtEnemy((LivingEntity) entity, player);
|
||||
if (!tool.isEmpty() && entity instanceof LivingEntity living && didHurt) {
|
||||
tool.postHurtEnemy(living, player);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -70,9 +70,9 @@ public final class DropConsumer {
|
||||
|
||||
public static boolean onEntitySpawn(Entity entity) {
|
||||
// Capture any nearby item spawns
|
||||
if (dropWorld == entity.level() && entity instanceof ItemEntity
|
||||
if (dropWorld == entity.level() && entity instanceof ItemEntity item
|
||||
&& assertNonNull(dropBounds).contains(entity.position())) {
|
||||
handleDrops(((ItemEntity) entity).getItem());
|
||||
handleDrops(item.getItem());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,9 @@ package dan200.computercraft.shared.util;
|
||||
import com.mojang.serialization.Codec;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -15,6 +17,8 @@ import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A non-negative integer id, used for computer and disk ids.
|
||||
*
|
||||
@@ -47,4 +51,8 @@ public record NonNegativeId(int id) {
|
||||
stack.set(component, new NonNegativeId(diskID));
|
||||
return diskID;
|
||||
}
|
||||
|
||||
public void addToTooltip(String translation, Consumer<Component> out) {
|
||||
out.accept(Component.translatable(translation, id()).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
}
|
||||
|
@@ -79,8 +79,8 @@ public class PrettyJsonWriter extends JsonWriter {
|
||||
|
||||
// Otherwise we either need to push to our list or finish a record pair.
|
||||
var head = stack.getLast();
|
||||
if (head instanceof DocList) {
|
||||
((DocList) head).add(object);
|
||||
if (head instanceof DocList headList) {
|
||||
headList.add(object);
|
||||
} else {
|
||||
stack.removeLast();
|
||||
((DocList) stack.getLast()).add(new Pair((String) head, object));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user