1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-17 23:17:38 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Jonathan Coates
d0d4df367f Remove QuadEmitter
And remove EMI support. I'm not sure it survived the 1.21.4 recipe
changes :(.
2025-10-14 22:10:00 +01:00
Jonathan Coates
91b3b60ca2 Finish lectern rendering
- Register our models via Minecraft's layer system. I confess, I don't
   fully understand what purpose this really solves (it's not
   data-driven or anything!), but makes it a bit more consistent with
   the rest of vanilla's code.

 - Update the lectern code to submit models correctly.
2025-10-14 20:04:31 +01:00
Jonathan Coates
64b1eb5fae Get everything compiling
Game tests are running but not passing. Well, on Fabric, there's some
fun classpath issues on NF. Classic.

 - Update NF to use new transfer code. Generic peripherals don't work
   yet, as the capabilities aren't wired up correctly. Suspect we're
   gonna have to tear out that code.
2025-10-14 08:51:35 +01:00
Jonathan Coates
ef1923dd3e Initial update to 1.21.10 2025-10-13 21:15:54 +01:00
Jonathan Coates
4cccf1817c Update to Minecraft 1.21.8 2025-07-18 17:04:28 +01:00
Jonathan Coates
b8d9499027 Fix terminals not receiving scroll events
Minecraft 1.21.2 added a mouseScroll override to
AbstractContainerScreen, which means that child widgets no longer
receive scroll events. We reimplement that logic in our computer screen.

Fixes #2245.
2025-07-17 09:11:26 +01:00
Jonathan Coates
e81a2c72ce Merge branch 'mc-1.21.x' into mc-1.21.y 2025-07-12 21:46:28 +01:00
Jonathan Coates
01de6110c6 Re-enable JEI integration 2025-07-12 21:41:50 +01:00
Jonathan Coates
9cf0f85fcb Merge branch 'mc-1.20.x' into mc-1.21.x 2025-07-12 19:00:00 +01:00
Jonathan Coates
018ce7c8a5 Bump CC:T to 1.116.1 2025-07-12 18:57:05 +01:00
Jonathan Coates
44726827b4 Sync translations from Crowdin 2025-07-12 09:49:14 +01:00
Jonathan Coates
2bf0aba455 Don't use SpeakerPosition to get speaker's level
We use getLevel() specifically for reading the current registries
(and/or server). We don't need the exact position of the speaker to
query this, so add a dedicated method for it.

We actually had a similar method on 1.21.7 already for upgrades. This
just moves it to SpeakerPeripheral.

Fixes #2236.
2025-07-10 01:07:51 +01:00
Jonathan Coates
3cf914cb4c Fix NPE when loading mcfunctions
CommandSourceStack.getServer can be null, despite being marked as
non-nullable. Mojang!!! *shakes fist*

Fixes #2235.
2025-07-10 00:47:42 +01:00
Jonathan Coates
4868c4aa32 Small bits of cleanup
- Remove unused MonitorRenderer. I'm sure this had been deleted
   already, but apparently not!
 - Add missing items to the changelog.
 - Fix crash when clearing tests in a world.
 - Bump Iris deps, to help with debugging #2229.
2025-07-10 00:29:06 +01:00
Batári Balázs László
180156ff1c Add lang hu_hu (#2232) 2025-07-06 21:47:22 +00:00
Jonathan Coates
c6ba753568 Update allowed Minecraft versions 2025-07-06 22:34:43 +01:00
Jonathan Coates
9f45c91925 Update to latest TeaVM 2025-07-02 09:21:43 +01:00
Jonathan Coates
5fa724ed24 Move Create integration setup to its proper place
Merge conflict gone wrong, I assume.
2025-06-30 22:53:15 +01:00
117 changed files with 1738 additions and 1475 deletions

View File

@@ -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

View File

@@ -25,6 +25,7 @@ repositories {
name = "Fabric"
content {
includeGroup("net.fabricmc")
includeGroup("net.fabricmc.unpick")
}
}

View File

@@ -68,5 +68,5 @@ tasks.ideaSyncTask {
tasks.named("checkDependencyConsistency", DependencyCheck::class.java) {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
// Minecraft depends on asm, but Fabric forces it to a more recent version
override(libs.findLibrary("asm").get(), "9.8")
override(libs.findLibrary("asm").get(), "9.9")
}

View File

@@ -50,7 +50,6 @@ repositories {
includeGroup("com.simibubi.create")
includeGroup("net.commoble.morered")
includeGroup("dev.architectury")
includeGroup("dev.emi")
includeGroup("maven.modrinth")
includeGroup("me.shedaniel.cloth")
includeGroup("me.shedaniel")

View File

@@ -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

View File

@@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
# Mod properties
isUnstable=true
modVersion=1.116.0
modVersion=1.116.1
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.21.7
mcVersion=1.21.10

View File

@@ -7,14 +7,14 @@
# Minecraft
# MC version is specified in gradle.properties, as we need that in settings.gradle.
# Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml
fabric-api = "0.128.0+1.21.7"
fabric-loader = "0.16.14"
neoForge = "21.7.1-beta"
fabric-api = "0.135.0+1.21.10"
fabric-loader = "0.17.3"
neoForge = "21.10.6-beta"
neoMergeTool = "2.0.0"
mixin = "0.8.5"
parchment = "2025.06.29"
parchmentMc = "1.21.6"
yarn = "1.21.7+build.1"
parchment = "2025.10.12"
parchmentMc = "1.21.10"
yarn = "1.21.10+build.2"
# Core dependencies (these versions are tied to the version Minecraft uses)
fastutil = "8.5.15"
@@ -36,16 +36,15 @@ kotlin-coroutines = "1.10.1"
nightConfig = "3.8.1"
# Minecraft mods
emi = "1.1.7+1.21"
fabricPermissions = "0.3.3"
iris-fabric = "1.8.11+1.21.5-fabric"
iris-forge = "1.8.11+1.21.5-neoforge"
jei = "19.8.2.99"
modmenu = "13.0.2"
iris-fabric = "1.9.1+1.21.7-fabric"
iris-forge = "1.9.1+1.21.7-neoforge"
jei = "26.0.0.1"
modmenu = "16.0.0-rc.1"
moreRed = "6.0.0.3"
rei = "18.0.800"
sodium-fabric = "mc1.21.5-0.6.12-fabric"
sodium-forge = "mc1.21.5-0.6.12-neoforge"
sodium-fabric = "mc1.21.6-0.6.13-fabric"
sodium-forge = "mc1.21.6-0.6.13-neoforge"
mixinExtra = "0.3.5"
create-forge = "6.0.0-6"
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
@@ -62,20 +61,20 @@ cctJavadoc = "1.8.5"
checkstyle = "10.23.1"
errorProne-core = "2.38.0"
errorProne-plugin = "4.1.0"
fabric-loom = "1.10.4"
fabric-loom = "1.11.8"
githubRelease = "2.5.2"
gradleVersions = "0.50.0"
ideaExt = "1.1.7"
illuaminate = "0.1.0-83-g1131f68"
lwjgl = "3.3.3"
minotaur = "2.8.7"
modDevGradle = "2.0.95"
modDevGradle = "2.0.113"
nullAway = "0.12.7"
shadow = "8.3.1"
spotless = "7.0.2"
taskTree = "2.1.1"
teavm = "0.11.0-SQUID.1"
vanillaExtract = "0.2.1"
teavm = "0.13.0-SQUID.1"
vanillaExtract = "0.3.0"
versionCatalogUpdate = "0.8.1"
[libraries]
@@ -106,16 +105,15 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
# Minecraft mods
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
create-forge = { module = "com.simibubi.create:create-1.21.1", version.ref = "create-forge" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
iris-fabric = { module = "maven.modrinth:iris", version.ref = "iris-fabric" }
iris-forge = { module = "maven.modrinth:iris", version.ref = "iris-forge" }
jei-api = { module = "mezz.jei:jei-1.21-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-1.21-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-1.21-neoforge", version.ref = "jei" }
jei-api = { module = "mezz.jei:jei-1.21.10-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-1.21.10-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-1.21.10-neoforge", version.ref = "jei" }
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
@@ -186,7 +184,7 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
# Minecraft
externalMods-common = ["iris-forge", "jei-api", "nightConfig-core", "nightConfig-toml"]
externalMods-forge-compile = ["moreRed", "iris-forge", "jei-api"]
externalMods-forge-runtime = []
externalMods-forge-runtime = ["jei-forge"]
externalMods-fabric-compile = ["fabricPermissions", "iris-fabric", "jei-api", "rei-api", "rei-builtin"]
externalMods-fabric-runtime = []

Binary file not shown.

View File

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

6
gradlew vendored
View File

@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -205,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

275
package-lock.json generated
View File

@@ -16,7 +16,7 @@
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-typescript": "^12.0.0 && <12.1.3",
"@rollup/plugin-typescript": "^12.0.0",
"@rollup/plugin-url": "^8.0.1",
"@swc/core": "^1.3.92",
"@types/node": "^24.0.0",
@@ -481,9 +481,9 @@
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "12.1.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.2.tgz",
"integrity": "sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg==",
"version": "12.1.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.4.tgz",
"integrity": "sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -554,9 +554,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz",
"integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz",
"integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==",
"cpu": [
"arm"
],
@@ -568,9 +568,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz",
"integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz",
"integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==",
"cpu": [
"arm64"
],
@@ -582,9 +582,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz",
"integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz",
"integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==",
"cpu": [
"arm64"
],
@@ -596,9 +596,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz",
"integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz",
"integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==",
"cpu": [
"x64"
],
@@ -610,9 +610,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz",
"integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz",
"integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==",
"cpu": [
"arm64"
],
@@ -624,9 +624,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz",
"integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz",
"integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==",
"cpu": [
"x64"
],
@@ -638,9 +638,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz",
"integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz",
"integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==",
"cpu": [
"arm"
],
@@ -652,9 +652,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz",
"integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz",
"integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==",
"cpu": [
"arm"
],
@@ -666,9 +666,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz",
"integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz",
"integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==",
"cpu": [
"arm64"
],
@@ -680,9 +680,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz",
"integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz",
"integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==",
"cpu": [
"arm64"
],
@@ -694,9 +694,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz",
"integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz",
"integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==",
"cpu": [
"loong64"
],
@@ -708,9 +708,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz",
"integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz",
"integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==",
"cpu": [
"ppc64"
],
@@ -722,9 +722,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz",
"integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz",
"integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==",
"cpu": [
"riscv64"
],
@@ -736,9 +736,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz",
"integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz",
"integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==",
"cpu": [
"riscv64"
],
@@ -750,9 +750,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz",
"integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz",
"integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==",
"cpu": [
"s390x"
],
@@ -764,9 +764,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz",
"integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz",
"integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==",
"cpu": [
"x64"
],
@@ -778,9 +778,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz",
"integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz",
"integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==",
"cpu": [
"x64"
],
@@ -792,9 +792,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz",
"integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz",
"integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==",
"cpu": [
"arm64"
],
@@ -806,9 +806,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz",
"integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz",
"integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==",
"cpu": [
"ia32"
],
@@ -820,9 +820,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz",
"integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz",
"integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==",
"cpu": [
"x64"
],
@@ -844,9 +844,9 @@
}
},
"node_modules/@swc/core": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.1.tgz",
"integrity": "sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.9.tgz",
"integrity": "sha512-O+LfT2JlVMsIMWG9x+rdxg8GzpzeGtCZQfXV7cKc1PjIKUkLFf1QJ7okuseA4f/9vncu37dQ2ZcRrPKy0Ndd5g==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
@@ -862,16 +862,16 @@
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.12.1",
"@swc/core-darwin-x64": "1.12.1",
"@swc/core-linux-arm-gnueabihf": "1.12.1",
"@swc/core-linux-arm64-gnu": "1.12.1",
"@swc/core-linux-arm64-musl": "1.12.1",
"@swc/core-linux-x64-gnu": "1.12.1",
"@swc/core-linux-x64-musl": "1.12.1",
"@swc/core-win32-arm64-msvc": "1.12.1",
"@swc/core-win32-ia32-msvc": "1.12.1",
"@swc/core-win32-x64-msvc": "1.12.1"
"@swc/core-darwin-arm64": "1.12.9",
"@swc/core-darwin-x64": "1.12.9",
"@swc/core-linux-arm-gnueabihf": "1.12.9",
"@swc/core-linux-arm64-gnu": "1.12.9",
"@swc/core-linux-arm64-musl": "1.12.9",
"@swc/core-linux-x64-gnu": "1.12.9",
"@swc/core-linux-x64-musl": "1.12.9",
"@swc/core-win32-arm64-msvc": "1.12.9",
"@swc/core-win32-ia32-msvc": "1.12.9",
"@swc/core-win32-x64-msvc": "1.12.9"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
@@ -883,9 +883,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.1.tgz",
"integrity": "sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.9.tgz",
"integrity": "sha512-GACFEp4nD6V+TZNR2JwbMZRHB+Yyvp14FrcmB6UCUYmhuNWjkxi+CLnEvdbuiKyQYv0zA+TRpCHZ+whEs6gwfA==",
"cpu": [
"arm64"
],
@@ -900,9 +900,9 @@
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.1.tgz",
"integrity": "sha512-OGm4a4d3OeJn+tRt8H/eiHgTFrJbS6r8mi/Ob65tAEXZGHN900T2kR7c5ALr0V2hBOQ8BfhexwPoQlGQP/B95w==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.9.tgz",
"integrity": "sha512-hv2kls7Ilkm2EpeJz+I9MCil7pGS3z55ZAgZfxklEuYsxpICycxeH+RNRv4EraggN44ms+FWCjtZFu0LGg2V3g==",
"cpu": [
"x64"
],
@@ -917,9 +917,9 @@
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.1.tgz",
"integrity": "sha512-76YeeQKyK0EtNkQiNBZ0nbVGooPf9IucY0WqVXVpaU4wuG7ZyLEE2ZAIgXafIuzODGQoLfetue7I8boMxh1/MA==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.9.tgz",
"integrity": "sha512-od9tDPiG+wMU9wKtd6y3nYJdNqgDOyLdgRRcrj1/hrbHoUPOM8wZQZdwQYGarw63iLXGgsw7t5HAF9Yc51ilFA==",
"cpu": [
"arm"
],
@@ -934,9 +934,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.1.tgz",
"integrity": "sha512-BxJDIJPq1+aCh9UsaSAN6wo3tuln8UhNXruOrzTI8/ElIig/3sAueDM6Eq7GvZSGGSA7ljhNATMJ0elD7lFatQ==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.9.tgz",
"integrity": "sha512-6qx1ka9LHcLzxIgn2Mros+CZLkHK2TawlXzi/h7DJeNnzi8F1Hw0Yzjp8WimxNCg6s2n+o3jnmin1oXB7gg8rw==",
"cpu": [
"arm64"
],
@@ -951,9 +951,9 @@
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.1.tgz",
"integrity": "sha512-NhLdbffSXvY0/FwUSAl4hKBlpe5GHQGXK8DxTo3HHjLsD9sCPYieo3vG0NQoUYAy4ZUY1WeGjyxeq4qZddJzEQ==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.9.tgz",
"integrity": "sha512-yghFZWKPVVGbUdqiD7ft23G0JX6YFGDJPz9YbLLAwGuKZ9th3/jlWoQDAw1Naci31LQhVC+oIji6ozihSuwB2A==",
"cpu": [
"arm64"
],
@@ -968,9 +968,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.1.tgz",
"integrity": "sha512-CrYnV8SZIgArQ9LKH0xEF95PKXzX9WkRSc5j55arOSBeDCeDUQk1Bg/iKdnDiuj5HC1hZpvzwMzSBJjv+Z70jA==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.9.tgz",
"integrity": "sha512-SFUxyhWLZRNL8QmgGNqdi2Q43PNyFVkRZ2zIif30SOGFSxnxcf2JNeSeBgKIGVgaLSuk6xFVVCtJ3KIeaStgRg==",
"cpu": [
"x64"
],
@@ -985,9 +985,9 @@
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.1.tgz",
"integrity": "sha512-BQMl3d0HaGB0/h2xcKlGtjk/cGRn2tnbsaChAKcjFdCepblKBCz1pgO/mL7w5iXq3s57wMDUn++71/a5RAkZOA==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.9.tgz",
"integrity": "sha512-9FB0wM+6idCGTI20YsBNBg9xSWtkDBymnpaTCsZM3qDc0l4uOpJMqbfWhQvp17x7r/ulZfb2QY8RDvQmCL6AcQ==",
"cpu": [
"x64"
],
@@ -1002,9 +1002,9 @@
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.1.tgz",
"integrity": "sha512-b7NeGnpqTfmIGtUqXBl0KqoSmOnH64nRZoT5l4BAGdvwY7nxitWR94CqZuwyLPty/bLywmyDA9uO12Kvgb3+gg==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.9.tgz",
"integrity": "sha512-zHOusMVbOH9ik5RtRrMiGzLpKwxrPXgXkBm3SbUCa65HAdjV33NZ0/R9Rv1uPESALtEl2tzMYLUxYA5ECFDFhA==",
"cpu": [
"arm64"
],
@@ -1019,9 +1019,9 @@
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.1.tgz",
"integrity": "sha512-iU/29X2D7cHBp1to62cUg/5Xk8K+lyOJiKIGGW5rdzTW/c2zz3d/ehgpzVP/rqC4NVr88MXspqHU4il5gmDajw==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.9.tgz",
"integrity": "sha512-aWZf0PqE0ot7tCuhAjRkDFf41AzzSQO0x2xRfTbnhpROp57BRJ/N5eee1VULO/UA2PIJRG7GKQky5bSGBYlFug==",
"cpu": [
"ia32"
],
@@ -1036,9 +1036,9 @@
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.1.tgz",
"integrity": "sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw==",
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.9.tgz",
"integrity": "sha512-C25fYftXOras3P3anSUeXXIpxmEkdAcsIL9yrr0j1xepTZ/yKwpnQ6g3coj8UXdeJy4GTVlR6+Ow/QiBgZQNOg==",
"cpu": [
"x64"
],
@@ -1124,9 +1124,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.0.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz",
"integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==",
"version": "24.0.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.8.tgz",
"integrity": "sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2856,13 +2856,13 @@
}
},
"node_modules/rollup": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz",
"integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==",
"version": "4.44.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz",
"integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.7"
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -2872,36 +2872,29 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.43.0",
"@rollup/rollup-android-arm64": "4.43.0",
"@rollup/rollup-darwin-arm64": "4.43.0",
"@rollup/rollup-darwin-x64": "4.43.0",
"@rollup/rollup-freebsd-arm64": "4.43.0",
"@rollup/rollup-freebsd-x64": "4.43.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.43.0",
"@rollup/rollup-linux-arm-musleabihf": "4.43.0",
"@rollup/rollup-linux-arm64-gnu": "4.43.0",
"@rollup/rollup-linux-arm64-musl": "4.43.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.43.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.43.0",
"@rollup/rollup-linux-riscv64-gnu": "4.43.0",
"@rollup/rollup-linux-riscv64-musl": "4.43.0",
"@rollup/rollup-linux-s390x-gnu": "4.43.0",
"@rollup/rollup-linux-x64-gnu": "4.43.0",
"@rollup/rollup-linux-x64-musl": "4.43.0",
"@rollup/rollup-win32-arm64-msvc": "4.43.0",
"@rollup/rollup-win32-ia32-msvc": "4.43.0",
"@rollup/rollup-win32-x64-msvc": "4.43.0",
"@rollup/rollup-android-arm-eabi": "4.44.1",
"@rollup/rollup-android-arm64": "4.44.1",
"@rollup/rollup-darwin-arm64": "4.44.1",
"@rollup/rollup-darwin-x64": "4.44.1",
"@rollup/rollup-freebsd-arm64": "4.44.1",
"@rollup/rollup-freebsd-x64": "4.44.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.44.1",
"@rollup/rollup-linux-arm-musleabihf": "4.44.1",
"@rollup/rollup-linux-arm64-gnu": "4.44.1",
"@rollup/rollup-linux-arm64-musl": "4.44.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.44.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.1",
"@rollup/rollup-linux-riscv64-gnu": "4.44.1",
"@rollup/rollup-linux-riscv64-musl": "4.44.1",
"@rollup/rollup-linux-s390x-gnu": "4.44.1",
"@rollup/rollup-linux-x64-gnu": "4.44.1",
"@rollup/rollup-linux-x64-musl": "4.44.1",
"@rollup/rollup-win32-arm64-msvc": "4.44.1",
"@rollup/rollup-win32-ia32-msvc": "4.44.1",
"@rollup/rollup-win32-x64-msvc": "4.44.1",
"fsevents": "~2.3.2"
}
},
"node_modules/rollup/node_modules/@types/estree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",

View File

@@ -13,7 +13,7 @@
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-typescript": "^12.0.0 && <12.1.3",
"@rollup/plugin-typescript": "^12.0.0",
"@rollup/plugin-url": "^8.0.1",
"@swc/core": "^1.3.92",
"@types/node": "^24.0.0",

View File

@@ -6,12 +6,15 @@ package dan200.computercraft.api.client;
import com.google.common.base.Suppliers;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.SheetedDecalTextureGenerator;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.feature.ModelFeatureRenderer;
import net.minecraft.client.renderer.item.BlockModelWrapper;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.ItemModelResolver;
@@ -19,10 +22,11 @@ import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BlockModelRotation;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ARGB;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ItemOwner;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.joml.Vector3f;
@@ -113,7 +117,7 @@ public final class StandaloneModel {
* Set up an {@link ItemStackRenderState.LayerRenderState} to render this model.
*
* @param layer The layer to set up.
* @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, LivingEntity, int)
* @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, ItemOwner, int)
*/
public void setupItemLayer(ItemStackRenderState.LayerRenderState layer) {
layer.setExtents(extents);
@@ -127,26 +131,37 @@ public final class StandaloneModel {
* Render the model directly.
*
* @param transform The current pose stack transformations.
* @param buffers The buffer source to use for rendering.
* @param collector The node collector to render to.
* @param light The current light texture coordinate.
* @param overlay The current overlay texture coordinate.
*/
public void render(PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
render(transform, buffers, light, overlay, null);
public void submit(PoseStack transform, SubmitNodeCollector collector, int light, int overlay) {
submit(transform, collector, light, overlay, null, null);
}
/**
* Render the model directly.
*
* @param transform The current pose stack transformations.
* @param buffers The buffer source to use for rendering.
* @param light The current light texture coordinate.
* @param overlay The current overlay texture coordinate.
* @param tints The tints for this model.
* @param transform The current pose stack transformations.
* @param collector The node collector to render to.
* @param light The current light texture coordinate.
* @param overlay The current overlay texture coordinate.
* @param tints The tints for this model.
* @param crumblingOverlay The current breaking progress.
*/
public void render(PoseStack transform, MultiBufferSource buffers, int light, int overlay, int @Nullable [] tints) {
var pose = transform.last();
var buffer = buffers.getBuffer(renderType);
public void submit(PoseStack transform, SubmitNodeCollector collector, int light, int overlay, int @Nullable [] tints, ModelFeatureRenderer.@Nullable CrumblingOverlay crumblingOverlay) {
collector.submitCustomGeometry(transform, renderType, (pose, buffer) -> render(pose, buffer, tints, light, overlay));
if (crumblingOverlay != null && renderType.affectsCrumbling()) {
// FIXME: We need a custom hook here, which renders to crumblingBufferSource. Currently the DESTROY_TYPES
// buffer gets flushed before the main model gets rendered.
collector.submitCustomGeometry(transform, ModelBakery.DESTROY_TYPES.get(crumblingOverlay.progress()), (pose, buffer) ->
render(pose, new SheetedDecalTextureGenerator(buffer, crumblingOverlay.cameraPose(), 1.0f), null, light, overlay)
);
}
}
private void render(PoseStack.Pose pose, VertexConsumer buffer, int @Nullable [] tints, int light, int overlay) {
for (var quad : quads) {
float r, g, b, a;
var idx = quad.tintIndex();

View File

@@ -4,16 +4,13 @@
package dan200.computercraft.api.client.turtle;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.StandaloneModel;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeData;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.item.BlockModelWrapper;
import net.minecraft.client.renderer.item.ItemModelResolver;
@@ -70,11 +67,6 @@ public final class BasicUpgradeModel implements TurtleUpgradeModel {
getModel(side).setupItemLayer(layer);
}
@Override
public void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
getModel(side).render(transform, buffers, light, overlay);
}
private record Unbaked(ResourceLocation left, ResourceLocation right) implements TurtleUpgradeModel.Unbaked {
@Override
public MapCodec<? extends TurtleUpgradeModel.Unbaked> type() {

View File

@@ -9,12 +9,10 @@ import com.mojang.math.Axis;
import com.mojang.math.Transformation;
import com.mojang.serialization.MapCodec;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeData;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
@@ -23,7 +21,6 @@ import net.minecraft.client.renderer.special.SpecialModelRenderer;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f;
@@ -77,15 +74,6 @@ public final class ItemUpgradeModel implements TurtleUpgradeModel {
}
}
@Override
public void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
transform.mulPose(getRenderer(side).transform().getMatrix());
transform.mulPose(Axis.YP.rotation(Mth.PI));
Minecraft.getInstance().getItemRenderer().renderStatic(
upgrade.getUpgradeItem(), ItemDisplayContext.FIXED, light, overlay, transform, buffers, turtle.getLevel(), 0
);
}
private static final class Unbaked implements TurtleUpgradeModel.Unbaked {
@Override
public MapCodec<? extends TurtleUpgradeModel.Unbaked> type() {
@@ -120,14 +108,14 @@ public final class ItemUpgradeModel implements TurtleUpgradeModel {
private record TransformedRenderer(Transformation transform) implements SpecialModelRenderer<ItemStackRenderState> {
@Override
public void render(
@Nullable ItemStackRenderState state, ItemDisplayContext itemDisplayContext, PoseStack poseStack,
MultiBufferSource multiBufferSource, int overlay, int light, boolean bl
public void submit(
@Nullable ItemStackRenderState state, ItemDisplayContext context, PoseStack poseStack, SubmitNodeCollector sink,
int light, int overlay, boolean foil, int outlineColour
) {
if (state == null) return;
poseStack.pushPose();
poseStack.mulPose(transform.getMatrix());
state.render(poseStack, multiBufferSource, overlay, light);
state.submit(poseStack, sink, light, overlay, outlineColour);
poseStack.popPose();
}

View File

@@ -6,20 +6,17 @@ package dan200.computercraft.api.client.turtle;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeData;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.Util;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
@@ -71,11 +68,6 @@ public final class SelectUpgradeModel<T> implements TurtleUpgradeModel {
getModel(upgrade).renderForItem(upgrade, side, renderer, resolver, transform, seed);
}
@Override
public void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
getModel(upgrade).renderForLevel(upgrade, side, turtle, transform, buffers, light, overlay);
}
private record Unbaked<T>(
Cases<T> cases,
Optional<TurtleUpgradeModel.Unbaked> fallback

View File

@@ -4,24 +4,21 @@
package dan200.computercraft.api.client.turtle;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ResolvableModel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ItemOwner;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
@@ -67,24 +64,10 @@ public interface TurtleUpgradeModel {
* @param resolver The model resolver.
* @param transform The root model's transformation.
* @param seed The current model seed.
* @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, LivingEntity, int)
* @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, ItemOwner, int)
*/
void renderForItem(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed);
/**
* Render this upgrade to a {@link MultiBufferSource}. This is used for rendering the block-entity form of the
* upgrade.
*
* @param upgrade The upgrade being rendered.
* @param side Which side of the turtle (left or right) the upgrade is equipped on.
* @param turtle Access to the turtle that the upgrade resides on.
* @param transform The current pose stack.
* @param buffers The buffers to render to.
* @param light The lightmap coordinate.
* @param overlay The overlay coordinate.
*/
void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay);
/**
* An unbaked turtle model. Much like other unbaked models (e.g. {@link ItemModel.Unbaked}), this should resolve
* any dependencies and returned the fully-resolved model.

View File

@@ -11,13 +11,6 @@ plugins {
id("cc-tweaked.publishing")
}
sourceSets.client {
java {
exclude("dan200/computercraft/client/integration/emi")
exclude("dan200/computercraft/client/integration/jei")
}
}
minecraft {
accessWideners(
"src/main/resources/computercraft.accesswidener",
@@ -46,7 +39,6 @@ dependencies {
compileOnly(libs.mixin)
compileOnly(libs.mixinExtra)
compileOnly(libs.bundles.externalMods.common)
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
annotationProcessorEverywhere(libs.autoService)
testFixturesAnnotationProcessor(libs.autoService)

View File

@@ -7,7 +7,6 @@ package dan200.computercraft.client;
import com.mojang.blaze3d.audio.Channel;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.CableHighlightRenderer;
import dan200.computercraft.client.render.ExtendedItemFrameRenderState;
@@ -22,14 +21,13 @@ import dan200.computercraft.shared.media.items.PrintoutItem;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.PauseAwareTimer;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.entity.state.ItemFrameRenderState;
import net.minecraft.client.sounds.AudioStream;
import net.minecraft.client.sounds.SoundEngine;
@@ -40,8 +38,6 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import java.util.function.Consumer;
/**
* Event listeners for client-only code.
* <p>
@@ -71,30 +67,31 @@ public final class ClientHooks {
}
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
// TODO: Reconsider this API once https://github.com/FabricMC/fabric/pull/4906/ is merged.
return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit)
|| MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit);
}
public static boolean onRenderHeldItem(
PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand,
PoseStack transform, SubmitNodeCollector collector, int lightTexture, InteractionHand hand,
float pitch, float equipProgress, float swingProgress, ItemStack stack
) {
if (stack.getItem() instanceof PocketComputerItem) {
PocketItemRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
PocketItemRenderer.INSTANCE.renderItemFirstPerson(transform, collector, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
return true;
}
if (stack.getItem() instanceof PrintoutItem) {
PrintoutItemRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
PrintoutItemRenderer.INSTANCE.renderItemFirstPerson(transform, collector, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
return true;
}
return false;
}
public static boolean onRenderItemFrame(PoseStack transform, MultiBufferSource render, ItemFrameRenderState frame, ExtendedItemFrameRenderState state, int light) {
public static boolean onRenderItemFrame(PoseStack transform, SubmitNodeCollector render, ItemFrameRenderState frame, ExtendedItemFrameRenderState state) {
if (state.printoutData != null) {
transform.mulPose(Axis.ZP.rotationDegrees(frame.rotation * 360.0f / 8.0f));
PrintoutItemRenderer.onRenderInFrame(transform, render, frame, state.printoutData, state.isBook, light);
PrintoutItemRenderer.onRenderInFrame(transform, render, frame, state.printoutData, state.isBook);
return true;
}
@@ -105,37 +102,6 @@ public final class ClientHooks {
SpeakerManager.onPlayStreaming(engine, channel, stream);
}
/**
* Add additional information about the currently targeted block to the debug screen.
*
* @param addText A callback which adds a single line of text.
*/
public static void addBlockDebugInfo(Consumer<String> addText) {
var minecraft = Minecraft.getInstance();
if (!minecraft.getDebugOverlay().showDebugScreen() || minecraft.level == null) return;
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
if (tile instanceof MonitorBlockEntity monitor) {
addText.accept("");
addText.accept(
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
);
} else if (tile instanceof TurtleBlockEntity turtle) {
addText.accept("");
addText.accept("Targeted turtle:");
addText.accept(String.format("Id: %d", turtle.getComputerID()));
addTurtleUpgrade(addText, turtle, TurtleSide.LEFT);
addTurtleUpgrade(addText, turtle, TurtleSide.RIGHT);
}
}
private static void addTurtleUpgrade(Consumer<String> out, TurtleBlockEntity turtle, TurtleSide side) {
var upgrade = turtle.getAccess().getUpgradeWithData(side);
if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location()));
}
public static BlockState getBlockBreakingState(BlockState state, BlockPos pos) {
// Only apply to cables which have both a cable and modem
if (state.getBlock() != ModRegistry.Blocks.CABLE.get()

View File

@@ -12,6 +12,9 @@ import dan200.computercraft.client.item.colour.PocketComputerLight;
import dan200.computercraft.client.item.model.TurtleOverlayModel;
import dan200.computercraft.client.item.properties.PocketComputerStateProperty;
import dan200.computercraft.client.item.properties.TurtleShowElfOverlay;
import dan200.computercraft.client.model.LecternBookModel;
import dan200.computercraft.client.model.LecternPocketModel;
import dan200.computercraft.client.model.LecternPrintoutModel;
import dan200.computercraft.client.platform.ClientPlatformHelper;
import dan200.computercraft.client.platform.ModelKey;
import dan200.computercraft.client.render.CustomLecternRenderer;
@@ -23,11 +26,14 @@ import dan200.computercraft.client.turtle.TurtleUpgradeModelManager;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import net.minecraft.client.color.item.ItemTintSource;
import net.minecraft.client.gui.components.debug.DebugScreenEntry;
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState;
import net.minecraft.client.gui.screens.MenuScreens;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
@@ -51,6 +57,7 @@ import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Registers client-side objects, such as {@link BlockEntityRendererProvider}s and
@@ -193,6 +200,12 @@ public final class ClientRegistry {
register.accept(TurtleShowElfOverlay.ID, TurtleShowElfOverlay.CODEC);
}
public static void registerLayerDefinitions(BiConsumer<ModelLayerLocation, Supplier<LayerDefinition>> register) {
register.accept(LecternBookModel.LAYER, LecternBookModel::createLayer);
register.accept(LecternPrintoutModel.LAYER, LecternPrintoutModel::createLayer);
register.accept(LecternPocketModel.LAYER, LecternPocketModel::createLayer);
}
public interface RegisterPictureInPictureRenderer {
<T extends PictureInPictureRenderState> void register(Class<T> state, Function<MultiBufferSource.BufferSource, PictureInPictureRenderer<T>> factory);
}
@@ -200,4 +213,8 @@ public final class ClientRegistry {
public static void registerPictureInPictureRenderers(RegisterPictureInPictureRenderer register) {
register.register(PrintoutScreen.PrintoutRenderState.class, PrintoutScreen.PrintoutPictureRenderer::new);
}
public static void registerDebugScreenEntries(BiConsumer<ResourceLocation, DebugScreenEntry> register) {
register.accept(LookingAtBlockEntityDebugEntry.ID, LookingAtBlockEntityDebugEntry.create());
}
}

View File

@@ -23,6 +23,8 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
@@ -105,24 +107,33 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
public boolean keyPressed(KeyEvent event) {
// Forward the tab key to the terminal, rather than moving between controls.
if (key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal) {
return getFocused().keyPressed(key, scancode, modifiers);
if (event.key() == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal) {
return getFocused().keyPressed(event);
}
return super.keyPressed(key, scancode, modifiers);
return super.keyPressed(event);
}
@Override
public boolean mouseReleased(double x, double y, int button) {
public boolean mouseReleased(MouseButtonEvent event) {
// Reimplement ContainerEventHandler.mouseReleased, as it's not called in vanilla (it is in Forge, but that
// shouldn't matter).
setDragging(false);
var child = getChildAt(x, y);
if (child.isPresent() && child.get().mouseReleased(x, y, button)) return true;
var child = getChildAt(event.x(), event.y());
if (child.isPresent() && child.get().mouseReleased(event)) return true;
return super.mouseReleased(x, y, button);
return super.mouseReleased(event);
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
// Reimplement ContainerEventHandler.mouseScrolled, as AbstractContainerScreen overrides it.
var child = getChildAt(mouseX, mouseY);
if (child.isPresent() && child.get().mouseScrolled(mouseX, mouseY, scrollX, scrollY)) return true;
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
}
@Override
@@ -132,8 +143,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
}
@Override
public boolean mouseClicked(double x, double y, int button) {
var changed = super.mouseClicked(x, y, button);
public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) {
var changed = super.mouseClicked(event, doubleClick);
// Clicking the terminate/shutdown button steals focus, which means then pressing "enter" will click the button
// again. Restore the focus to the terminal in these cases.
if (getFocused() instanceof DynamicImageButton) setFocused(terminal);
@@ -141,9 +152,9 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
}
@Override
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
return (getFocused() != null && getFocused().mouseDragged(x, y, button, deltaX, deltaY))
|| super.mouseDragged(x, y, button, deltaX, deltaY);
public boolean mouseDragged(MouseButtonEvent event, double deltaX, double deltaY) {
return (getFocused() != null && getFocused().mouseDragged(event, deltaX, deltaY))
|| super.mouseDragged(event, deltaX, deltaY);
}
@Override

View File

@@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.gui;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.debug.DebugEntryLookingAtBlock;
import net.minecraft.client.gui.components.debug.DebugScreenDisplayer;
import net.minecraft.client.gui.components.debug.DebugScreenEntry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* A {@link DebugScreenEntry} that provides information about the currently looked at block entity.
*
* @see DebugEntryLookingAtBlock
*/
public final class LookingAtBlockEntityDebugEntry implements DebugScreenEntry {
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "looking_at_block_entity");
private final Map<BlockEntityType<?>, BiConsumer<List<String>, BlockEntity>> blockEntityEmitters = new HashMap<>();
private LookingAtBlockEntityDebugEntry() {
}
@Override
public void display(DebugScreenDisplayer displayer, @Nullable Level level, @Nullable LevelChunk clientChunk, @Nullable LevelChunk serverChunk) {
var entity = Minecraft.getInstance().getCameraEntity();
var trueLevel = SharedConstants.DEBUG_SHOW_SERVER_DEBUG_VALUES ? level : Minecraft.getInstance().level;
if (entity == null || trueLevel == null) return;
var hitResult = entity.pick(20.0, 0.0F, false);
if (hitResult.getType() != HitResult.Type.BLOCK) return;
var blockEntity = trueLevel.getBlockEntity(((BlockHitResult) hitResult).getBlockPos());
if (blockEntity == null) return;
var emitter = blockEntityEmitters.get(blockEntity.getType());
if (emitter == null) return;
List<String> lines = new ArrayList<>();
emitter.accept(lines, blockEntity);
displayer.addToGroup(ID, lines);
}
@SuppressWarnings("unchecked")
private <T extends BlockEntity> LookingAtBlockEntityDebugEntry register(BlockEntityType<T> type, BiConsumer<List<String>, T> emit) {
blockEntityEmitters.put(type, (BiConsumer<List<String>, BlockEntity>) emit);
return this;
}
public static DebugScreenEntry create() {
return new LookingAtBlockEntityDebugEntry()
.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), LookingAtBlockEntityDebugEntry::debugMonitor)
.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), LookingAtBlockEntityDebugEntry::debugMonitor)
.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), LookingAtBlockEntityDebugEntry::debugTurtle)
.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), LookingAtBlockEntityDebugEntry::debugTurtle);
}
private static void debugMonitor(List<String> lines, MonitorBlockEntity monitor) {
lines.add(
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
);
}
private static void debugTurtle(List<String> lines, TurtleBlockEntity turtle) {
lines.add("Targeted turtle:");
lines.add(String.format("Id: %d", turtle.getComputerID()));
addTurtleUpgrade(lines, turtle, TurtleSide.LEFT);
addTurtleUpgrade(lines, turtle, TurtleSide.RIGHT);
}
private static void addTurtleUpgrade(List<String> out, TurtleBlockEntity turtle, TurtleSide side) {
var upgrade = turtle.getAccess().getUpgradeWithData(side);
if (upgrade != null) out.add(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location()));
}
}

View File

@@ -14,6 +14,7 @@ import net.minecraft.client.ScrollWheelHandler;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import org.jspecify.annotations.Nullable;
@@ -91,13 +92,13 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
}
@Override
public final boolean keyPressed(int key, int scancode, int modifiers) {
public final boolean keyPressed(KeyEvent event) {
// Forward the tab key to the terminal, rather than moving between controls.
if (key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal) {
return getFocused().keyPressed(key, scancode, modifiers);
if (event.key() == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal) {
return getFocused().keyPressed(event);
}
return super.keyPressed(key, scancode, modifiers);
return super.keyPressed(event);
}
@Override

View File

@@ -96,7 +96,7 @@ public final class OptionScreen extends Screen {
);
graphics.blit(RenderPipelines.GUI_TEXTURED, BACKGROUND, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING, 256, 256);
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(graphics, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
assertNonNull(messageRenderer).render(graphics, MultiLineLabel.Align.LEFT, +PADDING, y + PADDING, FONT_HEIGHT, false, 0x404040);
super.render(graphics, mouseX, mouseY, partialTicks);
}

View File

@@ -16,6 +16,7 @@ import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
import net.minecraft.client.gui.render.state.GuiElementRenderState;
import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.Component;
@@ -87,18 +88,18 @@ public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu>
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
if (key == GLFW.GLFW_KEY_RIGHT) {
public boolean keyPressed(KeyEvent event) {
if (event.key() == GLFW.GLFW_KEY_RIGHT) {
nextPage();
return true;
}
if (key == GLFW.GLFW_KEY_LEFT) {
if (event.key() == GLFW.GLFW_KEY_LEFT) {
previousPage();
return true;
}
return super.keyPressed(key, scancode, modifiers);
return super.keyPressed(event);
}
@Override
@@ -185,7 +186,9 @@ public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu>
pose.translate(-0.5f * X_SIZE, -(Y_SIZE + COVER_SIZE), 0);
pose.scale(1.0f, 1.0f, -1.0f);
drawBorder(pose, bufferSource, 0, 0, 0, state.page(), state.printout().pages(), state.printout().book(), LightTexture.FULL_BRIGHT);
var buffer = bufferSource.getBuffer(PrintoutRenderer.BACKGROUND);
drawBorder(pose.last().pose(), buffer, 0, 0, 0, state.page(), state.printout().pages(), state.printout().book(), LightTexture.FULL_BRIGHT);
drawText(
pose, bufferSource, X_TEXT_MARGIN, Y_TEXT_MARGIN, PrintoutData.LINES_PER_PAGE * state.page(), LightTexture.FULL_BRIGHT,
state.printout().text(), state.printout().colour()

View File

@@ -19,7 +19,9 @@ import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.render.TextureSetup;
import net.minecraft.client.gui.render.state.GuiElementRenderState;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.input.CharacterEvent;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.network.chat.Component;
@@ -78,22 +80,22 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public boolean charTyped(char ch, int modifiers) {
var terminalChar = StringUtil.unicodeToTerminal(ch);
public boolean charTyped(CharacterEvent event) {
var terminalChar = StringUtil.unicodeToTerminal(event.codepoint());
if (StringUtil.isTypableChar(terminalChar)) computer.charTyped((byte) terminalChar);
return true;
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
if (key == GLFW.GLFW_KEY_ESCAPE) return false;
if (Screen.isPaste(key)) {
public boolean keyPressed(KeyEvent event) {
if (event.key() == GLFW.GLFW_KEY_ESCAPE) return false;
if (event.isPaste()) {
paste();
return true;
}
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
switch (KeyConverter.physicalToActual(key, scancode)) {
if ((event.modifiers() & GLFW.GLFW_MOD_CONTROL) != 0) {
switch (KeyConverter.physicalToActual(event.key(), event.scancode())) {
case GLFW.GLFW_KEY_T -> {
if (terminateTimer < 0) terminateTimer = 0;
}
@@ -106,11 +108,11 @@ public class TerminalWidget extends AbstractWidget {
}
}
if (key >= 0 && terminateTimer < KEY_SUPPRESS_DELAY && rebootTimer < KEY_SUPPRESS_DELAY && shutdownTimer < KEY_SUPPRESS_DELAY) {
if (event.key() >= 0 && terminateTimer < KEY_SUPPRESS_DELAY && rebootTimer < KEY_SUPPRESS_DELAY && shutdownTimer < KEY_SUPPRESS_DELAY) {
// Queue the "key" event and add to the down set
var repeat = keysDown.get(key);
keysDown.set(key);
computer.keyDown(key, repeat);
var repeat = keysDown.get(event.key());
keysDown.set(event.key());
computer.keyDown(event.key(), repeat);
}
return true;
@@ -122,14 +124,14 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public boolean keyReleased(int key, int scancode, int modifiers) {
public boolean keyReleased(KeyEvent event) {
// Queue the "key_up" event and remove from the down set
if (key >= 0 && keysDown.get(key)) {
keysDown.set(key, false);
computer.keyUp(key);
if (event.key() >= 0 && keysDown.get(event.key())) {
keysDown.set(event.key(), false);
computer.keyUp(event.key());
}
switch (KeyConverter.physicalToActual(key, scancode)) {
switch (KeyConverter.physicalToActual(event.key(), event.scancode())) {
case GLFW.GLFW_KEY_T -> terminateTimer = -1;
case GLFW.GLFW_KEY_R -> rebootTimer = -1;
case GLFW.GLFW_KEY_S -> shutdownTimer = -1;
@@ -141,18 +143,18 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (!inTermRegion(mouseX, mouseY)) return false;
if (!hasMouseSupport() || button < 0 || button > 2) return false;
public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) {
if (!inTermRegion(event.x(), event.y())) return false;
if (!hasMouseSupport() || event.button() < 0 || event.button() > 2) return false;
var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
var charX = (int) ((event.x() - innerX) / FONT_WIDTH);
var charY = (int) ((event.y() - innerY) / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
computer.mouseClick(button + 1, charX + 1, charY + 1);
computer.mouseClick(event.button() + 1, charX + 1, charY + 1);
lastMouseButton = button;
lastMouseButton = event.button();
lastMouseX = charX;
lastMouseY = charY;
@@ -160,16 +162,16 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
if (!inTermRegion(mouseX, mouseY)) return false;
if (!hasMouseSupport() || button < 0 || button > 2) return false;
public boolean mouseReleased(MouseButtonEvent event) {
if (!inTermRegion(event.x(), event.y())) return false;
if (!hasMouseSupport() || event.button() < 0 || event.button() > 2) return false;
var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
var charX = (int) ((event.x() - innerX) / FONT_WIDTH);
var charY = (int) ((event.y() - innerY) / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
if (lastMouseButton == button) {
if (lastMouseButton == event.button()) {
computer.mouseUp(lastMouseButton + 1, charX + 1, charY + 1);
lastMouseButton = -1;
}
@@ -181,17 +183,17 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double v2, double v3) {
if (!inTermRegion(mouseX, mouseY)) return false;
if (!hasMouseSupport() || button < 0 || button > 2) return false;
public boolean mouseDragged(MouseButtonEvent event, double v2, double v3) {
if (!inTermRegion(event.x(), event.y())) return false;
if (!hasMouseSupport() || event.button() < 0 || event.button() > 2) return false;
var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
var charX = (int) ((event.x() - innerX) / FONT_WIDTH);
var charY = (int) ((event.y() - innerY) / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
if (button == lastMouseButton && (charX != lastMouseX || charY != lastMouseY)) {
computer.mouseDrag(button + 1, charX + 1, charY + 1);
if (event.button() == lastMouseButton && (charX != lastMouseX || charY != lastMouseY)) {
computer.mouseDrag(event.button() + 1, charX + 1, charY + 1);
lastMouseX = charX;
lastMouseY = charY;
}
@@ -308,9 +310,8 @@ public class TerminalWidget extends AbstractWidget {
@Nullable ScreenRectangle scissorArea
) implements GuiElementRenderState {
@Override
public void buildVertices(VertexConsumer vertexConsumer, float z) {
var quads = new FixedWidthFontRenderer.QuadEmitter(new Matrix4f().mul(pose).translate(0, 0, z), vertexConsumer);
FixedWidthFontRenderer.drawTerminalBackground(quads, x, y, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
public void buildVertices(VertexConsumer buffer) {
FixedWidthFontRenderer.drawTerminalBackground(new Matrix4f().mul(pose), buffer, x, y, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
}
@Override
@@ -324,14 +325,14 @@ public class TerminalWidget extends AbstractWidget {
@Nullable ScreenRectangle bounds, @Nullable ScreenRectangle scissorArea
) implements GuiElementRenderState {
@Override
public void buildVertices(VertexConsumer vertexConsumer, float z) {
var quads = new FixedWidthFontRenderer.QuadEmitter(new Matrix4f().mul(pose).translate(0, 0, z), vertexConsumer);
FixedWidthFontRenderer.drawTerminalForeground(quads, x, y, terminal);
FixedWidthFontRenderer.drawCursor(quads, x, y, terminal);
public void buildVertices(VertexConsumer buffer) {
var transform = new Matrix4f().mul(pose);
FixedWidthFontRenderer.drawTerminalForeground(transform, buffer, x, y, terminal);
FixedWidthFontRenderer.drawCursor(transform, buffer, x, y, terminal);
// The GUI renderer requires that the buffer is non-empty. Add a zero-size vertex so we always have something.
for (var i = 0; i < 4; i++) {
vertexConsumer.addVertex(0, 0, z).setColor(0x00ffffff).setUv(0, 0).setLight(LightTexture.FULL_BRIGHT);
buffer.addVertex(0, 0, 0).setColor(0x00ffffff).setUv(0, 0).setLight(LightTexture.FULL_BRIGHT);
}
}

View File

@@ -1,49 +0,0 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.integration.emi;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import dev.emi.emi.api.EmiEntrypoint;
import dev.emi.emi.api.EmiPlugin;
import dev.emi.emi.api.EmiRegistry;
import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
import net.minecraft.client.Minecraft;
import net.minecraft.world.item.ItemStack;
import java.util.function.BiPredicate;
@EmiEntrypoint
public class EMIComputerCraft implements EmiPlugin {
@Override
public void register(EmiRegistry registry) {
registry.setDefaultComparison(ModRegistry.Items.TURTLE_NORMAL.get(), turtleComparison);
registry.setDefaultComparison(ModRegistry.Items.TURTLE_ADVANCED.get(), turtleComparison);
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
for (var stack : RecipeModHelpers.getExtraStacks(Minecraft.getInstance().level.registryAccess())) {
registry.addEmiStack(EmiStack.of(stack));
}
}
private static final Comparison turtleComparison = compareStacks((left, right)
-> TurtleItem.getUpgrade(left, TurtleSide.LEFT) == TurtleItem.getUpgrade(right, TurtleSide.LEFT)
&& TurtleItem.getUpgrade(left, TurtleSide.RIGHT) == TurtleItem.getUpgrade(right, TurtleSide.RIGHT));
private static final Comparison pocketComparison = compareStacks((left, right) -> PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
return Comparison.of((left, right) -> {
ItemStack leftStack = left.getItemStack(), rightStack = right.getItemStack();
return leftStack.getItem() == rightStack.getItem() && test.test(leftStack, rightStack);
});
}
}

View File

@@ -8,13 +8,14 @@ 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.pocket.core.PocketSide;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.RecipeTypes;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.ingredients.subtypes.IIngredientSubtypeInterpreter;
import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter;
import mezz.jei.api.registration.IAdvancedRegistration;
import mezz.jei.api.registration.ISubtypeRegistration;
import mezz.jei.api.runtime.IJeiRuntime;
@@ -46,7 +47,7 @@ public class JEIComputerCraft implements IModPlugin {
@Override
public void registerAdvanced(IAdvancedRegistration registry) {
registry.addRecipeManagerPlugin(new RecipeResolver(getRegistryAccess()));
registry.addSimpleRecipeManagerPlugin(RecipeTypes.CRAFTING, new RecipeResolver(getRegistryAccess()));
}
@Override
@@ -62,7 +63,7 @@ public class JEIComputerCraft implements IModPlugin {
// Hide all upgrade recipes
var category = registry.createRecipeLookup(RecipeTypes.CRAFTING);
category.get().forEach(wrapper -> {
if (RecipeModHelpers.shouldRemoveRecipe(wrapper.id())) {
if (RecipeModHelpers.shouldRemoveRecipe(wrapper.id().location())) {
registry.hideRecipes(RecipeTypes.CRAFTING, List.of(wrapper));
}
});
@@ -71,7 +72,7 @@ public class JEIComputerCraft implements IModPlugin {
/**
* Distinguishes turtles by upgrades and family.
*/
private static final IIngredientSubtypeInterpreter<ItemStack> turtleSubtype = (stack, ctx) -> {
private static final ISubtypeInterpreter<ItemStack> turtleSubtype = (stack, ctx) -> {
var name = new StringBuilder("turtle:");
// Add left and right upgrades to the identifier
@@ -87,12 +88,15 @@ public class JEIComputerCraft implements IModPlugin {
/**
* Distinguishes pocket computers by upgrade and family.
*/
private static final IIngredientSubtypeInterpreter<ItemStack> pocketSubtype = (stack, ctx) -> {
private static final ISubtypeInterpreter<ItemStack> pocketSubtype = (stack, ctx) -> {
var name = new StringBuilder("pocket:");
// Add the upgrade to the identifier
var upgrade = PocketComputerItem.getUpgradeWithData(stack);
if (upgrade != null) name.append(upgrade.holder().key().location());
var back = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BACK);
var bottom = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BOTTOM);
if (back != null) name.append(back.holder().key().location());
if (back != null && bottom != null) name.append('|');
if (bottom != null) name.append(bottom.holder().key().location());
return name.toString();
};
@@ -100,7 +104,7 @@ public class JEIComputerCraft implements IModPlugin {
/**
* Distinguishes disks by colour.
*/
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DyedItemColor.getOrDefault(stack, -1));
private static final ISubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DyedItemColor.getOrDefault(stack, -1));
private static RegistryAccess getRegistryAccess() {
return Minecraft.getInstance().level.registryAccess();

View File

@@ -8,71 +8,90 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import mezz.jei.api.constants.RecipeTypes;
import mezz.jei.api.recipe.IFocus;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.advanced.IRecipeManagerPlugin;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.recipe.advanced.ISimpleRecipeManagerPlugin;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingRecipe;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.item.crafting.display.RecipeDisplay;
import net.minecraft.world.level.Level;
import java.util.List;
class RecipeResolver implements IRecipeManagerPlugin {
private final UpgradeRecipeGenerator<RecipeHolder<CraftingRecipe>> resolver;
class RecipeResolver implements ISimpleRecipeManagerPlugin<RecipeHolder<CraftingRecipe>> {
/**
* We need to generate unique ids for each recipe, as JEI will attempt to deduplicate them otherwise.
*/
private int nextId = 0;
private final UpgradeRecipeGenerator<RecipeHolder<CraftingRecipe>> resolver;
RecipeResolver(HolderLookup.Provider registries) {
resolver = new UpgradeRecipeGenerator<>(
x -> new RecipeHolder<>(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "upgrade_" + nextId++), x),
registries
);
resolver = new UpgradeRecipeGenerator<>(x -> new RecipeHolder<>(
ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "upgrade_" + nextId++)),
new CraftingWrapper(x)
), registries);
}
@Override
public <V> List<RecipeType<?>> getRecipeTypes(IFocus<V> focus) {
var value = focus.getTypedValue().getIngredient();
if (!(value instanceof ItemStack stack)) return List.of();
return switch (focus.getRole()) {
case INPUT ->
stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem || resolver.isUpgrade(stack)
? List.of(RecipeTypes.CRAFTING)
: List.of();
case OUTPUT -> stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem
? List.of(RecipeTypes.CRAFTING)
: List.of();
default -> List.of();
};
public boolean isHandledInput(ITypedIngredient<?> input) {
return input.getIngredient() instanceof ItemStack stack
&& (stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem || resolver.isUpgrade(stack));
}
@Override
public <T, V> List<T> getRecipes(IRecipeCategory<T> recipeCategory, IFocus<V> focus) {
if (!(focus.getTypedValue().getIngredient() instanceof ItemStack stack) || recipeCategory.getRecipeType() != RecipeTypes.CRAFTING) {
return List.of();
}
return switch (focus.getRole()) {
case INPUT -> cast(RecipeTypes.CRAFTING, resolver.findRecipesWithInput(stack));
case OUTPUT -> cast(RecipeTypes.CRAFTING, resolver.findRecipesWithOutput(stack));
default -> List.of();
};
public boolean isHandledOutput(ITypedIngredient<?> output) {
return output.getIngredient() instanceof ItemStack stack
&& (stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem);
}
@Override
public <T> List<T> getRecipes(IRecipeCategory<T> recipeCategory) {
public List<RecipeHolder<CraftingRecipe>> getRecipesForInput(ITypedIngredient<?> input) {
return input.getIngredient() instanceof ItemStack stack ? resolver.findRecipesWithInput(stack) : List.of();
}
@Override
public List<RecipeHolder<CraftingRecipe>> getRecipesForOutput(ITypedIngredient<?> output) {
return output.getIngredient() instanceof ItemStack stack ? resolver.findRecipesWithOutput(stack) : List.of();
}
@Override
public List<RecipeHolder<CraftingRecipe>> getAllRecipes() {
return List.of();
}
@SuppressWarnings({ "unchecked", "rawtypes", "UnusedVariable" })
private static <T, U> List<T> cast(RecipeType<U> ignoredType, List<U> from) {
return (List) from;
private record CraftingWrapper(RecipeDisplay recipes) implements CraftingRecipe {
@Override
public RecipeSerializer<? extends CraftingRecipe> getSerializer() {
throw new IllegalStateException("Should not serialise CraftingWrapper");
}
@Override
public CraftingBookCategory category() {
return CraftingBookCategory.MISC;
}
@Override
public boolean matches(CraftingInput input, Level level) {
return false;
}
@Override
public ItemStack assemble(CraftingInput input, HolderLookup.Provider registries) {
return ItemStack.EMPTY;
}
@Override
public PlacementInfo placementInfo() {
return PlacementInfo.NOT_PLACEABLE;
}
@Override
public List<RecipeDisplay> display() {
return List.of(recipes);
}
}
}

View File

@@ -17,7 +17,7 @@ import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ItemOwner;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.Nullable;
@@ -35,7 +35,7 @@ public record TurtleOverlayModel(ItemTransforms transforms) implements ItemModel
).apply(instance, Unbaked::new));
@Override
public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext context, @Nullable ClientLevel level, @Nullable LivingEntity holder, int light) {
public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext context, @Nullable ClientLevel level, @Nullable ItemOwner holder, int light) {
var overlay = TurtleItem.getOverlay(stack);
if (overlay == null) return;

View File

@@ -17,7 +17,7 @@ import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ItemOwner;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.Nullable;
@@ -36,7 +36,7 @@ public record TurtleUpgradeModel(TurtleSide side, ItemTransforms base) implement
).apply(instance, Unbaked::new));
@Override
public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext context, @Nullable ClientLevel level, @Nullable LivingEntity holder, int seed) {
public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext context, @Nullable ClientLevel level, @Nullable ItemOwner holder, int seed) {
var upgrade = TurtleItem.getUpgradeWithData(stack, side);
if (upgrade == null) return;

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.model;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.render.CustomLecternRenderer;
import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.CubeListBuilder;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.model.geom.builders.MeshDefinition;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_HEIGHT;
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_WIDTH;
/**
* A model for {@linkplain PrintoutItem printed books} placed on a lectern.
*
* @see CustomLecternRenderer
*/
public final class LecternBookModel extends Model<Unit> {
public static final ModelLayerLocation LAYER = new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "lectern_book"), "main");
public LecternBookModel(ModelPart root) {
super(root, RenderType::entitySolid);
}
public static LayerDefinition createLayer() {
var mesh = new MeshDefinition();
var parts = mesh.getRoot();
parts.addOrReplaceChild(
"spine",
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
PartPose.ZERO
);
var angle = (float) Math.toRadians(5);
parts.addOrReplaceChild(
"left",
CubeListBuilder.create()
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
);
parts.addOrReplaceChild(
"right",
CubeListBuilder.create()
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
);
return LayerDefinition.create(mesh, TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
}

View File

@@ -10,16 +10,22 @@ import dan200.computercraft.client.pocket.PocketComputerData;
import dan200.computercraft.client.render.CustomLecternRenderer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.CubeListBuilder;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.model.geom.builders.MeshDefinition;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.MaterialSet;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import net.minecraft.world.item.component.DyedItemColor;
/**
@@ -27,18 +33,14 @@ import net.minecraft.world.item.component.DyedItemColor;
*
* @see CustomLecternRenderer
*/
public class LecternPocketModel {
public static final ResourceLocation TEXTURE_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_normal");
public static final ResourceLocation TEXTURE_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_advanced");
public static final ResourceLocation TEXTURE_COLOUR = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_colour");
public static final ResourceLocation TEXTURE_FRAME = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_frame");
public static final ResourceLocation TEXTURE_LIGHT = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_light");
public class LecternPocketModel extends Model<Unit> {
public static final ModelLayerLocation LAYER = new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "lectern_pocket"), "main");
private static final Material MATERIAL_NORMAL = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_NORMAL);
private static final Material MATERIAL_ADVANCED = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_ADVANCED);
private static final Material MATERIAL_COLOUR = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_COLOUR);
private static final Material MATERIAL_FRAME = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_FRAME);
private static final Material MATERIAL_LIGHT = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_LIGHT);
public static final Material MATERIAL_NORMAL = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_normal"));
public static final Material MATERIAL_ADVANCED = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced"));
public static final Material MATERIAL_COLOUR = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_colour"));
public static final Material MATERIAL_FRAME = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_frame"));
public static final Material MATERIAL_LIGHT = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_light"));
// The size of the terminal within the model.
public static final float TERM_WIDTH = 12.0f / 32.0f;
@@ -48,13 +50,11 @@ public class LecternPocketModel {
private static final int TEXTURE_WIDTH = 48 / 2;
private static final int TEXTURE_HEIGHT = 48 / 2;
private final ModelPart root;
public LecternPocketModel() {
root = buildPages();
public LecternPocketModel(ModelPart root) {
super(root, RenderType::entityCutout);
}
private static ModelPart buildPages() {
public static LayerDefinition createLayer() {
var mesh = new MeshDefinition();
var parts = mesh.getRoot();
parts.addOrReplaceChild(
@@ -62,29 +62,43 @@ public class LecternPocketModel {
CubeListBuilder.create().texOffs(0, 0).addBox(0f, -5.0f, -4.0f, 1f, 10.0f, 8.0f),
PartPose.ZERO
);
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
return LayerDefinition.create(mesh, TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
/**
* Render the pocket computer model.
*
* @param poseStack The current pose stack.
* @param bufferSource The buffer source to draw to.
* @param packedLight The current light level.
* @param packedOverlay The overlay texture (used for entity hurt animation).
* @param family The computer family.
* @param frameColour The pocket computer's {@linkplain DyedItemColor colour}.
* @param lightColour The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}.
* @param poseStack The current pose stack.
* @param collector The collector to draw to.
* @param materials The current materials
* @param packedLight The current light level.
* @param family The computer family.
* @param frameColour The pocket computer's {@linkplain DyedItemColor colour}.
* @param lightColour The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}.
*/
public void render(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, ComputerFamily family, int frameColour, int lightColour) {
public void submit(
PoseStack poseStack, SubmitNodeCollector collector, MaterialSet materials, int packedLight, ComputerFamily family, int frameColour, int lightColour
) {
if (frameColour != -1) {
root.render(poseStack, MATERIAL_FRAME.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay);
root.render(poseStack, MATERIAL_COLOUR.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay, frameColour);
collector.submitModel(
this, Unit.INSTANCE, poseStack, MATERIAL_FRAME.renderType(RenderType::entityCutout),
packedLight, OverlayTexture.NO_OVERLAY, -1, materials.get(MATERIAL_FRAME), 0, null
);
collector.submitModel(
this, Unit.INSTANCE, poseStack, MATERIAL_COLOUR.renderType(RenderType::entityCutout),
packedLight, OverlayTexture.NO_OVERLAY, frameColour, materials.get(MATERIAL_COLOUR), 0, null
);
} else {
var buffer = (family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL).buffer(bufferSource, RenderType::entityCutout);
root.render(poseStack, buffer, packedLight, packedOverlay);
var material = family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL;
collector.submitModel(
this, Unit.INSTANCE, poseStack, material.renderType(RenderType::entityCutout),
packedLight, OverlayTexture.NO_OVERLAY, -1, materials.get(material), 0, null
);
}
root.render(poseStack, MATERIAL_LIGHT.buffer(bufferSource, RenderType::entityCutout), LightTexture.FULL_BRIGHT, packedOverlay, lightColour);
collector.submitModel(
this, Unit.INSTANCE, poseStack, MATERIAL_LIGHT.renderType(RenderType::entityCutout),
LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, lightColour, materials.get(MATERIAL_LIGHT), 0, null
);
}
}

View File

@@ -4,52 +4,46 @@
package dan200.computercraft.client.model;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.render.CustomLecternRenderer;
import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.CubeListBuilder;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.model.geom.builders.MeshDefinition;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import java.util.List;
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_HEIGHT;
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_WIDTH;
/**
* A model for {@linkplain PrintoutItem printouts} placed on a lectern.
* <p>
* This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
* number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
* A model for {@linkplain PrintoutItem printouts} placed on a lectern. This renders a variable number of pages (1-3),
* stored in {@link State#pages}.
*
* @see CustomLecternRenderer
*/
public class LecternPrintoutModel {
public static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/printout");
public static final Material MATERIAL = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE);
private static final int TEXTURE_WIDTH = 32;
private static final int TEXTURE_HEIGHT = 32;
public final class LecternPrintoutModel extends Model<LecternPrintoutModel.State> {
public static final ModelLayerLocation LAYER = new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "lectern_printout"), "main");
private static final String PAGE_1 = "page_1";
private static final String PAGE_2 = "page_2";
private static final String PAGE_3 = "page_3";
private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
private final ModelPart pagesRoot;
private final ModelPart bookRoot;
private final ModelPart[] pages;
public LecternPrintoutModel() {
pagesRoot = buildPages();
bookRoot = buildBook();
pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
public LecternPrintoutModel(ModelPart root) {
super(root, RenderType::entitySolid);
pages = PAGES.stream().map(root::getChild).toArray(ModelPart[]::new);
}
private static ModelPart buildPages() {
public static LayerDefinition createLayer() {
var mesh = new MeshDefinition();
var parts = mesh.getRoot();
parts.addOrReplaceChild(
@@ -69,49 +63,20 @@ public class LecternPrintoutModel {
PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
);
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
return LayerDefinition.create(mesh, TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
private static ModelPart buildBook() {
var mesh = new MeshDefinition();
var parts = mesh.getRoot();
parts.addOrReplaceChild(
"spine",
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
PartPose.ZERO
);
var angle = (float) Math.toRadians(5);
parts.addOrReplaceChild(
"left",
CubeListBuilder.create()
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
);
parts.addOrReplaceChild(
"right",
CubeListBuilder.create()
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
);
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
public void renderBook(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay) {
bookRoot.render(poseStack, buffer, packedLight, packedOverlay);
}
public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
@Override
public void setupAnim(State renderState) {
var pageCount = renderState.pages;
if (pageCount > pages.length) pageCount = pages.length;
var i = 0;
for (; i < pageCount; i++) pages[i].visible = true;
for (; i < pages.length; i++) pages[i].visible = false;
}
pagesRoot.render(poseStack, buffer, packedLight, packedOverlay);
public static class State {
public int pages;
}
}

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.model;
import dan200.computercraft.api.ComputerCraftAPI;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.resources.model.Material;
import net.minecraft.resources.ResourceLocation;
/**
* Definitions for the lectern printout model.
*
* @see LecternBookModel
* @see LecternPrintoutModel
*/
public final class LecternPrintoutModelDefinitions {
public static final Material MATERIAL = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "printout"));
static final int TEXTURE_WIDTH = 32;
static final int TEXTURE_HEIGHT = 32;
private LecternPrintoutModelDefinitions() {
}
}

View File

@@ -6,26 +6,36 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.client.model.LecternBookModel;
import dan200.computercraft.client.model.LecternPocketModel;
import dan200.computercraft.client.model.LecternPrintoutModel;
import dan200.computercraft.client.model.LecternPrintoutModelDefinitions;
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.computer.core.ComputerFamily;
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
import dan200.computercraft.shared.media.items.PrintoutData;
import dan200.computercraft.shared.media.items.PrintoutItem;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.LecternRenderer;
import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;
import net.minecraft.client.renderer.feature.ModelFeatureRenderer;
import net.minecraft.client.renderer.state.CameraRenderState;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.MaterialSet;
import net.minecraft.util.ARGB;
import net.minecraft.util.Unit;
import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.level.block.LecternBlock;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
@@ -36,60 +46,89 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON
* <p>
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
*/
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity, CustomLecternRenderer.State> {
private static final int POCKET_TERMINAL_RENDER_DISTANCE = 32;
private final MaterialSet materials;
private final LecternPrintoutModel printoutModel;
private final LecternBookModel bookModel;
private final LecternPocketModel pocketModel;
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
printoutModel = new LecternPrintoutModel();
pocketModel = new LecternPocketModel();
materials = context.materials();
bookModel = new LecternBookModel(context.bakeLayer(LecternBookModel.LAYER));
printoutModel = new LecternPrintoutModel(context.bakeLayer(LecternPrintoutModel.LAYER));
pocketModel = new LecternPocketModel(context.bakeLayer(LecternPocketModel.LAYER));
}
@Override
public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay, Vec3 camera) {
poseStack.pushPose();
poseStack.translate(0.5f, 1.0625f, 0.5f);
poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
poseStack.translate(0, -0.125f, 0);
public State createRenderState() {
return new State();
}
@Override
public void extractRenderState(CustomLecternBlockEntity lectern, State state, float f, Vec3 camera, ModelFeatureRenderer.@Nullable CrumblingOverlay overlay) {
BlockEntityRenderer.super.extractRenderState(lectern, state, f, camera, overlay);
var item = lectern.getItem();
if (item.getItem() instanceof PrintoutItem) {
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
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());
}
state.setPrintout(item.is(ModRegistry.Items.PRINTED_BOOK.get()), PrintoutData.getOrEmpty(item).pages());
} else if (item.getItem() instanceof PocketComputerItem pocket) {
var computer = ClientPocketComputers.get(item);
pocketModel.render(
poseStack, buffer, packedLight, packedOverlay, pocket.getFamily(), DyedItemColor.getOrDefault(item, -1),
ARGB.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState())
state.setPocket(
pocket.getFamily(), DyedItemColor.getOrDefault(item, -1),
ARGB.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState()),
// Only render a terminal if we're close to it.
computer == null || !Vec3.atCenterOf(lectern.getBlockPos()).closerThan(camera, POCKET_TERMINAL_RENDER_DISTANCE)
? null : computer.getTerminal()
);
} else {
state.setUnknown();
}
}
@Override
public void submit(State state, PoseStack poseStack, SubmitNodeCollector collector, CameraRenderState cameraRenderState) {
poseStack.pushPose();
poseStack.translate(0.5f, 1.0625f, 0.5f);
poseStack.mulPose(Axis.YP.rotationDegrees(-state.blockState.getValue(LecternBlock.FACING).getClockWise().toYRot()));
poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
poseStack.translate(0, -0.125f, 0);
if (state.type == Type.PRINTOUT) {
if (state.isBook) {
collector.submitModel(
bookModel, Unit.INSTANCE, poseStack, LecternPrintoutModelDefinitions.MATERIAL.renderType(RenderType::entitySolid),
state.lightCoords, OverlayTexture.NO_OVERLAY, -1,
materials.get(LecternPrintoutModelDefinitions.MATERIAL), 0, null
);
} else {
collector.submitModel(
printoutModel, state.printoutState, poseStack, LecternPrintoutModelDefinitions.MATERIAL.renderType(RenderType::entitySolid),
state.lightCoords, OverlayTexture.NO_OVERLAY, -1,
materials.get(LecternPrintoutModelDefinitions.MATERIAL), 0, null
);
}
} else if (state.type == Type.POCKET_COMPUTER) {
pocketModel.submit(poseStack, collector, materials, state.lightCoords, state.pocketFamily, state.pocketColour, state.pocketLight);
// Jiggle the terminal about a bit, so (0, 0) is in the top left of the model's terminal hole.
poseStack.mulPose(Axis.YP.rotationDegrees(90f));
poseStack.translate(-0.5 * LecternPocketModel.TERM_WIDTH, 0.5 * LecternPocketModel.TERM_HEIGHT + 1f / 32.0f, 1 / 16.0f);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
// Either render the terminal or a black screen, depending on how close we are.
var terminal = computer == null ? null : computer.getTerminal();
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(poseStack, buffer.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT));
if (terminal != null && Vec3.atCenterOf(lectern.getBlockPos()).closerThan(camera, POCKET_TERMINAL_RENDER_DISTANCE)) {
renderPocketTerminal(poseStack, quadEmitter, terminal);
// Either render the terminal or a black screen.
if (state.pocketTerminal != null) {
renderPocketTerminal(poseStack, collector, state.pocketTerminal);
} else {
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT);
FixedWidthFontRenderer.drawEmptyTerminal(poseStack, collector, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT);
}
}
poseStack.popPose();
}
private static void renderPocketTerminal(PoseStack poseStack, FixedWidthFontRenderer.QuadEmitter quadEmitter, Terminal terminal) {
private static void renderPocketTerminal(PoseStack poseStack, SubmitNodeCollector collector, Terminal terminal) {
var width = terminal.getWidth() * FONT_WIDTH;
var height = terminal.getHeight() * FONT_HEIGHT;
@@ -103,6 +142,48 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
var marginX = ((LecternPocketModel.TERM_WIDTH / scale) - width) / 2;
var marginY = ((LecternPocketModel.TERM_HEIGHT / scale) - height) / 2;
FixedWidthFontRenderer.drawTerminal(quadEmitter, marginX, marginY, terminal, marginY, marginY, marginX, marginX);
collector.submitCustomGeometry(poseStack, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) ->
FixedWidthFontRenderer.drawTerminal(pose.pose(), buffer, marginX, marginY, terminal, marginY, marginY, marginX, marginX));
}
private enum Type {
PRINTOUT,
POCKET_COMPUTER,
UNKNOWN,
}
public static final class State extends BlockEntityRenderState {
private Type type = Type.PRINTOUT;
private boolean isBook;
private final LecternPrintoutModel.State printoutState = new LecternPrintoutModel.State();
private ComputerFamily pocketFamily = ComputerFamily.NORMAL;
private int pocketColour;
private int pocketLight;
private @Nullable Terminal pocketTerminal; // TODO: Make this immutable
private State() {
}
private void setUnknown() {
this.type = Type.UNKNOWN;
this.pocketTerminal = null;
}
private void setPrintout(boolean isBook, int pages) {
this.type = Type.PRINTOUT;
this.isBook = isBook;
this.printoutState.pages = pages;
this.pocketTerminal = null;
}
private void setPocket(ComputerFamily family, int colour, int light, @Nullable Terminal terminal) {
this.type = Type.POCKET_COMPUTER;
this.pocketFamily = family;
this.pocketColour = colour;
this.pocketLight = light;
this.pocketTerminal = terminal;
}
}
}

View File

@@ -8,7 +8,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.HumanoidArm;
@@ -22,29 +22,29 @@ import java.util.Objects;
/**
* A base class for items which have map-like rendering when held in the hand.
*
* @see dan200.computercraft.client.ClientHooks#onRenderHeldItem(PoseStack, MultiBufferSource, int, InteractionHand, float, float, float, ItemStack)
* @see dan200.computercraft.client.ClientHooks#onRenderHeldItem(PoseStack, SubmitNodeCollector, int, InteractionHand, float, float, float, ItemStack)
*/
public abstract class ItemMapLikeRenderer {
/**
* The main rendering method for the item.
*
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param collector The buffer to render to
* @param stack The stack to render
* @param light The packed lightmap coordinates.
* @see ItemInHandRenderer#renderItem(LivingEntity, ItemStack, ItemDisplayContext, boolean, PoseStack, MultiBufferSource, int)
* @see ItemInHandRenderer#renderItem(LivingEntity, ItemStack, ItemDisplayContext, PoseStack, SubmitNodeCollector, int)
*/
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
protected abstract void renderItem(PoseStack transform, SubmitNodeCollector collector, ItemStack stack, int light);
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
public void renderItemFirstPerson(PoseStack transform, SubmitNodeCollector collector, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
Player player = Objects.requireNonNull(Minecraft.getInstance().player);
transform.pushPose();
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {
renderItemFirstPersonCenter(transform, render, lightTexture, pitch, equipProgress, swingProgress, stack);
renderItemFirstPersonCenter(transform, collector, lightTexture, pitch, equipProgress, swingProgress, stack);
} else {
renderItemFirstPersonSide(
transform, render, lightTexture,
transform, collector, lightTexture,
hand == InteractionHand.MAIN_HAND ? player.getMainArm() : player.getMainArm().getOpposite(),
equipProgress, swingProgress, stack
);
@@ -56,15 +56,15 @@ public abstract class ItemMapLikeRenderer {
* Renders the item to one side of the player.
*
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param collector The buffer to render to
* @param combinedLight The current light level
* @param side The side to render on
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see ItemInHandRenderer#renderOneHandedMap(PoseStack, MultiBufferSource, int, float, HumanoidArm, float, ItemStack)
* @see ItemInHandRenderer#renderOneHandedMap(PoseStack, SubmitNodeCollector, int, float, HumanoidArm, float, ItemStack)
*/
private void renderItemFirstPersonSide(PoseStack transform, MultiBufferSource render, int combinedLight, HumanoidArm side, float equipProgress, float swingProgress, ItemStack stack) {
private void renderItemFirstPersonSide(PoseStack transform, SubmitNodeCollector collector, int combinedLight, HumanoidArm side, float equipProgress, float swingProgress, ItemStack stack) {
var minecraft = Minecraft.getInstance();
var offset = side == HumanoidArm.RIGHT ? 1f : -1f;
transform.translate(offset * 0.125f, -0.125f, 0f);
@@ -73,7 +73,7 @@ public abstract class ItemMapLikeRenderer {
if (!minecraft.player.isInvisible()) {
transform.pushPose();
transform.mulPose(Axis.ZP.rotationDegrees(offset * 10f));
minecraft.getEntityRenderDispatcher().getItemInHandRenderer().renderPlayerArm(transform, render, combinedLight, equipProgress, swingProgress, side);
minecraft.getEntityRenderDispatcher().getItemInHandRenderer().renderPlayerArm(transform, collector, combinedLight, equipProgress, swingProgress, side);
transform.popPose();
}
@@ -90,7 +90,7 @@ public abstract class ItemMapLikeRenderer {
transform.mulPose(Axis.XP.rotationDegrees(f2 * -45f));
transform.mulPose(Axis.YP.rotationDegrees(offset * f2 * -30f));
renderItem(transform, render, stack, combinedLight);
renderItem(transform, collector, stack, combinedLight);
transform.popPose();
}
@@ -99,15 +99,15 @@ public abstract class ItemMapLikeRenderer {
* Render an item in the middle of the screen.
*
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param collector The buffer to render to
* @param combinedLight The current light level
* @param pitch The pitch of the player
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see ItemInHandRenderer#renderTwoHandedMap(PoseStack, MultiBufferSource, int, float, float, float)
* @see ItemInHandRenderer#renderTwoHandedMap(PoseStack, SubmitNodeCollector, int, float, float, float)
*/
private void renderItemFirstPersonCenter(PoseStack transform, MultiBufferSource render, int combinedLight, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
private void renderItemFirstPersonCenter(PoseStack transform, SubmitNodeCollector collector, int combinedLight, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
var minecraft = Minecraft.getInstance();
var renderer = minecraft.getEntityRenderDispatcher().getItemInHandRenderer();
@@ -124,8 +124,8 @@ public abstract class ItemMapLikeRenderer {
if (!minecraft.player.isInvisible()) {
transform.pushPose();
transform.mulPose(Axis.YP.rotationDegrees(90.0F));
renderer.renderMapHand(transform, render, combinedLight, HumanoidArm.RIGHT);
renderer.renderMapHand(transform, render, combinedLight, HumanoidArm.LEFT);
renderer.renderMapHand(transform, collector, combinedLight, HumanoidArm.RIGHT);
renderer.renderMapHand(transform, collector, combinedLight, HumanoidArm.LEFT);
transform.popPose();
}
@@ -133,6 +133,6 @@ public abstract class ItemMapLikeRenderer {
transform.mulPose(Axis.XP.rotationDegrees(rX * 20.0F));
transform.scale(2.0F, 2.0F, 2.0F);
renderItem(transform, render, stack, combinedLight);
renderItem(transform, collector, stack, combinedLight);
}
}

View File

@@ -15,12 +15,14 @@ import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.metadata.gui.GuiMetadataSection;
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
import net.minecraft.data.AtlasIds;
import net.minecraft.util.ARGB;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.DyedItemColor;
import org.joml.Matrix4f;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
@@ -42,7 +44,7 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
}
@Override
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
protected void renderItem(PoseStack transform, SubmitNodeCollector collector, ItemStack stack, int light) {
var computer = ClientPocketComputers.get(stack);
var terminal = computer == null ? null : computer.getTerminal();
@@ -74,42 +76,41 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
var family = item.getFamily();
var frameColour = DyedItemColor.getOrDefault(stack, -1);
var matrix = transform.last().pose();
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
renderFrame(transform, collector, family, frameColour, light, width, height);
// Render the light
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
renderLight(transform, bufferSource, lightColour, width, height);
renderLight(transform, collector, lightColour, width, height);
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT));
if (terminal == null) {
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
FixedWidthFontRenderer.drawEmptyTerminal(transform, collector, 0, 0, width, height);
} else {
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) ->
FixedWidthFontRenderer.drawTerminal(pose.pose(), buffer, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN));
}
transform.popPose();
}
private static void renderFrame(Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height) {
private static void renderFrame(PoseStack transform, SubmitNodeCollector submit, ComputerFamily family, int colour, int light, int width, int height) {
var textures = colour != -1 ? GuiSprites.COMPUTER_COLOUR : GuiSprites.getComputerTextures(family);
var spriteRenderer = new SpriteRenderer(transform, render, 0, light, colour);
var spriteRenderer = new SpriteRenderer(transform, submit, 0, light, colour);
renderBorder(spriteRenderer, textures, width, height);
}
private static void renderBorder(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int width, int height) {
var sprites = Minecraft.getInstance().getGuiSprites();
var sprites = Minecraft.getInstance().getAtlasManager().getAtlasOrThrow(AtlasIds.GUI);
// 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 borderSlice = getSlice(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 bottomSlice = getSlice(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.
@@ -157,13 +158,12 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
);
}
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
var buffer = render.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT);
FixedWidthFontRenderer.drawQuad(
FixedWidthFontRenderer.toVertexConsumer(transform, buffer),
private static void renderLight(PoseStack transform, SubmitNodeCollector render, int colour, int width, int height) {
render.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) -> FixedWidthFontRenderer.drawQuad(
pose.pose(), buffer,
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
ARGB.opaque(colour), LightTexture.FULL_BRIGHT
);
));
}
private static final GuiSpriteScaling.NineSlice DEFAULT_BORDER = new GuiSpriteScaling.NineSlice(
@@ -177,4 +177,8 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
private static GuiSpriteScaling.NineSlice getSlice(GuiSpriteScaling scaling, GuiSpriteScaling.NineSlice fallback) {
return scaling instanceof GuiSpriteScaling.NineSlice slice ? slice : fallback;
}
private static GuiSpriteScaling.NineSlice getSlice(TextureAtlasSprite sprite, GuiSpriteScaling.NineSlice fallback) {
return getSlice(sprite.contents().getAdditionalMetadata(GuiMetadataSection.TYPE).orElse(GuiMetadataSection.DEFAULT).scaling(), fallback);
}
}

View File

@@ -6,9 +6,10 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.media.items.PrintoutData;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.entity.state.ItemFrameRenderState;
import net.minecraft.world.item.ItemStack;
@@ -28,26 +29,26 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
}
@Override
protected void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
protected void renderItem(PoseStack transform, SubmitNodeCollector collector, ItemStack stack, int light) {
transform.mulPose(Axis.XP.rotationDegrees(180f));
transform.scale(0.42f, 0.42f, -0.42f);
transform.translate(-0.5f, -0.48f, 0.0f);
drawPrintout(transform, render, PrintoutData.getOrEmpty(stack), stack.getItem() == ModRegistry.Items.PRINTED_BOOK.get(), light);
drawPrintout(transform, collector, PrintoutData.getOrEmpty(stack), stack.getItem() == ModRegistry.Items.PRINTED_BOOK.get(), light);
}
public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrameRenderState frame, PrintoutData data, boolean isBook, int packedLight) {
public static void onRenderInFrame(PoseStack transform, SubmitNodeCollector collector, ItemFrameRenderState frame, PrintoutData data, boolean isBook) {
// Move a little bit forward to ensure we're not clipping with the frame
transform.translate(0.0f, 0.0f, -0.001f);
transform.mulPose(Axis.ZP.rotationDegrees(180f));
transform.scale(0.95f, 0.95f, -0.95f);
transform.translate(-0.5f, -0.5f, 0.0f);
var light = frame.isGlowFrame ? 0xf000d2 : packedLight; // See getLightCoords.
drawPrintout(transform, render, data, isBook, light);
var light = frame.isGlowFrame ? 0xf000d2 : frame.lightCoords; // See getLightCoords.
drawPrintout(transform, collector, data, isBook, light);
}
private static void drawPrintout(PoseStack transform, MultiBufferSource render, PrintoutData pageData, boolean book, int light) {
private static void drawPrintout(PoseStack transform, SubmitNodeCollector collector, PrintoutData pageData, boolean book, int light) {
var pages = pageData.pages();
double width = LINE_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
@@ -71,7 +72,7 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
transform.scale(scale, scale, scale);
transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
drawBorder(transform, render, 0, 0, -0.01f, 0, pages, book, light);
drawText(transform, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, pageData.lines());
collector.submitCustomGeometry(transform, BACKGROUND, (matrix, buffer) -> drawBorder(matrix.pose(), buffer, 0, 0, -0.01f, 0, pages, book, light));
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (matrix, buffer) -> drawText(matrix.pose(), buffer, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, pageData.lines()));
}
}

View File

@@ -29,7 +29,7 @@ public final class PrintoutRenderer {
* Printout's background texture. {@link RenderType#text(ResourceLocation)} is a <em>little</em> questionable, but
* it is what maps use, so should behave the same as vanilla in both item frames and in-hand.
*/
private static final RenderType BACKGROUND = RenderType.text(ResourceLocation.fromNamespaceAndPath("computercraft", "textures/gui/printout.png"));
public static final RenderType BACKGROUND = RenderType.text(ResourceLocation.fromNamespaceAndPath("computercraft", "textures/gui/printout.png"));
private static final float BG_SIZE = 256.0f;
@@ -74,21 +74,20 @@ public final class PrintoutRenderer {
public static void drawText(PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours) {
var buffer = bufferSource.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT);
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, buffer);
for (var line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
FixedWidthFontRenderer.drawString(emitter,
FixedWidthFontRenderer.drawString(
transform.last().pose(), buffer,
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line],
Palette.DEFAULT, light
);
}
}
public static void drawText(PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, List<PrintoutData.Line> lines) {
var buffer = bufferSource.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT);
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, buffer);
public static void drawText(Matrix4f matrix4f, VertexConsumer buffer, int x, int y, int start, int light, List<PrintoutData.Line> lines) {
for (var line = 0; line < LINES_PER_PAGE && line < lines.size(); line++) {
var lineContents = lines.get(start + line);
FixedWidthFontRenderer.drawString(emitter,
FixedWidthFontRenderer.drawString(
matrix4f, buffer,
x, y + line * FONT_HEIGHT,
new TextBuffer(lineContents.text()), new TextBuffer(lineContents.foreground()),
Palette.DEFAULT, light
@@ -96,13 +95,10 @@ public final class PrintoutRenderer {
}
}
public static void drawBorder(PoseStack transform, MultiBufferSource bufferSource, float x, float y, float z, int page, int pages, boolean isBook, int light) {
var matrix = transform.last().pose();
public static void drawBorder(Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, int page, int pages, boolean isBook, int light) {
var leftPages = page;
var rightPages = pages - page - 1;
var buffer = bufferSource.getBuffer(BACKGROUND);
if (isBook) {
// Border
var offset = offsetAt(pages);

View File

@@ -4,13 +4,12 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
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.SubmitNodeCollector;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
import org.joml.Matrix4f;
/**
@@ -21,17 +20,15 @@ import org.joml.Matrix4f;
* sheet.
*/
public class SpriteRenderer {
public static final ResourceLocation TEXTURE = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png");
private final Matrix4f transform;
private final MultiBufferSource buffers;
private final PoseStack transform;
private final SubmitNodeCollector submit;
private final int light;
private final int z;
private final int colour;
public SpriteRenderer(Matrix4f transform, MultiBufferSource buffers, int z, int light, int colour) {
public SpriteRenderer(PoseStack transform, SubmitNodeCollector submit, int z, int light, int colour) {
this.transform = transform;
this.buffers = buffers;
this.submit = submit;
this.z = z;
this.light = light;
this.colour = colour;
@@ -47,11 +44,12 @@ public class SpriteRenderer {
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);
submit.submitCustomGeometry(transform, RenderType.text(sprite.atlasLocation()), (t, vertices) -> {
vertices.addVertex(t, x0, y1, z).setColor(colour).setUv(u0, v1).setLight(light);
vertices.addVertex(t, x1, y1, z).setColor(colour).setUv(u1, v1).setLight(light);
vertices.addVertex(t, x1, y0, z).setColor(colour).setUv(u1, v0).setLight(light);
vertices.addVertex(t, x0, y0, z).setColor(colour).setUv(u0, v0).setLight(light);
});
}
public void blitTiled(

View File

@@ -4,9 +4,12 @@
package dan200.computercraft.client.render;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.StandaloneModel;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.ClientRegistry;
import dan200.computercraft.client.turtle.TurtleOverlay;
@@ -16,111 +19,147 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.Holiday;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;
import net.minecraft.client.renderer.feature.ModelFeatureRenderer;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.renderer.state.CameraRenderState;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ARGB;
import net.minecraft.util.CommonColors;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity, TurtleBlockEntityRenderer.State> {
public static final ResourceLocation NORMAL_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_normal");
public static final ResourceLocation ADVANCED_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_advanced");
public static final ResourceLocation COLOUR_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
private final BlockEntityRenderDispatcher renderer;
private final Font font;
private final ItemModelResolver itemModelResolver;
public TurtleBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
renderer = context.getBlockEntityRenderDispatcher();
font = context.getFont();
itemModelResolver = context.itemModelResolver();
}
public static final class State extends BlockEntityRenderState {
private @Nullable String label;
private Vec3 offset = Vec3.ZERO;
private int colour;
private float yaw;
private @LazyInit StandaloneModel model;
private @Nullable StandaloneModel overlay;
private @Nullable StandaloneModel elfOverlay;
private float leftAngle;
private final ItemStackRenderState leftUpgrade = new ItemStackRenderState();
private float rightAngle;
private final ItemStackRenderState rightUpgrade = new ItemStackRenderState();
private State() {
}
}
@Override
public void render(TurtleBlockEntity turtle, float partialTicks, PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, Vec3 camera) {
public State createRenderState() {
return new State();
}
@Override
public void extractRenderState(TurtleBlockEntity turtle, State state, float partialTicks, Vec3 camera, ModelFeatureRenderer.@Nullable CrumblingOverlay crumblingOverlay) {
BlockEntityRenderer.super.extractRenderState(turtle, state, partialTicks, camera, crumblingOverlay);
var modelManager = Minecraft.getInstance().getModelManager();
var hit = Minecraft.getInstance().hitResult;
state.label = hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())
? turtle.getLabel() : null;
state.colour = turtle.getColour();
state.offset = turtle.getRenderOffset(partialTicks);
state.yaw = turtle.getRenderYaw(partialTicks);
var modelLocation = state.colour == -1
? (turtle.getFamily() == ComputerFamily.NORMAL ? NORMAL_TURTLE_MODEL : ADVANCED_TURTLE_MODEL)
: COLOUR_TURTLE_MODEL;
state.model = ClientRegistry.getModel(modelManager, modelLocation);
var overlay = TurtleOverlayManager.get(modelManager, turtle.getOverlay());
state.overlay = overlay == null ? null : overlay.model();
state.elfOverlay = Holiday.getCurrent() == Holiday.CHRISTMAS && (overlay == null || overlay.showElfOverlay())
? ClientRegistry.getModel(modelManager, TurtleOverlay.ELF_MODEL)
: null;
state.leftAngle = turtle.getToolRenderAngle(TurtleSide.LEFT, partialTicks);
extractUpgrade(turtle.getAccess(), TurtleSide.LEFT, state.leftUpgrade);
state.rightAngle = turtle.getToolRenderAngle(TurtleSide.RIGHT, partialTicks);
extractUpgrade(turtle.getAccess(), TurtleSide.RIGHT, state.rightUpgrade);
}
private void extractUpgrade(ITurtleAccess turtle, TurtleSide side, ItemStackRenderState state) {
state.clear();
var upgrade = turtle.getUpgradeWithData(side);
if (upgrade == null) return;
TurtleUpgradeModelManager.get(Minecraft.getInstance().getModelManager(), upgrade.holder())
.renderForItem(upgrade, side, state, itemModelResolver, ItemTransform.NO_TRANSFORM, 31);
}
@Override
public void submit(State state, PoseStack transform, SubmitNodeCollector collector, CameraRenderState camera) {
transform.pushPose();
// Translate the turtle first, so the label moves with it.
var offset = turtle.getRenderOffset(partialTicks);
transform.translate(offset.x, offset.y, offset.z);
transform.translate(state.offset);
// Render the label
var label = turtle.getLabel();
var hit = renderer.cameraHitResult;
if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
var mc = Minecraft.getInstance();
var font = this.font;
transform.pushPose();
transform.translate(0.5, 1.2, 0.5);
transform.mulPose(mc.getEntityRenderDispatcher().cameraOrientation());
transform.scale(0.025f, -0.025f, 0.025f);
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, 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();
if (state.label != null) {
collector.submitNameTag(
transform, new Vec3(0.5, 1.2, 0.5), 0, Component.literal(state.label), false, state.lightCoords,
camera.pos.distanceToSqr(Vec3.atCenterOf(state.blockPos)), // TODO: Should we read camera from the render state instead?
camera
);
}
// Then apply rotation and flip if needed.
transform.translate(0.5f, 0.5f, 0.5f);
var yaw = turtle.getRenderYaw(partialTicks);
transform.mulPose(Axis.YP.rotationDegrees(180.0f - yaw));
transform.mulPose(Axis.YP.rotationDegrees(180.0f - state.yaw));
transform.translate(-0.5f, -0.5f, -0.5f);
// Render the turtle
var colour = turtle.getColour();
var overlay = TurtleOverlayManager.get(Minecraft.getInstance().getModelManager(), turtle.getOverlay());
state.model.submit(transform, collector, state.lightCoords, OverlayTexture.NO_OVERLAY, state.colour == -1 ? null : new int[]{ ARGB.opaque(state.colour) }, state.breakProgress);
if (colour == -1) {
renderModel(transform, buffers, lightmapCoord, overlayLight, turtle.getFamily() == ComputerFamily.NORMAL ? NORMAL_TURTLE_MODEL : ADVANCED_TURTLE_MODEL, null);
} else {
// Otherwise render it using the colour item.
renderModel(transform, buffers, lightmapCoord, overlayLight, COLOUR_TURTLE_MODEL, new int[]{ ARGB.opaque(colour) });
if (state.overlay != null) {
state.overlay.submit(transform, collector, state.lightCoords, OverlayTexture.NO_OVERLAY);
}
if (state.elfOverlay != null) {
state.elfOverlay.submit(transform, collector, state.lightCoords, OverlayTexture.NO_OVERLAY);
}
// Render the overlay
if (overlay != null) overlay.model().render(transform, buffers, lightmapCoord, overlayLight);
// And the Christmas overlay.
var showChristmas = Holiday.getCurrent() == Holiday.CHRISTMAS && (overlay == null || overlay.showElfOverlay());
if (showChristmas) renderModel(transform, buffers, lightmapCoord, overlayLight, TurtleOverlay.ELF_MODEL, null);
// Render the upgrades
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
submitUpgrade(transform, collector, state.lightCoords, state.leftAngle, state.leftUpgrade);
submitUpgrade(transform, collector, state.lightCoords, state.rightAngle, state.rightUpgrade);
transform.popPose();
}
private void renderUpgrade(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) {
var upgrade = turtle.getAccess().getUpgradeWithData(side);
if (upgrade == null) return;
private void submitUpgrade(PoseStack transform, SubmitNodeCollector collector, int lightmapCoord, float angle, ItemStackRenderState state) {
if (state.isEmpty()) return;
transform.pushPose();
var toolAngle = turtle.getToolRenderAngle(side, f);
// Swing the tool
transform.translate(0.0f, 0.5f, 0.5f);
transform.mulPose(Axis.XN.rotationDegrees(toolAngle));
transform.mulPose(Axis.XN.rotationDegrees(angle));
transform.translate(0.0f, -0.5f, -0.5f);
TurtleUpgradeModelManager.get(Minecraft.getInstance().getModelManager(), upgrade.holder())
.renderForLevel(upgrade, side, turtle.getAccess(), transform, buffers, lightmapCoord, overlayLight);
// Then reposition for rendering the item
transform.translate(0.5f, 0.5f, 0.5f);
state.submit(transform, collector, lightmapCoord, OverlayTexture.NO_OVERLAY, 0);
transform.popPose();
}
private void renderModel(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, int @Nullable [] tints) {
var modelManager = Minecraft.getInstance().getModelManager();
ClientRegistry.getModel(modelManager, modelLocation).render(transform, buffers, lightmapCoord, overlayLight, tints);
}
}

View File

@@ -4,104 +4,81 @@
package dan200.computercraft.client.render.monitor;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.integration.ShaderMod;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.fog.FogRenderer;
import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;
import net.minecraft.client.renderer.feature.ModelFeatureRenderer;
import net.minecraft.client.renderer.state.CameraRenderState;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.jspecify.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBlockEntity> {
public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBlockEntity, MonitorBlockEntityRenderer.State> {
/**
* {@link MonitorBlockEntity#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between
* the monitor frame and contents.
*/
private static final float MARGIN = (float) (MonitorBlockEntity.RENDER_MARGIN * 1.1);
private static @Nullable ByteBuffer backingBuffer;
public MonitorBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
}
@Override
public void render(MonitorBlockEntity monitor, float partialTicks, PoseStack transform, MultiBufferSource bufferSource, int lightmapCoord, int overlayLight, Vec3 camera) {
// Render from the origin monitor
var originTerminal = monitor.getOriginClientMonitor();
if (originTerminal == null) return;
public State createRenderState() {
return new State();
}
var origin = originTerminal.getOrigin();
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
var monitorPos = monitor.getBlockPos();
@Override
public void extractRenderState(MonitorBlockEntity monitor, State state, float f, Vec3 camera, ModelFeatureRenderer.@Nullable CrumblingOverlay crumblingOverlay) {
BlockEntityRenderer.super.extractRenderState(monitor, state, f, camera, crumblingOverlay);
// Ensure each monitor terminal is rendered only once. We allow rendering a specific tile
// multiple times in a single frame to ensure compatibility with shaders which may run a
// pass multiple times.
var renderFrame = FrameInfo.getRenderFrame();
if (renderState.lastRenderFrame == renderFrame && !monitorPos.equals(renderState.lastRenderPos)) {
return;
}
state.direction = monitor.getDirection();
state.front = monitor.getFront();
state.width = monitor.getWidth();
state.height = monitor.getHeight();
state.terminal = monitor.getOriginClientMonitor();
}
renderState.lastRenderFrame = renderFrame;
renderState.lastRenderPos = monitorPos;
var originPos = origin.getBlockPos();
@Override
public void submit(State state, PoseStack transform, SubmitNodeCollector collector, CameraRenderState camera) {
if (state.terminal == null) return;
// Determine orientation
var dir = origin.getDirection();
var front = origin.getFront();
var dir = state.direction;
var front = state.front;
var yaw = dir.toYRot();
var pitch = DirectionUtil.toPitchAngle(front);
// Setup initial transform
transform.pushPose();
transform.translate(
originPos.getX() - monitorPos.getX() + 0.5,
originPos.getY() - monitorPos.getY() + 0.5,
originPos.getZ() - monitorPos.getZ() + 0.5
);
transform.translate(0.5, 0.5, 0.5);
transform.mulPose(Axis.YN.rotationDegrees(yaw));
transform.mulPose(Axis.XP.rotationDegrees(pitch));
transform.translate(
-0.5 + MonitorBlockEntity.RENDER_BORDER + MonitorBlockEntity.RENDER_MARGIN,
origin.getHeight() - 0.5 - (MonitorBlockEntity.RENDER_BORDER + MonitorBlockEntity.RENDER_MARGIN) + 0,
state.height - 0.5 - (MonitorBlockEntity.RENDER_BORDER + MonitorBlockEntity.RENDER_MARGIN) + 0,
0.5
);
var xSize = origin.getWidth() - 2.0 * (MonitorBlockEntity.RENDER_MARGIN + MonitorBlockEntity.RENDER_BORDER);
var ySize = origin.getHeight() - 2.0 * (MonitorBlockEntity.RENDER_MARGIN + MonitorBlockEntity.RENDER_BORDER);
var xSize = state.width - 2.0 * (MonitorBlockEntity.RENDER_MARGIN + MonitorBlockEntity.RENDER_BORDER);
var ySize = state.height - 2.0 * (MonitorBlockEntity.RENDER_MARGIN + MonitorBlockEntity.RENDER_BORDER);
// Draw the contents
var terminal = originTerminal.getTerminal();
if (terminal != null && !ShaderMod.get().isRenderingShadowPass()) {
var terminal = state.terminal.getTerminal();
if (terminal != null) {
// Draw a terminal
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
@@ -110,158 +87,25 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
transform.pushPose();
transform.scale((float) xScale, (float) -yScale, 1.0f);
var matrix = transform.last().pose();
var xMargin = (float) (MARGIN / xScale);
var yMargin = (float) (MARGIN / yScale);
renderTerminal(matrix, originTerminal, renderState, terminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale));
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) -> {
FixedWidthFontRenderer.drawTerminalBackground(pose.pose(), buffer, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin);
});
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT_OFFSET, (pose, buffer) -> {
FixedWidthFontRenderer.drawTerminalForeground(pose.pose(), buffer, 0, 0, terminal);
FixedWidthFontRenderer.drawCursor(pose.pose(), buffer, 0, 0, terminal);
});
transform.popPose();
} else {
FixedWidthFontRenderer.drawEmptyTerminal(
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT)),
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
FixedWidthFontRenderer.drawEmptyTerminal(transform, collector, -MARGIN, MARGIN, (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2));
}
transform.popPose();
}
private static void renderTerminal(
Matrix4f matrix, ClientMonitor monitor, MonitorRenderState renderState, Terminal terminal, float xMargin, float yMargin
) {
var redraw = monitor.pollTerminalChanged();
if (renderState.vertexBuffer == null) redraw = true;
if (redraw) {
// Cursor, Foreground, Background+Margin
var maxQuadCount = 1 + (terminal.getWidth() * terminal.getHeight()) + ((terminal.getWidth() + 2) * (terminal.getHeight() + 2));
var maxVertexCount = 4 * maxQuadCount;
var sink = ShaderMod.get().getQuadEmitter(maxQuadCount, MonitorBlockEntityRenderer::getBuffer);
DirectFixedWidthFontRenderer.drawTerminalBackground(sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin);
var vertexCountAfterBackground = sink.vertexCount();
DirectFixedWidthFontRenderer.drawTerminalForeground(sink, 0, 0, terminal);
var vertexCountAfterForeground = sink.vertexCount();
DirectFixedWidthFontRenderer.drawCursor(sink, 0, 0, terminal);
var vertexCountAfterCursor = sink.vertexCount();
if (vertexCountAfterCursor > maxVertexCount) {
throw new IllegalStateException("Drew too many vertices. Expected " + maxVertexCount + ", drew " + vertexCountAfterCursor);
}
if (vertexCountAfterCursor != 0) {
renderState.register();
var commandEncoder = RenderSystem.getDevice().createCommandEncoder();
var resultBuffer = sink.byteBuffer().flip();
// Ensure our buffer contains the correct number of vertices.
if (resultBuffer.remaining() != sink.format().getVertexSize() * vertexCountAfterCursor) {
throw new IllegalStateException(String.format(
"Mismatched vertex count. Buffer is %d bytes long, but was expected to be %d (vertex size) * %d (vertex count) = %d bytes.",
resultBuffer.limit(), sink.format().getVertexSize(), vertexCountAfterCursor, sink.format().getVertexSize() * vertexCountAfterCursor
));
}
// Upload the buffer, reallocating if required.
if (renderState.vertexBuffer == null || resultBuffer.remaining() > renderState.vertexBuffer.size()) {
if (renderState.vertexBuffer != null) {
renderState.vertexBuffer.close();
renderState.vertexBuffer = null;
}
renderState.vertexBuffer = RenderSystem.getDevice().createBuffer(
() -> "Monitor at " + monitor.getOrigin().getBlockPos(), GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_COPY_DST, resultBuffer
);
} else if (!renderState.vertexBuffer.isClosed()) {
commandEncoder.writeToBuffer(renderState.vertexBuffer.slice(), resultBuffer);
}
}
renderState.vertexCountAfterBackground = vertexCountAfterBackground;
renderState.vertexCountAfterForeground = vertexCountAfterForeground;
renderState.vertexCountAfterCursor = vertexCountAfterCursor;
}
if (renderState.vertexCountAfterCursor == 0) return;
// Our VBO renders coordinates in monitor-space rather than world space. A full sized monitor (8x6) will
// use positions from (0, 0) to (164*FONT_WIDTH, 81*FONT_HEIGHT) = (984, 729). This is far outside the
// normal render distance (~200), and the edges of the monitor fade out due to fog.
// There's not really a good way around this, at least without using a custom render type (which the VBO
// renderer is trying to avoid!). Instead, we just disable fog entirely by setting the fog start to an
// absurdly high value.
var oldFog = RenderSystem.getShaderFog();
RenderSystem.setShaderFog(Minecraft.getInstance().gameRenderer.fogRenderer.getBuffer(FogRenderer.FogMode.NONE));
// Compose the existing model view matrix with our transformation matrix.
RenderSystem.getModelViewStack().pushMatrix();
RenderSystem.getModelViewStack().mul(matrix);
// Render background geometry
drawWithShader(renderState, FixedWidthFontRenderer.TERMINAL_TEXT, RenderPipelines.TEXT, 0, renderState.vertexCountAfterBackground);
drawWithShader(
renderState, FixedWidthFontRenderer.TERMINAL_TEXT_OFFSET, RenderPipelines.TEXT_POLYGON_OFFSET, renderState.vertexCountAfterBackground,
(
FixedWidthFontRenderer.isCursorVisible(terminal) && FrameInfo.getGlobalCursorBlink()
? renderState.vertexCountAfterCursor : renderState.vertexCountAfterForeground
) - renderState.vertexCountAfterBackground
);
// Clear state
RenderSystem.getModelViewStack().popMatrix();
RenderSystem.setShaderFog(oldFog);
}
private static void drawWithShader(MonitorRenderState renderState, RenderType renderType, RenderPipeline pipeline, int vertexOffset, int vertexCount) {
if (renderState.vertexBuffer == null) {
throw new IllegalStateException("MonitorRenderState has not been initialised");
}
if (vertexCount == 0) return;
var transforms = RenderSystem.getDynamicUniforms().writeTransform(
RenderSystem.getModelViewMatrix(),
new Vector4f(1.0F, 1.0F, 1.0F, 1.0F),
RenderSystem.getModelOffset(),
RenderSystem.getTextureMatrix(),
RenderSystem.getShaderLineWidth()
);
renderType.setupRenderState();
var autoStorageBuffer = RenderSystem.getSequentialBuffer(renderType.mode());
var indexCount = FixedWidthFontRenderer.TERMINAL_TEXT.mode().indexCount(vertexCount);
var indexBuffer = autoStorageBuffer.getBuffer(indexCount);
var target = Minecraft.getInstance().getMainRenderTarget();
var colourTarget = RenderSystem.outputColorTextureOverride != null ? RenderSystem.outputColorTextureOverride : target.getColorTextureView();
var depthTarget = target.useDepth
? (RenderSystem.outputDepthTextureOverride != null ? RenderSystem.outputDepthTextureOverride : target.getDepthTextureView())
: null;
try (var renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(
() -> "Monitor", colourTarget, OptionalInt.empty(), depthTarget, OptionalDouble.empty()
)) {
renderPass.setPipeline(pipeline);
RenderSystem.bindDefaultUniforms(renderPass);
renderPass.setUniform("DynamicTransforms", transforms);
renderPass.setVertexBuffer(0, renderState.vertexBuffer);
renderPass.setIndexBuffer(indexBuffer, autoStorageBuffer.type());
for (var j = 0; j < 12; j++) {
var gpuTexture = RenderSystem.getShaderTexture(j);
if (gpuTexture != null) renderPass.bindSampler("Sampler" + j, gpuTexture);
}
renderPass.drawIndexed(vertexOffset, 0, indexCount, 1);
}
renderType.clearRenderState();
}
@Override
public int getViewDistance() {
return Config.monitorDistance;
@@ -272,13 +116,19 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
return monitor.getRenderBoundingBox();
}
private static ByteBuffer getBuffer(int capacity) {
var buffer = backingBuffer;
if (buffer == null || buffer.capacity() < capacity) {
buffer = backingBuffer = buffer == null ? MemoryUtil.memAlloc(capacity) : MemoryUtil.memRealloc(buffer, capacity);
}
@Override
public boolean shouldRender(MonitorBlockEntity monitor, Vec3 camera) {
return BlockEntityRenderer.super.shouldRender(monitor, camera) && monitor.getXIndex() == 0 && monitor.getYIndex() == 0;
}
buffer.clear();
return buffer;
public static final class State extends BlockEntityRenderState {
private Direction direction = Direction.NORTH;
private Direction front = Direction.NORTH;
private int width;
private int height;
private @Nullable ClientMonitor terminal;
private State() {
}
}
}

View File

@@ -13,6 +13,7 @@ import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ARGB;
import org.joml.Matrix4f;
@@ -67,7 +68,7 @@ public final class FixedWidthFontRenderer {
return 15 - Terminal.getColour(c, def);
}
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) {
private static void drawChar(Matrix4f matrix, VertexConsumer buffer, float x, float y, int index, int colour, int light) {
// Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return;
@@ -78,30 +79,30 @@ public final class FixedWidthFontRenderer {
var yStart = 1 + row * (FONT_HEIGHT + 2);
quad(
emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour,
matrix, buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour,
xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light
);
}
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) {
quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
public static void drawQuad(Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float width, float height, int colour, int light) {
quad(matrix, buffer, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
}
private static void drawQuad(QuadEmitter emitter, float x, float y, float width, float height, Palette palette, char colourIndex, int light) {
private static void drawQuad(Matrix4f matrix, VertexConsumer buffer, float x, float y, float width, float height, Palette palette, char colourIndex, int light) {
var colour = palette.getRenderColours(getColour(colourIndex, Colour.BLACK));
drawQuad(emitter, x, y, 0, width, height, colour, light);
drawQuad(matrix, buffer, x, y, 0, width, height, colour, light);
}
private static void drawBackground(
QuadEmitter emitter, float x, float y, TextBuffer backgroundColour, Palette palette,
Matrix4f matrix, VertexConsumer buffer, float x, float y, TextBuffer backgroundColour, Palette palette,
float leftMarginSize, float rightMarginSize, float height, int light
) {
if (leftMarginSize > 0) {
drawQuad(emitter, x - leftMarginSize, y, leftMarginSize, height, palette, backgroundColour.charAt(0), light);
drawQuad(matrix, buffer, x - leftMarginSize, y, leftMarginSize, height, palette, backgroundColour.charAt(0), light);
}
if (rightMarginSize > 0) {
drawQuad(emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, backgroundColour.charAt(backgroundColour.length() - 1), light);
drawQuad(matrix, buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, backgroundColour.charAt(backgroundColour.length() - 1), light);
}
// Batch together runs of identical background cells.
@@ -112,7 +113,7 @@ public final class FixedWidthFontRenderer {
if (colourIndex == blockColour) continue;
if (blockColour != '\0') {
drawQuad(emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, blockColour, light);
drawQuad(matrix, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, blockColour, light);
}
blockColour = colourIndex;
@@ -120,22 +121,22 @@ public final class FixedWidthFontRenderer {
}
if (blockColour != '\0') {
drawQuad(emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, blockColour, light);
drawQuad(matrix, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, blockColour, light);
}
}
public static void drawString(QuadEmitter emitter, float x, float y, TextBuffer text, TextBuffer textColour, Palette palette, int light) {
public static void drawString(Matrix4f matrix, VertexConsumer buffer, float x, float y, TextBuffer text, TextBuffer textColour, Palette palette, int light) {
for (var i = 0; i < text.length(); i++) {
var colour = palette.getRenderColours(getColour(textColour.charAt(i), Colour.BLACK));
int index = text.charAt(i);
if (index > 255) index = '?';
drawChar(emitter, x + i * FONT_WIDTH, y, index, colour, light);
drawChar(matrix, buffer, x + i * FONT_WIDTH, y, index, colour, light);
}
}
public static void drawTerminalForeground(QuadEmitter emitter, float x, float y, Terminal terminal) {
public static void drawTerminalForeground(Matrix4f matrix, VertexConsumer buffer, float x, float y, Terminal terminal) {
var palette = terminal.getPalette();
var height = terminal.getHeight();
@@ -143,14 +144,14 @@ public final class FixedWidthFontRenderer {
for (var i = 0; i < height; i++) {
var rowY = y + FONT_HEIGHT * i;
drawString(
emitter, x, rowY, terminal.getLine(i), terminal.getTextColourLine(i),
matrix, buffer, x, rowY, terminal.getLine(i), terminal.getTextColourLine(i),
palette, LightTexture.FULL_BRIGHT
);
}
}
public static void drawTerminalBackground(
QuadEmitter emitter, float x, float y, Terminal terminal,
Matrix4f matrix, VertexConsumer buffer, float x, float y, Terminal terminal,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
) {
var palette = terminal.getPalette();
@@ -158,12 +159,12 @@ public final class FixedWidthFontRenderer {
// Top and bottom margins
drawBackground(
emitter, x, y - topMarginSize, terminal.getBackgroundColourLine(0), palette,
matrix, buffer, x, y - topMarginSize, terminal.getBackgroundColourLine(0), palette,
leftMarginSize, rightMarginSize, topMarginSize, LightTexture.FULL_BRIGHT
);
drawBackground(
emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine(height - 1), palette,
matrix, buffer, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine(height - 1), palette,
leftMarginSize, rightMarginSize, bottomMarginSize, LightTexture.FULL_BRIGHT
);
@@ -171,7 +172,7 @@ public final class FixedWidthFontRenderer {
for (var i = 0; i < height; i++) {
var rowY = y + FONT_HEIGHT * i;
drawBackground(
emitter, x, rowY, terminal.getBackgroundColourLine(i), palette,
matrix, buffer, x, rowY, terminal.getBackgroundColourLine(i), palette,
leftMarginSize, rightMarginSize, FONT_HEIGHT, LightTexture.FULL_BRIGHT
);
}
@@ -185,52 +186,40 @@ public final class FixedWidthFontRenderer {
return cursorX >= 0 && cursorX < terminal.getWidth() && cursorY >= 0 && cursorY < terminal.getHeight();
}
public static void drawCursor(QuadEmitter emitter, float x, float y, Terminal terminal) {
public static void drawCursor(Matrix4f matrix, VertexConsumer buffer, float x, float y, Terminal terminal) {
if (isCursorVisible(terminal) && FrameInfo.getGlobalCursorBlink()) {
var colour = terminal.getPalette().getRenderColours(15 - terminal.getTextColour());
drawChar(emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, LightTexture.FULL_BRIGHT);
drawChar(matrix, buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, LightTexture.FULL_BRIGHT);
}
}
public static void drawTerminal(
QuadEmitter emitter, float x, float y, Terminal terminal,
Matrix4f matrix, VertexConsumer buffer, float x, float y, Terminal terminal,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
) {
drawTerminalBackground(
emitter, x, y, terminal,
matrix, buffer, x, y, terminal,
topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize
);
// Render the foreground with a slight offset. By calling .translate() on the matrix itself, we're translating
// in screen space, rather than in model/view space.
// It's definitely not perfect, but better than z fighting!
var transformBackup = new Matrix4f(emitter.poseMatrix());
emitter.poseMatrix().translate(new Vector3f(0, 0, Z_OFFSET));
var offsetMatrix = new Matrix4f(matrix).translate(new Vector3f(0, 0, Z_OFFSET));
drawTerminalForeground(emitter, x, y, terminal);
drawCursor(emitter, x, y, terminal);
emitter.poseMatrix().set(transformBackup);
drawTerminalForeground(offsetMatrix, buffer, x, y, terminal);
drawCursor(offsetMatrix, buffer, x, y, terminal);
}
public static void drawEmptyTerminal(QuadEmitter emitter, float x, float y, float width, float height) {
drawQuad(emitter, x, y, 0, width, height, BLACK, LightTexture.FULL_BRIGHT);
public static void drawEmptyTerminal(PoseStack transform, SubmitNodeCollector collector, float x, float y, float width, float height) {
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) ->
drawQuad(pose.pose(), buffer, x, y, 0, width, height, BLACK, LightTexture.FULL_BRIGHT));
}
public record QuadEmitter(Matrix4f poseMatrix, VertexConsumer consumer) {
}
public static QuadEmitter toVertexConsumer(PoseStack transform, VertexConsumer consumer) {
return new QuadEmitter(transform.last().pose(), consumer);
}
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) {
var poseMatrix = c.poseMatrix();
var consumer = c.consumer();
consumer.addVertex(poseMatrix, x1, y1, z).setColor(colour).setUv(u1, v1).setLight(light);
consumer.addVertex(poseMatrix, x1, y2, z).setColor(colour).setUv(u1, v2).setLight(light);
consumer.addVertex(poseMatrix, x2, y2, z).setColor(colour).setUv(u2, v2).setLight(light);
consumer.addVertex(poseMatrix, x2, y1, z).setColor(colour).setUv(u2, v1).setLight(light);
private static void quad(Matrix4f matrix, VertexConsumer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) {
buffer.addVertex(matrix, x1, y1, z).setColor(colour).setUv(u1, v1).setLight(light);
buffer.addVertex(matrix, x1, y2, z).setColor(colour).setUv(u1, v2).setLight(light);
buffer.addVertex(matrix, x2, y2, z).setColor(colour).setUv(u2, v2).setLight(light);
buffer.addVertex(matrix, x2, y1, z).setColor(colour).setUv(u2, v1).setLight(light);
}
}

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.client.gui.GuiSprites;
import dan200.computercraft.client.model.LecternPocketModel;
import dan200.computercraft.client.model.LecternPrintoutModel;
import dan200.computercraft.client.model.LecternPrintoutModelDefinitions;
import dan200.computercraft.client.turtle.TurtleOverlay;
import dan200.computercraft.data.client.BlockModelProvider;
import dan200.computercraft.data.client.ItemModelProvider;
@@ -21,9 +21,9 @@ import net.minecraft.client.data.models.ItemModelGenerators;
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
import net.minecraft.client.resources.model.AtlasIds;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.data.AtlasIds;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.registries.RegistryPatchGenerator;
@@ -71,9 +71,9 @@ public final class DataProviders {
generator.addFromCodec("Block atlases", PackOutput.Target.RESOURCE_PACK, "atlases", SpriteSources.FILE_CODEC, out -> {
out.accept(AtlasIds.BLOCKS, makeSprites(Stream.of(
LecternPrintoutModel.TEXTURE,
LecternPocketModel.TEXTURE_NORMAL, LecternPocketModel.TEXTURE_ADVANCED,
LecternPocketModel.TEXTURE_COLOUR, LecternPocketModel.TEXTURE_FRAME, LecternPocketModel.TEXTURE_LIGHT
LecternPrintoutModelDefinitions.MATERIAL.texture(),
LecternPocketModel.MATERIAL_NORMAL.texture(), LecternPocketModel.MATERIAL_ADVANCED.texture(),
LecternPocketModel.MATERIAL_COLOUR.texture(), LecternPocketModel.MATERIAL_FRAME.texture(), LecternPocketModel.MATERIAL_LIGHT.texture()
)));
out.accept(AtlasIds.GUI, makeSprites(

View File

@@ -23,6 +23,7 @@ import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction;
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
@@ -92,7 +93,7 @@ class LootTableProvider {
private static void namedBlockDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
blockDrop(
add, wrapper,
LootItem.lootTableItem(wrapper.get()).apply(CopyNameFunction.copyName(CopyNameFunction.NameSource.BLOCK_ENTITY)),
LootItem.lootTableItem(wrapper.get()).apply(CopyNameFunction.copyName(new CopyNameFunction.Source(LootContextParams.BLOCK_ENTITY))),
ExplosionCondition.survivesExplosion()
);
}
@@ -100,7 +101,7 @@ class LootTableProvider {
private static void computerDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> block) {
blockDrop(
add, block,
LootItem.lootTableItem(block.get()).apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY)),
LootItem.lootTableItem(block.get()).apply(CopyComponentsFunction.copyComponentsFromBlockEntity(LootContextParams.BLOCK_ENTITY)),
AnyOfCondition.anyOf(
BlockNamedEntityLootCondition.BUILDER,
HasComputerIdLootCondition.BUILDER,

View File

@@ -447,7 +447,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
}
private static ItemStack playerHead(String name, String uuid) {
return DataComponentUtil.createStack(Items.PLAYER_HEAD, DataComponents.PROFILE, new ResolvableProfile(new GameProfile(UUID.fromString(uuid), name)));
return DataComponentUtil.createStack(Items.PLAYER_HEAD, DataComponents.PROFILE, ResolvableProfile.createResolved(new GameProfile(UUID.fromString(uuid), name)));
}
private ShapedSpecBuilder customShaped(RecipeCategory category, ItemStack result) {

View File

@@ -50,14 +50,20 @@ 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")
: player != null && server.isSingleplayerOwner(player.getGameProfile());
: player != null && server.isSingleplayerOwner(player.nameAndId());
}
public static boolean isOwner(ServerPlayer player) {
var server = player.getServer();
return server != null && server.isSingleplayerOwner(player.getGameProfile());
var server = player.level().getServer();
return server != null && server.isSingleplayerOwner(player.nameAndId());
}
}

View File

@@ -50,7 +50,7 @@ public abstract class HorizontalContainerBlock extends BaseEntityBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
if (level.isClientSide) return InteractionResult.SUCCESS;
if (level.isClientSide()) return InteractionResult.SUCCESS;
if (level.getBlockEntity(pos) instanceof BaseContainerBlockEntity container) {
player.openMenu(container);
@@ -65,7 +65,7 @@ public abstract class HorizontalContainerBlock extends BaseEntityBlock {
}
@Override
protected final int getAnalogOutputSignal(BlockState pBlockState, Level pLevel, BlockPos pPos) {
protected final int getAnalogOutputSignal(BlockState pBlockState, Level pLevel, BlockPos pPos, Direction direction) {
return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(pLevel.getBlockEntity(pPos));
}

View File

@@ -112,7 +112,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
if (!player.isCrouching() && level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) {
// Regular right click to activate computer
if (!level.isClientSide && computer.isUsable(player)) {
if (!level.isClientSide() && computer.isUsable(player)) {
var serverComputer = computer.createServerComputer();
serverComputer.turnOn();
@@ -143,7 +143,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
@Override
@Nullable
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
return level.isClientSide ? null : BlockEntityHelpers.createTickerHelper(type, this.type.get(), serverTicker);
return level.isClientSide() ? null : BlockEntityHelpers.createTickerHelper(type, this.type.get(), serverTicker);
}
@Nullable

View File

@@ -67,7 +67,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
}
protected void unload() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
var computer = getServerComputer();
if (computer != null) computer.close();
@@ -91,7 +91,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
}
protected void serverTick() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
if (computerID < 0 && !startOn) return; // Don't tick if we don't need a computer!
var computer = createServerComputer();
@@ -157,7 +157,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
@Override
public final void loadAdditional(ValueInput nbt) {
super.loadAdditional(nbt);
if (level != null && level.isClientSide) {
if (level != null && level.isClientSide()) {
loadClient(nbt);
} else {
loadServer(nbt);
@@ -340,14 +340,14 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
}
public final void setComputerID(int id) {
if (getLevel().isClientSide || computerID == id) return;
if (getLevel().isClientSide() || computerID == id) return;
computerID = id;
BlockEntityHelpers.updateBlock(this);
}
public final void setLabel(@Nullable String label) {
if (getLevel().isClientSide || Objects.equals(this.label, label)) return;
if (getLevel().isClientSide() || Objects.equals(this.label, label)) return;
this.label = label;
var computer = getServerComputer();
@@ -386,7 +386,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
@Nullable
public ServerComputer getServerComputer() {
return getLevel().isClientSide || getLevel().getServer() == null ? null : ServerContext.get(getLevel().getServer()).registry().get(instanceID);
return getLevel().isClientSide() || getLevel().getServer() == null ? null : ServerContext.get(getLevel().getServer()).registry().get(instanceID);
}
// Networking stuff

View File

@@ -45,7 +45,7 @@ public enum ComputerFamily {
}
private static boolean checkCommandUsable(Player player) {
var server = player.getServer();
var server = player.level().getServer();
if (server == null || !server.isCommandBlockEnabled()) {
player.displayClientMessage(Component.translatable("advMode.notEnabled"), true);
return false;

View File

@@ -5,11 +5,14 @@
package dan200.computercraft.shared.container;
import net.minecraft.world.Container;
import net.minecraft.world.entity.ContainerUser;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
@@ -69,12 +72,12 @@ public interface InventoryDelegate extends Container {
}
@Override
default void startOpen(Player player) {
default void startOpen(ContainerUser player) {
getInventory().startOpen(player);
}
@Override
default void stopOpen(Player player) {
default void stopOpen(ContainerUser player) {
getInventory().stopOpen(player);
}
@@ -102,4 +105,24 @@ public interface InventoryDelegate extends Container {
default boolean hasAnyMatching(Predicate<ItemStack> predicate) {
return getInventory().hasAnyMatching(predicate);
}
@Override
default int getMaxStackSize(ItemStack stack) {
return getInventory().getMaxStackSize(stack);
}
@Override
default List<ContainerUser> getEntitiesWithContainerOpen() {
return getInventory().getEntitiesWithContainerOpen();
}
@Override
default boolean canTakeItem(Container target, int slot, ItemStack stack) {
return getInventory().canTakeItem(target, slot, stack);
}
@Override
default Iterator<ItemStack> iterator() {
return getInventory().iterator();
}
}

View File

@@ -9,6 +9,7 @@ import dan200.computercraft.shared.media.items.PrintoutItem;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.util.BlockEntityHelpers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.stats.Stats;
import net.minecraft.util.RandomSource;
@@ -55,7 +56,7 @@ public class CustomLecternBlock extends LecternBlock {
*/
public static InteractionResult tryPlaceItem(Player player, Level level, BlockPos pos, BlockState blockState, ItemStack item) {
if (item.getItem() instanceof PrintoutItem || item.getItem() instanceof PocketComputerItem) {
if (!level.isClientSide) replaceLectern(player, level, pos, blockState, item);
if (!level.isClientSide()) replaceLectern(player, level, pos, blockState, item);
return InteractionResult.SUCCESS;
}
@@ -129,13 +130,13 @@ public class CustomLecternBlock extends LecternBlock {
}
@Override
public int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos, Direction direction) {
return level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern ? lectern.getRedstoneSignal() : 0;
}
@Override
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
if (!level.isClientSide && level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
if (!level.isClientSide() && level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
if (player.isSecondaryUseActive()) {
// When shift+clicked with an empty hand, drop the item and replace with the normal lectern.
clearLectern(level, pos, state);
@@ -152,7 +153,7 @@ public class CustomLecternBlock extends LecternBlock {
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
return level.isClientSide ? null : BlockEntityHelpers.createTickerHelper(type, ModRegistry.BlockEntities.LECTERN.get(), serverTicker);
return level.isClientSide() ? null : BlockEntityHelpers.createTickerHelper(type, ModRegistry.BlockEntities.LECTERN.get(), serverTicker);
}
private static final BlockEntityTicker<CustomLecternBlockEntity> serverTicker = (level, pos, state, lectern) -> lectern.tick();

View File

@@ -23,7 +23,7 @@ public class PrintoutItem extends Item {
@Override
public InteractionResult use(Level world, Player player, InteractionHand hand) {
var stack = player.getItemInHand(hand);
if (!world.isClientSide) {
if (!world.isClientSide()) {
var title = PrintoutData.getOrEmpty(stack).title();
var displayTitle = Strings.isNullOrEmpty(title) ? stack.getDisplayName() : Component.literal(title);
player.openMenu(new SimpleMenuProvider((id, playerInventory, p) -> PrintoutMenu.createInHand(id, p, hand), displayTitle));

View File

@@ -58,7 +58,7 @@ public class DiskDriveBlock extends HorizontalContainerBlock {
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()) {
if (!level.isClientSide() && level.getBlockEntity(blockPos) instanceof DiskDriveBlockEntity drive && drive.getDiskStack().isEmpty()) {
drive.setDiskStack(context.getItemInHand().split(1));
}
return InteractionResult.SUCCESS;
@@ -76,6 +76,6 @@ public class DiskDriveBlock extends HorizontalContainerBlock {
@Override
@Nullable
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
return level.isClientSide ? null : BaseEntityBlock.createTickerHelper(type, ModRegistry.BlockEntities.DISK_DRIVE.get(), serverTicker);
return level.isClientSide() ? null : BaseEntityBlock.createTickerHelper(type, ModRegistry.BlockEntities.DISK_DRIVE.get(), serverTicker);
}
}

View File

@@ -162,7 +162,7 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity imp
@Override
public void setChanged() {
if (level != null && !level.isClientSide) updateMedia();
if (level != null && !level.isClientSide()) updateMedia();
super.setChanged();
}
@@ -357,7 +357,7 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity imp
}
private void ejectContents() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
var stack = getDiskStack();
if (stack.isEmpty()) return;

View File

@@ -40,7 +40,7 @@ public abstract class AbstractEnergyMethods<T> implements GenericPeripheral {
* @return The energy stored in this block, in FE.
*/
@LuaFunction(mainThread = true)
public abstract int getEnergy(T energy);
public abstract long getEnergy(T energy);
/**
* Get the maximum amount of energy this block can store.
@@ -49,5 +49,5 @@ public abstract class AbstractEnergyMethods<T> implements GenericPeripheral {
* @return The energy capacity of this block.
*/
@LuaFunction(mainThread = true)
public abstract int getEnergyCapacity(T energy);
public abstract long getEnergyCapacity(T energy);
}

View File

@@ -102,7 +102,7 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
return false;
}
return world.setBlock(pos, fluid.createLegacyBlock(), world.isClientSide ? UPDATE_ALL_IMMEDIATE : UPDATE_ALL);
return world.setBlock(pos, fluid.createLegacyBlock(), world.isClientSide() ? UPDATE_ALL_IMMEDIATE : UPDATE_ALL);
}
public boolean onCustomDestroyBlock(BlockState state, Level world, BlockPos pos, Player player) {
@@ -130,7 +130,7 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
world.setBlockAndUpdate(pos, correctConnections(world, pos, newState));
cable.connectionsChanged();
if (!world.isClientSide && !player.getAbilities().instabuild) {
if (!world.isClientSide() && !player.getAbilities().instabuild) {
Block.popResource(world, pos, item);
}

View File

@@ -80,7 +80,7 @@ public class CableBlockEntity extends BlockEntity {
public void setRemoved() {
super.setRemoved();
modem.removed();
if (level == null || !level.isClientSide) node.remove();
if (level == null || !level.isClientSide()) node.remove();
}
@Override
@@ -108,7 +108,7 @@ public class CableBlockEntity extends BlockEntity {
void neighborChanged() {
var dir = getModemDirection();
if (!getLevel().isClientSide && dir != null && isPeripheralOn()) queueRefreshPeripheral();
if (!getLevel().isClientSide() && dir != null && isPeripheralOn()) queueRefreshPeripheral();
}
void queueRefreshPeripheral() {
@@ -119,7 +119,7 @@ public class CableBlockEntity extends BlockEntity {
InteractionResult use(Player player) {
if (!canAttachPeripheral()) return InteractionResult.FAIL;
if (getLevel().isClientSide) return InteractionResult.SUCCESS;
if (getLevel().isClientSide()) return InteractionResult.SUCCESS;
var oldName = peripheral.getConnectedName();
if (isPeripheralOn()) {
@@ -167,7 +167,7 @@ public class CableBlockEntity extends BlockEntity {
}
void blockTick() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
if (refreshPeripheral) {
refreshPeripheral = false;
@@ -185,7 +185,7 @@ public class CableBlockEntity extends BlockEntity {
}
void connectionsChanged() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
refreshConnections = false;
var state = getBlockState();

View File

@@ -97,7 +97,7 @@ public class WiredModemFullBlockEntity extends BlockEntity {
for (var modem : modems) {
if (modem != null) modem.removed();
}
if (level == null || !level.isClientSide) node.remove();
if (level == null || !level.isClientSide()) node.remove();
}
@Override
@@ -120,7 +120,7 @@ public class WiredModemFullBlockEntity extends BlockEntity {
public InteractionResult use(Player player) {
if (player.isCrouching() || !player.mayBuild()) return InteractionResult.PASS;
if (getLevel().isClientSide) return InteractionResult.SUCCESS;
if (getLevel().isClientSide()) return InteractionResult.SUCCESS;
// On server, we interacted if a peripheral was found
var oldPeriphNames = getConnectedPeripheralNames();
@@ -167,7 +167,7 @@ public class WiredModemFullBlockEntity extends BlockEntity {
}
void blockTick() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
if (invalidSides != 0) {
var oldInvalidSides = invalidSides;
@@ -194,7 +194,7 @@ public class WiredModemFullBlockEntity extends BlockEntity {
}
private void connectionsChanged() {
if (getLevel().isClientSide) return;
if (getLevel().isClientSide()) return;
refreshConnections = false;
var world = getLevel();

View File

@@ -95,7 +95,7 @@ public class MonitorBlock extends HorizontalDirectionalBlock implements EntityBl
return InteractionResult.PASS;
}
if (!level.isClientSide) {
if (!level.isClientSide()) {
monitor.monitorTouched(
(float) (hit.getLocation().x - hit.getBlockPos().getX()),
(float) (hit.getLocation().y - hit.getBlockPos().getY()),
@@ -111,7 +111,7 @@ public class MonitorBlock extends HorizontalDirectionalBlock implements EntityBl
super.setPlacedBy(world, pos, blockState, livingEntity, itemStack);
var entity = world.getBlockEntity(pos);
if (entity instanceof MonitorBlockEntity monitor && !world.isClientSide) {
if (entity instanceof MonitorBlockEntity monitor && !world.isClientSide()) {
// Defer the block update if we're being placed by another TE. See #691
if (livingEntity == null || (livingEntity instanceof ServerPlayer player && PlatformHelper.get().isFakePlayer(player))) {
monitor.updateNeighborsDeferred();

View File

@@ -99,7 +99,7 @@ public class MonitorBlockEntity extends BlockEntity {
public void preRemoveSideEffects(BlockPos blockPos, BlockState blockState) {
super.preRemoveSideEffects(blockPos, blockState);
isRemoving = true;
if (level != null && !getLevel().isClientSide) contractNeighbours();
if (level != null && !getLevel().isClientSide()) contractNeighbours();
}
@Override
@@ -129,7 +129,7 @@ public class MonitorBlockEntity extends BlockEntity {
width = nbt.getIntOr(NBT_WIDTH, 1);
height = nbt.getIntOr(NBT_HEIGHT, 1);
if (level != null && level.isClientSide) onClientLoad(oldXIndex, oldYIndex);
if (level != null && level.isClientSide()) onClientLoad(oldXIndex, oldYIndex);
}
void blockTick() {

View File

@@ -1,32 +0,0 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.peripheral.monitor;
/**
* The render type to use for monitors.
*
* @see dan200.computercraft.client.render.TileEntityMonitorRenderer
* @see ClientMonitor
*/
public enum MonitorRenderer {
/**
* Determine the best monitor backend.
*/
BEST,
/**
* Render using texture buffer objects.
*
* @see org.lwjgl.opengl.GL31#glTexBuffer(int, int, int)
*/
TBO,
/**
* Render using VBOs.
*
* @see com.mojang.blaze3d.vertex.VertexBuffer
*/
VBO,
}

View File

@@ -50,7 +50,7 @@ public class SpeakerBlock extends HorizontalDirectionalBlock implements EntityBl
@Override
@Nullable
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
return level.isClientSide ? null : BlockEntityHelpers.createTickerHelper(type, ModRegistry.BlockEntities.SPEAKER.get(), serverTicker);
return level.isClientSide() ? null : BlockEntityHelpers.createTickerHelper(type, ModRegistry.BlockEntities.SPEAKER.get(), serverTicker);
}
@Nullable

View File

@@ -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;
@@ -30,7 +31,7 @@ public class SpeakerBlockEntity extends BlockEntity {
@Override
public void setRemoved() {
super.setRemoved();
if (level != null && !level.isClientSide) {
if (level != null && !level.isClientSide()) {
ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(peripheral.getSource()), Nullability.assertNonNull(getLevel().getServer()));
}
}
@@ -47,7 +48,12 @@ 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()));
}

View File

@@ -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,7 +260,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
// Prevent playing music discs.
var soundEvent = BuiltInRegistries.SOUND_EVENT.getValue(identifier);
// TODO: Build a set of sound events at server startup, and cache this.
var level = Objects.requireNonNull(getPosition().level());
var level = getLevel();
if (soundEvent != null && level.registryAccess().lookupOrThrow(Registries.JUKEBOX_SONG).stream().anyMatch(x -> x.soundEvent().value() == soundEvent)) {
return false;
}

View File

@@ -7,7 +7,6 @@ package dan200.computercraft.shared.peripheral.speaker;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
import dan200.computercraft.shared.network.server.ServerNetworking;
import net.minecraft.server.level.ServerLevel;
/**
@@ -16,8 +15,6 @@ import net.minecraft.server.level.ServerLevel;
public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral {
public static final String ADJECTIVE = "upgrade.computercraft.speaker.adjective";
protected abstract ServerLevel getLevel();
@Override
public void detach(IComputerAccess computer) {
super.detach(computer);

View File

@@ -111,7 +111,7 @@ public class PocketComputerItem extends Item {
@ForgeOverride
public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) {
var level = entity.level();
if (level.isClientSide || level.getServer() == null) return false;
if (level.isClientSide() || level.getServer() == null) return false;
// If we're an item entity, tick an already existing computer (as to update the position), but do not keep the
// computer alive.
@@ -123,7 +123,7 @@ public class PocketComputerItem extends Item {
@Override
public InteractionResult use(Level world, Player player, InteractionHand hand) {
var stack = player.getItemInHand(hand);
if (!world.isClientSide) {
if (!world.isClientSide()) {
var holder = new PocketHolder.PlayerHolder((ServerPlayer) player, InventoryUtil.getHandSlot(player, hand));
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
var computer = brain.computer();

View File

@@ -11,10 +11,10 @@ import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.server.level.ServerLevel;
import org.jspecify.annotations.Nullable;
public final class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
final class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
private final IPocketAccess access;
public PocketSpeakerPeripheral(IPocketAccess access) {
PocketSpeakerPeripheral(IPocketAccess access) {
this.access = access;
}
@@ -24,7 +24,7 @@ public final class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
}
@Override
public SpeakerPosition getPosition() {
protected SpeakerPosition getPosition() {
var entity = access.getEntity();
return entity == null ? SpeakerPosition.of(access.getLevel(), access.getPosition()) : SpeakerPosition.of(entity);
}

View File

@@ -125,7 +125,7 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity entity, ItemStack stack) {
super.setPlacedBy(level, pos, state, entity, stack);
if (!level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle && entity instanceof Player player) {
if (!level.isClientSide() && level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle && entity instanceof Player player) {
turtle.setOwningPlayer(player.getGameProfile());
}
}
@@ -134,7 +134,7 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
protected InteractionResult useItemOn(ItemStack currentItem, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (currentItem.getItem() == Items.NAME_TAG && currentItem.has(DataComponents.CUSTOM_NAME) && level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) {
// Label to rename computer
if (!level.isClientSide) {
if (!level.isClientSide()) {
computer.setLabel(currentItem.getHoverName().getString());
currentItem.shrink(1);
}
@@ -157,6 +157,6 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
@Override
@Nullable
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
return level.isClientSide ? BlockEntityHelpers.createTickerHelper(type, this.type.get(), clientTicker) : super.getTicker(level, state, type);
return level.isClientSide() ? BlockEntityHelpers.createTickerHelper(type, this.type.get(), clientTicker) : super.getTicker(level, state, type);
}
}

View File

@@ -6,7 +6,6 @@ package dan200.computercraft.shared.turtle.core;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.api.lua.ILuaCallback;
import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.peripheral.IPeripheral;
@@ -28,7 +27,6 @@ import dan200.computercraft.shared.util.Holiday;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.UUIDUtil;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.resources.ResourceLocation;
@@ -65,15 +63,7 @@ public class TurtleBrain implements TurtleAccessInternal {
private static final String NBT_SLOT = "Slot";
/**
* {@link net.minecraft.world.item.component.ResolvableProfile#CODEC}, but resolving to a {@link GameProfile}
* directly. We don't use {@link ExtraCodecs#GAME_PROFILE}, as that encodes the UUID as a string, not an int array.
*/
private static final Codec<GameProfile> GAME_PROFILE_CODEC = RecordCodecBuilder.create(instance -> instance.group(
UUIDUtil.CODEC.fieldOf("id").forGetter(GameProfile::getId),
ExtraCodecs.PLAYER_NAME.fieldOf("name").forGetter(GameProfile::getName)
)
.apply(instance, GameProfile::new));
private static final Codec<GameProfile> GAME_PROFILE_CODEC = ExtraCodecs.STORED_GAME_PROFILE.codec();
private static final int ANIM_DURATION = 8;
@@ -121,7 +111,7 @@ public class TurtleBrain implements TurtleAccessInternal {
public void update() {
var world = getLevel();
if (!world.isClientSide) {
if (!world.isClientSide()) {
// Advance movement
updateCommands();
@@ -221,7 +211,7 @@ public class TurtleBrain implements TurtleAccessInternal {
@Override
public boolean teleportTo(Level world, BlockPos pos) {
if (world.isClientSide || getLevel().isClientSide) {
if (world.isClientSide() || getLevel().isClientSide()) {
throw new UnsupportedOperationException("Cannot teleport on the client");
}
@@ -335,7 +325,7 @@ public class TurtleBrain implements TurtleAccessInternal {
@Override
public void setSelectedSlot(int slot) {
if (getLevel().isClientSide) throw new UnsupportedOperationException("Cannot set the slot on the client");
if (getLevel().isClientSide()) throw new UnsupportedOperationException("Cannot set the slot on the client");
if (slot >= 0 && slot < owner.getContainerSize()) {
selectedSlot = slot;
@@ -371,7 +361,7 @@ public class TurtleBrain implements TurtleAccessInternal {
@Override
public boolean consumeFuel(int fuel) {
if (getLevel().isClientSide) throw new UnsupportedOperationException("Cannot consume fuel on the client");
if (getLevel().isClientSide()) throw new UnsupportedOperationException("Cannot consume fuel on the client");
if (!isFuelNeeded()) return true;
@@ -385,7 +375,7 @@ public class TurtleBrain implements TurtleAccessInternal {
@Override
public void addFuel(int fuel) {
if (getLevel().isClientSide) throw new UnsupportedOperationException("Cannot add fuel on the client");
if (getLevel().isClientSide()) throw new UnsupportedOperationException("Cannot add fuel on the client");
var addition = Math.max(fuel, 0);
setFuelLevel(getFuelLevel() + addition);
@@ -393,7 +383,7 @@ public class TurtleBrain implements TurtleAccessInternal {
@Override
public MethodResult executeCommand(TurtleCommand command) {
if (getLevel().isClientSide) throw new UnsupportedOperationException("Cannot run commands on the client");
if (getLevel().isClientSide()) throw new UnsupportedOperationException("Cannot run commands on the client");
if (commandQueue.size() > 16) return MethodResult.of(false, "Too many ongoing turtle commands");
commandQueue.offer(new TurtleCommandQueueEntry(++commandsIssued, command));
@@ -403,7 +393,7 @@ public class TurtleBrain implements TurtleAccessInternal {
@Override
public void playAnimation(TurtleAnimation animation) {
if (getLevel().isClientSide) throw new UnsupportedOperationException("Cannot play animations on the client");
if (getLevel().isClientSide()) throw new UnsupportedOperationException("Cannot play animations on the client");
this.animation = animation;
if (this.animation == TurtleAnimation.SHORT_WAIT) {
@@ -489,7 +479,9 @@ public class TurtleBrain implements TurtleAccessInternal {
instance.setUpgrade(upgrade);
// Create peripherals
if (owner.getLevel() != null && !owner.getLevel().isClientSide) updatePeripherals(owner.createServerComputer());
if (owner.getLevel() != null && !owner.getLevel().isClientSide()) {
updatePeripherals(owner.createServerComputer());
}
return true;
}
@@ -681,7 +673,7 @@ public class TurtleBrain implements TurtleAccessInternal {
}
// Advance valentines day easter egg
if (world.isClientSide && animation == TurtleAnimation.MOVE_FORWARD && animationProgress == 4) {
if (world.isClientSide() && animation == TurtleAnimation.MOVE_FORWARD && animationProgress == 4) {
// Spawn love pfx if valentines day
var currentHoliday = Holiday.getCurrent();
if (currentHoliday == Holiday.VALENTINES) {

View File

@@ -57,7 +57,7 @@ public class TurtleItem extends BlockItem {
public static final CauldronInteraction CAULDRON_INTERACTION = (blockState, level, pos, player, hand, stack) -> {
if (!stack.has(DataComponents.DYED_COLOR)) return InteractionResult.TRY_WITH_EMPTY_HAND;
if (!level.isClientSide) {
if (!level.isClientSide()) {
stack.remove(DataComponents.DYED_COLOR);
LayeredCauldronBlock.lowerFillLevel(blockState, level, pos);
}

View File

@@ -80,7 +80,7 @@ public class TurtleModem extends AbstractTurtleUpgrade {
@Override
public void update(ITurtleAccess turtle, TurtleSide side) {
// Advance the modem
if (!turtle.getLevel().isClientSide) {
if (!turtle.getLevel().isClientSide()) {
var peripheral = turtle.getPeripheral(side);
if (peripheral instanceof Peripheral modem) {
var state = modem.getModemState();

View File

@@ -32,7 +32,7 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade {
}
@Override
public SpeakerPosition getPosition() {
protected SpeakerPosition getPosition() {
return SpeakerPosition.of(turtle.getLevel(), Vec3.atCenterOf(turtle.getPosition()));
}

View File

@@ -29,6 +29,7 @@ import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.decoration.ArmorStand;
@@ -218,7 +219,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
// If this is a projectile, attempt to deflect it instead.
if (entity.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entity instanceof Projectile projectile &&
projectile.deflect(ProjectileDeflection.AIM_DEFLECT, player, player, true)
projectile.deflect(ProjectileDeflection.AIM_DEFLECT, player, EntityReference.of(player), true)
) {
return true;
}

View File

@@ -56,7 +56,7 @@ public final class TickScheduler {
*/
public static void schedule(Token token) {
var world = token.owner.getLevel();
if (world != null && !world.isClientSide && Token.STATE.compareAndSet(token, State.IDLE, State.SCHEDULED)) {
if (world != null && !world.isClientSide() && Token.STATE.compareAndSet(token, State.IDLE, State.SCHEDULED)) {
toTick.add(token);
}
}

View File

@@ -0,0 +1,224 @@
{
"argument.computercraft.argument_expected": "Argumentum szükséges",
"argument.computercraft.computer.distance": "Távolság az entitástól",
"argument.computercraft.computer.family": "Számítógép típus",
"argument.computercraft.computer.id": "Számítógép ID",
"argument.computercraft.computer.instance": "Egyedi példányazonosító",
"argument.computercraft.computer.label": "Számítógép címke",
"argument.computercraft.computer.many_matching": "Több számítógép egyezik '%s' (példányok %s)",
"argument.computercraft.computer.no_matching": "Nincs egyező számítógép '%s'",
"argument.computercraft.tracking_field.no_field": "Ismeretlen mező '%s'",
"argument.computercraft.unknown_computer_family": "Ismeretlen számítógéptípus '%s'",
"block.computercraft.cable": "Hálózati kábel",
"block.computercraft.computer_advanced": "Fejlett számítógép",
"block.computercraft.computer_command": "Parancsszámítógép",
"block.computercraft.computer_normal": "Számítógép",
"block.computercraft.disk_drive": "Lemezmeghajtó",
"block.computercraft.monitor_advanced": "Fejlett monitor",
"block.computercraft.printer": "Nyomtató",
"block.computercraft.redstone_relay": "Redstone jelfogó",
"block.computercraft.speaker": "Hangszóró",
"block.computercraft.turtle_advanced": "Fejlett teknős",
"block.computercraft.turtle_advanced.upgraded": "Fejlett %s teknős",
"block.computercraft.turtle_advanced.upgraded_twice": "Fejlett %s %s teknős",
"block.computercraft.turtle_normal": "Teknős",
"block.computercraft.turtle_normal.upgraded": "%s teknős",
"block.computercraft.turtle_normal.upgraded_twice": "%s %s teknős",
"block.computercraft.wired_modem": "Vezetékes modem",
"block.computercraft.wired_modem_full": "Vezetékes modem",
"block.computercraft.wireless_modem_advanced": "Ender modem",
"block.computercraft.wireless_modem_normal": "Vezeték nélküli modem",
"chat.computercraft.wired_modem.peripheral_connected": "Periféria \"%s\" csatlakoztatva a hálózathoz",
"chat.computercraft.wired_modem.peripheral_disconnected": "Periféria \"%s\" lecsatlakoztatva a hálózatról",
"commands.computercraft.desc": "A /computercraft parancs különböző hibakereső és adminisztrációs eszközöket biztosít a számítógépek vezérléséhez és kezeléséhez.",
"commands.computercraft.dump.action": "További információ a számítógépről",
"commands.computercraft.dump.desc": "Az összes számítógép állapotát, vagy egy konkrét számítógépről szóló információkat jeleníti meg. Megadhatod a számítógép példányazonosítóját (pl. 123), számítógép azonosítóját (pl. #123) vagy címkéjét (pl. \"@Saját Számítógép\").",
"commands.computercraft.dump.open_path": "Nézd meg ennek a számítógépnek a fájljait",
"commands.computercraft.dump.synopsis": "Számítógépek állapotának megjelenítése.",
"commands.computercraft.generic.additional_rows": "%d további sor…",
"commands.computercraft.generic.exception": "Kezelhetetlen kivétel (%s)",
"commands.computercraft.generic.yes": "I",
"commands.computercraft.help.desc": "Megjeleníti ezt a súgóüzenetet",
"commands.computercraft.help.no_children": "%s nem rendelkezik alparancsokkal",
"commands.computercraft.help.no_command": "Nincs ilyen parancs '%s'",
"commands.computercraft.help.synopsis": "Súgó biztosítása egy adott parancshoz",
"commands.computercraft.queue.desc": "Számítógép_parancs eseményt küld egy parancsszámítógépnek, további argumentumok továbbításával. Ez főleg térképkészítők számára készült, mint egy számítógépbarátabb változata a /trigger parancsnak. Bármelyik játékos futtathatja a parancsot, amit valószínűleg egy szövegelem kattintási eseményén keresztül végeznek.",
"commands.computercraft.queue.synopsis": "Számítógép_parancs esemény küldése egy parancsszámítógépnek",
"commands.computercraft.shutdown.desc": "Leállítja a megadott számítógépeket, vagy mindet, ha nincs megadva. Megadhatod a számítógép példányazonosítóját (pl. 123), számítógép azonosítóját (pl. #123) vagy címkéjét (pl. \"@Saját Számítógép\").",
"commands.computercraft.shutdown.done": "%s/%s számítógép leállítva",
"commands.computercraft.shutdown.synopsis": "Számítógépek távoli leállítása.",
"commands.computercraft.synopsis": "Különféle parancsok számítógépek vezérlésére.",
"commands.computercraft.tp.action": "Teleportálj ehhez a számítógéphez",
"commands.computercraft.tp.desc": "Teleportálj egy számítógép helyére. Megadhatod a számítógép példányazonosítóját (pl. 123) vagy számítógép azonosítóját (pl. #123).",
"commands.computercraft.tp.synopsis": "Teleportálás egy adott számítógéphez.",
"commands.computercraft.track.desc": "Követi, mennyi ideig futnak a számítógépek, valamint hány eseményt kezelnek. Ez hasonló információkat nyújt, mint a /forge track, és hasznos lehet a késés diagnosztizálásában.",
"commands.computercraft.track.dump.computer": "Számítógép",
"commands.computercraft.track.dump.desc": "A számítógépes követés legfrissebb eredményeinek megjelenítése.",
"commands.computercraft.track.dump.no_timings": "Nincsenek elérhető időzítések",
"commands.computercraft.track.dump.synopsis": "A legfrissebb követési eredmények megjelenítése",
"commands.computercraft.track.start.desc": "Minden számítógép végrehajtási idejének és eseményszámának követését indítja. Ezzel az előző futások eredményei elvesznek.",
"commands.computercraft.track.start.stop": "Futtasd a %s parancsot a követés leállításához és az eredmények megtekintéséhez",
"commands.computercraft.track.start.synopsis": "Minden számítógép követésének indítása",
"commands.computercraft.track.stop.action": "Kattints a követés leállításához",
"commands.computercraft.track.stop.desc": "Minden számítógépes esemény és végrehajtási idő követésének leállítása",
"commands.computercraft.track.stop.not_enabled": "A számítógépek jelenleg nem követhetők",
"commands.computercraft.track.stop.synopsis": "Minden számítógép követésének leállítása",
"commands.computercraft.track.synopsis": "Számítógépek végrehajtási idejének követése.",
"commands.computercraft.turn_on.desc": "Kapcsold be a megadott számítógépeket. Megadhatod a számítógép példányazonosítóját (pl. 123), számítógép azonosítóját (pl. #123) vagy címkéjét (pl. \"@Saját Számítógép\").",
"commands.computercraft.turn_on.done": "%s/%s számítógép bekapcsolva",
"commands.computercraft.turn_on.synopsis": "Számítógépek távoli bekapcsolása.",
"commands.computercraft.view.action": "Nézd meg ezt a számítógépet",
"commands.computercraft.view.desc": "Nyisd meg egy számítógép terminálját, amely lehetővé teszi a távoli vezérlést. Ez nem biztosít hozzáférést a teknősök leltárához. Megadhatod a számítógép példányazonosítóját (pl. 123) vagy számítógép azonosítóját (pl. #123).",
"commands.computercraft.view.not_player": "Nem lehet terminált nyitni nem-játékos entitáshoz",
"commands.computercraft.view.synopsis": "Számítógép termináljának megtekintése.",
"gui.computercraft.config.command_require_creative": "Parancsszámítógépekhez kreatív mód szükséges",
"gui.computercraft.config.command_require_creative.tooltip": "Szükséges, hogy a játékosok kreatív módban legyenek és rendelkezzenek adminisztrátori jogosultsággal a parancsszámítógépek használatához. Ez az alapértelmezett viselkedés a Minecraft parancsblokkjaihoz.",
"gui.computercraft.config.computer_space_limit": "Számítógép tárhelykorlát (byte-ban)",
"gui.computercraft.config.computer_space_limit.tooltip": "A számítógépek és teknősök lemezterületének korlátja, byte-ban.",
"gui.computercraft.config.default_computer_settings": "Alapértelmezett számítógép-beállítások",
"gui.computercraft.config.default_computer_settings.tooltip": "Alapértelmezett rendszerbeállítások vesszővel elválasztott listája, amelyek az új számítógépekre vonatkoznak.\nPélda: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\" letiltja az automatikus kiegészítést.",
"gui.computercraft.config.disabled_generic_methods": "Letiltott általános metódusok",
"gui.computercraft.config.disabled_generic_methods.tooltip": "Általános metódusok vagy metódusforrások listája, amelyek le vannak tiltva. Az általános metódusokat akkor adják hozzá egy blokkhoz vagy blokk entitáshoz, ha nincs kifejezetten hozzárendelt periféria-szolgáltató. Ez magában foglalja a leltár metódusokat (pl. inventory.getItemDetail, inventory.pushItems), valamint (Forge esetén) a fluid_storage és energy_storage metódusokat is.\nA listában lévő metódus lehet egy teljes metóduscsoport (computercraft:inventory), vagy egyetlen metódus (computercraft:inventory#pushItems).",
"gui.computercraft.config.execution": "Végrehajtás",
"gui.computercraft.config.execution.computer_threads": "Számítógép szálak",
"gui.computercraft.config.execution.computer_threads.tooltip": "Az egyidejűleg futó számítógépek számát szabályozza. A magasabb szám több számítógép egyidejű futását teszi lehetővé, de lassulást okozhat. Vegye figyelembe, hogy néhány mod nem működik, ha a szálak száma meghaladja az 1-et. Óvatosan használja.\nTartomány: > 1",
"gui.computercraft.config.execution.max_main_computer_time": "Szerver tick számítógépidő-korlát",
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "Az ideális maximális idő, ameddig egy számítógép egy tick-ben futtathat, milliszekundumban. Megjegyzés: Lehetséges, hogy túllépjük ezt a határt, mivel nincs mód az idő pontos előrejelzésére - ez az átlagos felső határ.",
"gui.computercraft.config.execution.max_main_global_time": "Szerver tick globális időkorlát",
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Az egy tick-ben feladatok végrehajtására fordított maximális idő, milliszekundumban.",
"gui.computercraft.config.execution.tooltip": "A számítógépek végrehajtási viselkedését szabályozza. Ez elsősorban szerverek finomhangolására szolgál, és általában nem szükséges megváltoztatni.",
"gui.computercraft.config.floppy_space_limit": "Floppy lemez tárhelykorlát (byte-ban)",
"gui.computercraft.config.floppy_space_limit.tooltip": "A floppy lemezek lemezterületének korlátja, byte-ban.",
"gui.computercraft.config.http.bandwidth": "Sávszélesség",
"gui.computercraft.config.http.bandwidth.global_download": "Globális letöltési korlát",
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Az egy másodperc alatt letölthető byte-ok száma, amelyet minden számítógép oszt meg. (byte/s).\nTartomány: > 1",
"gui.computercraft.config.http.bandwidth.global_upload": "Globális feltöltési korlát",
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Az egy másodperc alatt feltölthető byte-ok száma, amelyet minden számítógép oszt meg. (byte/s).\nTartomány: > 1",
"gui.computercraft.config.http.bandwidth.tooltip": "A számítógépek által használt sávszélességet korlátozza.",
"gui.computercraft.config.http.enabled": "HTTP API engedélyezése",
"gui.computercraft.config.http.enabled.tooltip": "Az \"http\" API engedélyezése a számítógépeken. Ha letiltjuk, akkor az \"http\", \"pastebin\" és \"wget\" programok is le lesznek tiltva, amelyeket sok felhasználó használ. Ajánlott engedélyezve hagyni, és a \"rules\" beállítás használatával finomhangolni a szabályozást.",
"gui.computercraft.config.http.max_requests": "Maximális egyidejű kérések",
"gui.computercraft.config.http.max_requests.tooltip": "Az egyszerre elküldhető http kérések száma egy számítógépen. A további kérések sorba állnak, és akkor kerülnek elküldésre, amikor a futó kérések befejeződtek. 0-ra állítva nincs korlátozás.\nTartomány: > 0",
"gui.computercraft.config.http.max_websockets": "Maximális egyidejű websockets",
"gui.computercraft.config.http.max_websockets.tooltip": "Az egyszerre megnyitható websockets száma egy számítógépen.\nTartomány: > 1",
"gui.computercraft.config.http.proxy.host": "Hosztnév",
"gui.computercraft.config.http.proxy.host.tooltip": "A proxy szerver hosztneve vagy IP-címe.",
"gui.computercraft.config.http.proxy.port.tooltip": "A proxy szerver portja.\nTartomány: 1 ~ 65536",
"gui.computercraft.config.http.proxy.tooltip": "Az HTTP és websocket kérések proxy szerveren keresztüli alagútba helyezése. Csak az \"use_proxy\" igazra állított HTTP szabályok esetén hatékony (alapértelmezésben kikapcsolva).",
"gui.computercraft.config.http.proxy.type": "Proxy típus",
"gui.computercraft.config.http.proxy.type.tooltip": "A használni kívánt proxy típus.\nMegengedett értékek: HTTP, HTTPS, SOCKS4, SOCKS5",
"gui.computercraft.config.http.rules": "Engedélyezés/tiltás szabályok",
"gui.computercraft.config.http.rules.tooltip": "Szabályok listája, amelyek szabályozzák az \"http\" API viselkedését adott domainek vagy IP-k számára. Minden szabály egy hosztnévre és egy opcionális portra vonatkozik, majd több tulajdonságot állít be a kéréshez. A szabályok sorrendben kerülnek értékelésre, így az előbbi szabályok felülírják a későbbieket.\n\nÉrvényes tulajdonságok:\n - \"host\" (kötelező): Az a domain vagy IP-cím, amelyre ez a szabály vonatkozik. Ez lehet domain név (\"pastebin.com\"), helyettesítő karakter (\"*.pastebin.com\") vagy CIDR jelölés (\"127.0.0.0/8\").\n - \"port\" (opcionális): Csak adott porttal rendelkező kérésekre vonatkozik, mint például 80 vagy 443.\n\n - \"action\" (opcionális): Azt határozza meg, hogy engedélyezett vagy tiltott legyen-e a kérés.\n - \"max_download\" (opcionális): A maximális méret (byte-ban), amit egy számítógép letölthet ezen a kérésen keresztül.\n - \"max_upload\" (opcionális): A maximális méret (byte-ban), amit egy számítógép feltölthet ezen a kérésen keresztül.\n - \"max_websocket_message\" (opcionális): A maximális méret (byte-ban), amit egy számítógép egy websocket csomagon keresztül küldhet vagy fogadhat.\n - \"use_proxy\" (opcionális): Proxy vagy HTTP/SOCKS proxy használata, ha be van állítva.",
"gui.computercraft.config.http.tooltip": "A HTTP API szabályozása",
"gui.computercraft.config.http.websocket_enabled": "Websockets engedélyezése",
"gui.computercraft.config.http.websocket_enabled.tooltip": "A websockets használatának engedélyezése az http segítségével. Ehhez az \"http_enable\" opciónak is igaznak kell lennie.",
"gui.computercraft.config.log_computer_errors": "Számítógép hibáinak naplózása",
"gui.computercraft.config.log_computer_errors.tooltip": "A perifériák és más Lua objektumok által kiváltott kivételek naplózása. Ez segíthet a mod készítőknek a hibák megoldásában, de túl sok naplózott hiba esetén log-túltelítettséget eredményezhet.",
"gui.computercraft.config.maximum_open_files": "Maximálisan megnyitható fájlok száma számítógépenként",
"gui.computercraft.config.maximum_open_files.tooltip": "Beállítja, hogy egy számítógép hány fájlt nyithat meg egyszerre. 0-ra állítva nincs korlátozás.\nTartomány: > 0",
"gui.computercraft.config.monitor_distance": "Monitor távolság",
"gui.computercraft.config.monitor_distance.tooltip": "A monitorok maximális megjelenítési távolsága. Alapértelmezés szerint a szabványos blokk entitás korlátot használja, de ha nagyobb monitorokat szeretne építeni, akkor ezt növelheti.\nTartomány: 16 ~ 1024",
"gui.computercraft.config.monitor_renderer": "Monitor renderelő",
"gui.computercraft.config.monitor_renderer.tooltip": "A monitorok renderelőjének beállítása. Általában érdemes \"legjobb\" értéken hagyni - ha a monitorok teljesítményproblémákat okoznak, kipróbálhat más renderelő opciókat.\nMegengedett értékek: BEST, TBO, VBO",
"gui.computercraft.config.peripheral": "Perifériák",
"gui.computercraft.config.peripheral.command_block_enabled": "Parancsblokk periféria engedélyezése",
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "Parancsblokk periféria támogatásának engedélyezése",
"gui.computercraft.config.peripheral.max_notes_per_tick": "Egy tick alatt játszható maximális hangjegyek száma",
"gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "A hangszóró által egy tick alatt lejátszható maximális hangjegyek száma.\nTartomány: > 1",
"gui.computercraft.config.peripheral.modem_high_altitude_range": "Modem hatótávolság (magas magasság)",
"gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "A vezeték nélküli modemek hatótávolsága maximális magasságban, tiszta időben, méterben.\nTartomány: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "Modem hatótávolság (magas magasság, viharos idő)",
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "A vezeték nélküli modemek hatótávolsága maximális magasságban viharos időben, méterben.\nTartomány: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_range": "Modem hatótávolság (alapértelmezett)",
"gui.computercraft.config.peripheral.modem_range.tooltip": "A vezeték nélküli modemek hatótávolsága alacsony magasságban, tiszta időben, méterben.\nTartomány: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_range_during_storm": "Modem hatótávolság (viharos idő)",
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "A vezeték nélküli modemek hatótávolsága alacsony magasságban, viharos időben, méterben.\nTartomány: 0 ~ 100000",
"gui.computercraft.config.peripheral.monitor_bandwidth": "Monitor sávszélesség",
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "A monitoronként elküldhető adatkorlát *tick-enként*. Megjegyzés:\n - A sávszélességet a tömörítés előtt mérjük, így az ügyfélhez elküldött adat kisebb.\n - Nem veszi figyelembe a játékosok számát, akiknek egy csomagot küldünk. Egy monitor frissítése egy játékos számára ugyanolyan sávszélesség-korlátot fogyaszt, mint 20 játékosnak küldve.\n - Egy teljes méretű monitor ~25kb adatot küld. Az alapértelmezett érték (1MB) ~40 monitor frissítését teszi lehetővé egyetlen tick-ben.\n0-ra állítva letiltja.\nTartomány: > 0",
"gui.computercraft.config.peripheral.tooltip": "Különböző beállítások a perifériákhoz.",
"gui.computercraft.config.term_sizes": "Terminál méretek",
"gui.computercraft.config.term_sizes.computer": "Számítógép",
"gui.computercraft.config.term_sizes.computer.height": "Terminál magasság",
"gui.computercraft.config.term_sizes.computer.height.tooltip": "Tartomány: 1 ~ 255",
"gui.computercraft.config.term_sizes.computer.tooltip": "Számítógépek terminálmérete.",
"gui.computercraft.config.term_sizes.computer.width": "Terminál szélesség",
"gui.computercraft.config.term_sizes.computer.width.tooltip": "Tartomány: 1 ~ 255",
"gui.computercraft.config.term_sizes.monitor.height": "Maximális monitor magasság",
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "Tartomány: 1 ~ 32",
"gui.computercraft.config.term_sizes.monitor.tooltip": "A monitorok maximális mérete (blokkokban).",
"gui.computercraft.config.term_sizes.monitor.width": "Maximális monitor szélesség",
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "Tartomány: 1 ~ 32",
"gui.computercraft.config.term_sizes.pocket_computer": "Zseb számítógép",
"gui.computercraft.config.term_sizes.pocket_computer.height": "Terminál magasság",
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "Tartomány: 1 ~ 255",
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "Zseb számítógépek terminálmérete.",
"gui.computercraft.config.term_sizes.pocket_computer.width": "Terminál szélesség",
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "Tartomány: 1 ~ 255",
"gui.computercraft.config.term_sizes.tooltip": "Különböző számítógépek terminálméretének konfigurálása.\nA nagyobb terminálok több sávszélességet igényelnek, ezért óvatosan használjuk.",
"gui.computercraft.config.turtle": "Teknősök",
"gui.computercraft.config.turtle.advanced_fuel_limit": "Fejlett teknős üzemanyag korlát",
"gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "A fejlett teknősök üzemanyag korlátja.\nTartomány: > 0",
"gui.computercraft.config.turtle.can_push": "A teknősök tolják az entitásokat",
"gui.computercraft.config.turtle.can_push.tooltip": "Ha be van állítva igazra, a teknősök eltolják az entitásokat az útból, ha van elég hely, ahelyett, hogy megállnának.",
"gui.computercraft.config.turtle.need_fuel": "Üzemanyag szükséges",
"gui.computercraft.config.turtle.need_fuel.tooltip": "Beállítja, hogy a teknősöknek szükségük van-e üzemanyagra a mozgáshoz.",
"gui.computercraft.config.turtle.normal_fuel_limit": "Teknős üzemanyag korlát",
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "A teknősök üzemanyag korlátja.\nTartomány: > 0",
"gui.computercraft.config.turtle.tooltip": "Különböző beállítások a teknősökhöz.",
"gui.computercraft.config.upload_max_size": "Feltöltési fájlméret korlát (byte-ban)",
"gui.computercraft.config.upload_max_size.tooltip": "A feltöltési fájlméret korlátja, byte-ban. A tartománynak 1 KiB és 16 MiB között kell lennie.\nVegye figyelembe, hogy a feltöltések egyetlen tick alatt kerülnek feldolgozásra - a nagy fájlok vagy a gyenge hálózati teljesítmény leállíthatják a hálózati szálat. És figyeljünk a lemezterületre!\nTartomány: 1024 ~ 16777216",
"gui.computercraft.config.upload_nag_delay": "Feltöltési figyelmeztetési késleltetés",
"gui.computercraft.config.upload_nag_delay.tooltip": "Az a késleltetés másodpercben, ami után figyelmeztetést kapunk a feldolgozatlan importokról. 0-ra állítva letiltja.\nTartomány: 0 ~ 60",
"gui.computercraft.pocket_computer_overlay": "Zseb számítógép megnyitva. Nyomd meg az ESC-t a bezáráshoz.",
"gui.computercraft.terminal": "Számítógép terminál",
"gui.computercraft.tooltip.computer_id": "Számítógép ID: %s",
"gui.computercraft.tooltip.copy": "Másolás vágólapra",
"gui.computercraft.tooltip.disk_id": "Lemez ID: %s",
"gui.computercraft.tooltip.terminate": "A futó kód leállítása",
"gui.computercraft.tooltip.terminate.key": "Tartsd lenyomva a Ctrl+T billentyűket",
"gui.computercraft.tooltip.turn_off": "Kapcsold ki ezt a számítógépet",
"gui.computercraft.tooltip.turn_off.key": "Tartsd lenyomva a Ctrl+S billentyűket",
"gui.computercraft.tooltip.turn_on": "Kapcsold be ezt a számítógépet",
"gui.computercraft.upload.failed": "Feltöltés sikertelen",
"gui.computercraft.upload.failed.computer_off": "Be kell kapcsolnod a számítógépet a fájlok feltöltéséhez.",
"gui.computercraft.upload.failed.corrupted": "Fájlok megsérültek a feltöltés során. Kérlek próbáld újra.",
"gui.computercraft.upload.failed.generic": "Feltöltés sikertelen (%s)",
"gui.computercraft.upload.failed.name_too_long": "A fájlnevek túl hosszúak a feltöltéshez.",
"gui.computercraft.upload.failed.too_many_files": "Nem lehet ilyen sok fájlt feltölteni.",
"gui.computercraft.upload.failed.too_much": "A fájlok túl nagyok a feltöltéshez.",
"gui.computercraft.upload.no_response": "Fájlok átvitele",
"gui.computercraft.upload.no_response.msg": "A számítógéped nem használta a továbbított fájlokat. Lehet, hogy futtatnod kell a %s programot, majd újra próbálkozni.",
"item.computercraft.disk": "Floppy lemez",
"item.computercraft.pocket_computer_advanced": "Fejlett zseb számítógép",
"item.computercraft.pocket_computer_advanced.upgraded": "Fejlett %s zseb számítógép",
"item.computercraft.pocket_computer_normal": "Zseb számítógép",
"item.computercraft.pocket_computer_normal.upgraded": "%s zseb számítógép",
"item.computercraft.printed_book": "Nyomtatott könyv",
"item.computercraft.printed_page": "Nyomtatott oldal",
"item.computercraft.printed_pages": "Nyomtatott oldalak",
"item.computercraft.treasure_disk": "Floppy lemez",
"tag.item.computercraft.computer": "Számítógépek",
"tag.item.computercraft.monitor": "Monitorok",
"tag.item.computercraft.turtle": "Teknősök",
"tag.item.computercraft.wired_modem": "Vezetékes modemek",
"tracking_field.computercraft.avg": "%s (átlag)",
"tracking_field.computercraft.computer_tasks.name": "Feladatok",
"tracking_field.computercraft.count": "%s (szám)",
"tracking_field.computercraft.fs.name": "Fájlrendszer műveletek",
"tracking_field.computercraft.http_download.name": "HTTP letöltés",
"tracking_field.computercraft.http_requests.name": "HTTP kérések",
"tracking_field.computercraft.http_upload.name": "HTTP feltöltés",
"tracking_field.computercraft.peripheral.name": "Periféria hívások",
"tracking_field.computercraft.server_tasks.name": "Szerver feladatok",
"tracking_field.computercraft.turtle_ops.name": "Teknős műveletek",
"tracking_field.computercraft.websocket_incoming.name": "Websocket bejövő",
"tracking_field.computercraft.websocket_outgoing.name": "Websocket kimenő",
"upgrade.computercraft.speaker.adjective": "Hangos",
"upgrade.computercraft.wireless_modem_normal.adjective": "Vezeték nélküli",
"upgrade.minecraft.crafting_table.adjective": "Kézműves",
"upgrade.minecraft.diamond_axe.adjective": "Fanyeső",
"upgrade.minecraft.diamond_hoe.adjective": "Gazdálkodó",
"upgrade.minecraft.diamond_pickaxe.adjective": "Bányász",
"upgrade.minecraft.diamond_shovel.adjective": "Ásó",
"upgrade.minecraft.diamond_sword.adjective": "Közelharci"
}

View File

@@ -1,19 +1,19 @@
{
"argument.computercraft.argument_expected": "预期自变量",
"argument.computercraft.computer.distance": "实体距离",
"argument.computercraft.computer.family": "电脑类别",
"argument.computercraft.computer.id": "电脑ID",
"argument.computercraft.computer.family": "计算机类别",
"argument.computercraft.computer.id": "计算机ID",
"argument.computercraft.computer.instance": "唯一实例ID",
"argument.computercraft.computer.label": "电脑标签",
"argument.computercraft.computer.label": "计算机标签",
"argument.computercraft.computer.many_matching": "多台计算机匹配'%s' (实例%s)",
"argument.computercraft.computer.no_matching": "没有计算机匹配'%s'",
"argument.computercraft.tracking_field.no_field": "未知字段'%s'",
"argument.computercraft.unknown_computer_family": "未知电脑类别 '%s'",
"block.computercraft.cable": "网络缆",
"argument.computercraft.unknown_computer_family": "未知计算机类别“%s",
"block.computercraft.cable": "网络线缆",
"block.computercraft.computer_advanced": "高级计算机",
"block.computercraft.computer_command": "命令电脑",
"block.computercraft.computer_command": "命令计算机",
"block.computercraft.computer_normal": "计算机",
"block.computercraft.disk_drive": "盘驱动器",
"block.computercraft.disk_drive": "盘驱动器",
"block.computercraft.monitor_advanced": "高级显示器",
"block.computercraft.monitor_normal": "显示器",
"block.computercraft.printer": "打印机",
@@ -29,36 +29,36 @@
"block.computercraft.wired_modem_full": "有线调制解调器",
"block.computercraft.wireless_modem_advanced": "末影调制解调器",
"block.computercraft.wireless_modem_normal": "无线调制解调器",
"chat.computercraft.wired_modem.peripheral_connected": "外部设备\"%s\"连接到网络",
"chat.computercraft.wired_modem.peripheral_disconnected": "外部设备\"%s\"与网络断开连接",
"chat.computercraft.wired_modem.peripheral_connected": "外部设备\"%s\"连接到网络",
"chat.computercraft.wired_modem.peripheral_disconnected": "外部设备\"%s\"与网络断开连接",
"commands.computercraft.desc": "/computercraft命令提供各种调试和管理工具用于控制和与计算机交互.",
"commands.computercraft.dump.action": "查看有关此计算机的更多信息",
"commands.computercraft.dump.desc": "显示所有计算机的状态或某台计算机的特定信息. 你可以指定计算机的实例id (例如. 123), 计算机id (例如. #123)或标签(例如. \"@My Computer\").",
"commands.computercraft.dump.open_path": "查看该电脑的文件",
"commands.computercraft.dump.synopsis": "显示计算机的状态.",
"commands.computercraft.generic.additional_rows": "%d额外的行…",
"commands.computercraft.generic.exception": "未处理的异常(%s)",
"commands.computercraft.generic.no": "N",
"commands.computercraft.generic.yes": "Y",
"commands.computercraft.dump.open_path": "查看此计算机的文件",
"commands.computercraft.dump.synopsis": "显示计算机的状态",
"commands.computercraft.generic.additional_rows": "%d额外的行…",
"commands.computercraft.generic.exception": "未处理的异常%s",
"commands.computercraft.generic.no": "",
"commands.computercraft.generic.yes": "",
"commands.computercraft.help.desc": "显示该帮助信息",
"commands.computercraft.help.no_children": "%s没有子命令",
"commands.computercraft.help.no_command": "没有这样的命令'%s'",
"commands.computercraft.help.no_command": "没有这样的命令%s",
"commands.computercraft.help.synopsis": "为特定的命令提供帮助",
"commands.computercraft.queue.desc": "发送computer_command事件到命令计算机,并传递其他参数. 这主要是为地图制作者设计的, 作为/trigger更加计算机友好的版本. 任何玩家都可以运行命令, 这很可能是通过文本组件的点击事件完成的.",
"commands.computercraft.queue.synopsis": "将computer_command事件发送到命令计算机",
"commands.computercraft.shutdown.desc": "关闭列出的计算机或全部计算机(如果未指定). 你可以指定计算机的实例id (例如. 123), 计算机id (例如. #123)或标签(例如. \"@My Computer\").",
"commands.computercraft.shutdown.desc": "关闭列出的计算机或全部计算机(如果未指定)你可以指定计算机的实例id例如 123计算机id例如 #123或标签例如 \"@My Computer\")。",
"commands.computercraft.shutdown.done": "关闭%s/%s计算机",
"commands.computercraft.shutdown.synopsis": "远程关闭计算机.",
"commands.computercraft.synopsis": "各种控制计算机的命令.",
"commands.computercraft.tp.action": "传送到这台电脑",
"commands.computercraft.tp.desc": "传送到计算机的位置. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).",
"commands.computercraft.tp.synopsis": "传送到特定的计算机.",
"commands.computercraft.track.desc": "跟踪计算机执行的时间以及它们处理的事件数. 这以/forge track类似的方式呈现信息用于诊断滞后.",
"commands.computercraft.shutdown.synopsis": "远程关闭计算机",
"commands.computercraft.synopsis": "各种控制计算机的命令",
"commands.computercraft.tp.action": "传送到这台计算机",
"commands.computercraft.tp.desc": "传送到计算机的位置你可以指定计算机的实例id例如 123或计算机id例如 #123)。",
"commands.computercraft.tp.synopsis": "传送到特定的计算机",
"commands.computercraft.track.desc": "跟踪计算机执行的时间以及它们处理的事件数这以/forge track类似的方式呈现信息能有助于诊断卡顿与滞后",
"commands.computercraft.track.dump.computer": "计算机",
"commands.computercraft.track.dump.desc": "输出计算机跟踪的最新结果.",
"commands.computercraft.track.dump.desc": "输出计算机跟踪的最新结果",
"commands.computercraft.track.dump.no_timings": "没有时序可用",
"commands.computercraft.track.dump.synopsis": "输出最新的跟踪结果",
"commands.computercraft.track.start.desc": "开始跟踪所有计算机的执行时间和事件计数. 这将放弃先前运行的结果.",
"commands.computercraft.track.start.desc": "开始跟踪所有计算机的执行时间和事件计数这将放弃先前运行的结果",
"commands.computercraft.track.start.stop": "运行%s以停止跟踪并查看结果",
"commands.computercraft.track.start.synopsis": "开始跟踪所有计算机",
"commands.computercraft.track.stop.action": "点击停止跟踪",
@@ -67,16 +67,16 @@
"commands.computercraft.track.stop.synopsis": "停止跟踪所有计算机",
"commands.computercraft.track.synopsis": "跟踪计算机的执行时间.",
"commands.computercraft.turn_on.desc": "打开列出的计算机. 你可以指定计算机的实例id (例如. 123), 计算机id (例如. #123)或标签(例如. \"@My Computer\").",
"commands.computercraft.turn_on.done": "打开%s/%s计算机",
"commands.computercraft.turn_on.done": "打开%s/%s计算机",
"commands.computercraft.turn_on.synopsis": "远程打开计算机.",
"commands.computercraft.view.action": "查看此计算机",
"commands.computercraft.view.desc": "打开计算机的终端,允许远程控制计算机. 这不提供对海龟库存的访问. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).",
"commands.computercraft.view.not_player": "无法为非玩家打开终端",
"commands.computercraft.view.synopsis": "查看计算机的终端.",
"gui.computercraft.config.command_require_creative": "命令电脑需要创造模式",
"gui.computercraft.config.command_require_creative": "命令计算机需要创造模式",
"gui.computercraft.config.command_require_creative.tooltip": "玩家需要处于创造模式并为管理员才能与命令计算机交互。\n这是原版命令方块的默认行为。",
"gui.computercraft.config.computer_space_limit": "计算机空间限制(字节)",
"gui.computercraft.config.computer_space_limit.tooltip": "计算机和海龟的盘空间限制,以字节为单位。",
"gui.computercraft.config.computer_space_limit.tooltip": "计算机和海龟的盘空间限制,以字节为单位。",
"gui.computercraft.config.default_computer_settings": "默认计算机设置",
"gui.computercraft.config.default_computer_settings.tooltip": "以逗号分隔的默认系统设置列表,用于在新计算机上设置。\n示例“shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false”\n将禁用所有自动补全功能。",
"gui.computercraft.config.disabled_generic_methods": "禁用的通用方法",
@@ -89,15 +89,15 @@
"gui.computercraft.config.execution.max_main_global_time": "服务器全局tick时间限制",
"gui.computercraft.config.execution.max_main_global_time.tooltip": "在1刻内执行任务所花费的最大时间以毫秒为单位。\n请注意我们很可能会超出此限制因为无法确定需要多长时间——这旨在成为平均时间的上限。",
"gui.computercraft.config.execution.tooltip": "控制计算机的执行行为。这主要用于微调服务器,一般不需要触碰。",
"gui.computercraft.config.floppy_space_limit": "软盘空间限制(字节)",
"gui.computercraft.config.floppy_space_limit.tooltip": "软盘的磁盘空间限制,以字节为单位。",
"gui.computercraft.config.floppy_space_limit": "软盘空间限制字节",
"gui.computercraft.config.floppy_space_limit.tooltip": "软盘的存储空间限制,以字节为单位。",
"gui.computercraft.config.http": "HTTP",
"gui.computercraft.config.http.bandwidth": "带宽",
"gui.computercraft.config.http.bandwidth.global_download": "全局下载限速",
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "每秒钟可下载的字节数. 所有电脑共享该设置 (bytes/s).",
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "每秒钟可下载的字节数。此值在所有计算机之间共享。(字节/秒)",
"gui.computercraft.config.http.bandwidth.global_upload": "全局上传限速",
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "每秒钟可上传的字节数. 所有电脑共享该设置 (bytes/s).",
"gui.computercraft.config.http.bandwidth.tooltip": "限制电脑可以使用的带宽.",
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "每秒钟可上传的字节数。此值在所有计算机之间共享。(字节/秒)",
"gui.computercraft.config.http.bandwidth.tooltip": "限制计算机可以使用的带宽",
"gui.computercraft.config.http.enabled": "启用HTTP API",
"gui.computercraft.config.http.enabled.tooltip": "在计算机上启用“http”API。禁用此功能还会禁用许多用户依赖的“pastebin”和“wget”程序。建议保持此功能开启并使用“规则”配置选项来实施更精细的控制。",
"gui.computercraft.config.http.max_requests": "最大并发请求数",
@@ -112,7 +112,7 @@
"gui.computercraft.config.http.proxy.tooltip": "通过代理服务器传输HTTP和WebSocket请求。仅影响将“use_proxy”设为true默认关闭的HTTP规则。\n如果代理需要认证请在与“computercraft-server.toml”相同的目录中创建一个“computercraft-proxy.pw”文件其中包含以冒号分隔的用户名和密码例如“myuser:mypassword”。对于SOCKS4代理只需要用户名。",
"gui.computercraft.config.http.proxy.type": "代理类型",
"gui.computercraft.config.http.proxy.type.tooltip": "代理使用的协议.",
"gui.computercraft.config.http.rules": "允许/阻止规则",
"gui.computercraft.config.http.rules": "允许/拒绝规则",
"gui.computercraft.config.http.rules.tooltip": "控制特定域或IP的“http”API行为的规则列表。每条规则匹配一个主机名和一个可选端口然后为请求设置多个属性。规则按顺序进行评估这意味着较早的规则将覆盖较晚的规则。\n\n有效属性\n —“host”必需此规则匹配的域或 IP 地址。这可能是域名“pastebin.com”、通配符“*.pastebin.com”或 CIDR 表示法“127.0.0.0/8”。\n —“port”可选仅匹配特定端口的请求例如80或443。\n\n —“action”可选允许还是拒绝此请求。\n —“max_download”可选计算机在此请求中可以下载的最大大小以字节为单位。\n —“max_upload”可选计算机在此请求中可以上传的最大大小以字节为单位。\n —“max_websocket_message”可选计算机在一个WebSocket数据包中可以发送或接收的最大大小以字节为单位。\n —“use_proxy”可选如果已配置则启用HTTP/SOCKS代理。",
"gui.computercraft.config.http.tooltip": "控制HTTP API",
"gui.computercraft.config.http.websocket_enabled": "启用websockets",
@@ -121,11 +121,11 @@
"gui.computercraft.config.log_computer_errors.tooltip": "记录外设和其他Lua对象抛出的异常。这可让Mod作者更轻松地调试问题但如果人们使用有缺陷的方法则可能导致日志垃圾。",
"gui.computercraft.config.maximum_open_files": "每台计算机打开的最大文件数",
"gui.computercraft.config.maximum_open_files.tooltip": "设置计算机可同时打开的文件数。设为0表示无限制。",
"gui.computercraft.config.monitor_distance": "监视器距离",
"gui.computercraft.config.monitor_distance.tooltip": "监视器渲染的最大距离。默认为标准图块实体限制,但如果你希望建造更大的监视器,则可以扩展。",
"gui.computercraft.config.monitor_renderer": "监视器渲染器",
"gui.computercraft.config.monitor_renderer.tooltip": "用于监视器的渲染器。通常,应将其保持在“最佳”状态——如果监视器存在性能问题,你可以尝试其他渲染器。",
"gui.computercraft.config.peripheral": "外设备",
"gui.computercraft.config.monitor_distance": "显示器距离",
"gui.computercraft.config.monitor_distance.tooltip": "显示器渲染的最大距离。默认为标准图块实体限制,但如果你希望建造更大的显示器,则可以调高此值。",
"gui.computercraft.config.monitor_renderer": "显示器渲染器",
"gui.computercraft.config.monitor_renderer.tooltip": "用于显示器的渲染器。通常,应将其保持在“最佳”状态——如果显示器存在性能问题,你可以尝试其他渲染器。",
"gui.computercraft.config.peripheral": "外设备",
"gui.computercraft.config.peripheral.command_block_enabled": "启用命令方块外设",
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "启用命令方块外设支持",
"gui.computercraft.config.peripheral.max_notes_per_tick": "计算机一次可以播放的最大音符数量",
@@ -138,8 +138,8 @@
"gui.computercraft.config.peripheral.modem_range.tooltip": "晴朗天气下低海拔无线调制解调器的范围,以米为单位。",
"gui.computercraft.config.peripheral.modem_range_during_storm": "调制解调器范围(恶劣天气)",
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "暴风雨天气下低海拔无线调制解调器的范围,以米为单位。",
"gui.computercraft.config.peripheral.monitor_bandwidth": "监视器带宽",
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "*每刻*可以发送多少监视器数据的限制。请注意:\n 带宽是在压缩之前测量的,因此发送到客户端的数据较少。\n 这忽略了数据包发送到的玩家数量。为一个玩家更新监视器所消耗的带宽限制与发送到20个玩家的带宽限制相同。\n 全尺寸监视器发送约25kb的数据。因此默认值1MB允许在1刻内更新约40个监视器。\n设为0以禁用。",
"gui.computercraft.config.peripheral.monitor_bandwidth": "显示器带宽",
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "*每刻*可以发送多少显示器数据的限制。请注意:\n - 带宽是在压缩之前测量的,因此发送到客户端的数据较少。\n - 这忽略了数据包发送到的玩家数量。为一个玩家更新显示器所消耗的带宽限制与发送到20个玩家的带宽限制相同。\n - 全尺寸显示器发送约25kb的数据。因此默认值1MB允许在1刻内更新约40个显示器。\n设为0以禁用。",
"gui.computercraft.config.peripheral.tooltip": "与外设相关的各种选项。",
"gui.computercraft.config.term_sizes": "终端尺寸",
"gui.computercraft.config.term_sizes.computer": "计算机",
@@ -148,18 +148,18 @@
"gui.computercraft.config.term_sizes.computer.tooltip": "计算机的终端尺寸。",
"gui.computercraft.config.term_sizes.computer.width": "终端宽度",
"gui.computercraft.config.term_sizes.computer.width.tooltip": "计算机终端宽度",
"gui.computercraft.config.term_sizes.monitor": "监视器",
"gui.computercraft.config.term_sizes.monitor.height": "最大监视器高度",
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "监视器的最大高度",
"gui.computercraft.config.term_sizes.monitor.tooltip": "监视器的最大尺寸(以方块为单位)。",
"gui.computercraft.config.term_sizes.monitor.width": "最大监视器宽度",
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "监视器的最大宽度",
"gui.computercraft.config.term_sizes.pocket_computer": "手提计算机",
"gui.computercraft.config.term_sizes.monitor": "显示器",
"gui.computercraft.config.term_sizes.monitor.height": "最大显示器高度",
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "显示器的最大高度",
"gui.computercraft.config.term_sizes.monitor.tooltip": "显示器的最大尺寸(以方块为单位)。",
"gui.computercraft.config.term_sizes.monitor.width": "最大显示器宽度",
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "显示器的最大宽度",
"gui.computercraft.config.term_sizes.pocket_computer": "便携式计算机",
"gui.computercraft.config.term_sizes.pocket_computer.height": "终端高度",
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "手提计算机终端高度",
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "手提计算机终端尺寸",
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "便携式计算机终端高度",
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "便携式计算机终端尺寸",
"gui.computercraft.config.term_sizes.pocket_computer.width": "终端宽度",
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "手提计算机终端宽度",
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "便携式计算机终端宽度",
"gui.computercraft.config.term_sizes.tooltip": "配置各种计算机终端的尺寸。\n终端越大需要的带宽越多请谨慎使用。",
"gui.computercraft.config.turtle": "海龟",
"gui.computercraft.config.turtle.advanced_fuel_limit": "高级海龟燃料限制",
@@ -172,14 +172,14 @@
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "海龟的燃料限制。",
"gui.computercraft.config.turtle.tooltip": "与海龟相关的各种选项。",
"gui.computercraft.config.upload_max_size": "文件上传大小限制(字节)",
"gui.computercraft.config.upload_max_size.tooltip": "文件上传大小限制以字节为单位。必须在1KiB到16MiB之间。\n请记住上传在1刻内处理——大文件或不佳的网络性能可能会使网络线程停滞。并且注意盘空间!",
"gui.computercraft.config.upload_max_size.tooltip": "文件上传大小限制以字节为单位。必须在1KiB到16MiB之间。\n请记住上传在1刻内处理——大文件或不佳的网络性能可能会使网络线程停滞。记得注意盘空间!",
"gui.computercraft.config.upload_nag_delay": "上传延迟",
"gui.computercraft.config.upload_nag_delay.tooltip": "通知未处理的导入之前延迟的秒数。设为0以禁用。",
"gui.computercraft.pocket_computer_overlay": "手提计算机已打开。按ESC键关闭。",
"gui.computercraft.pocket_computer_overlay": "便携式计算机已打开。按ESC键关闭。",
"gui.computercraft.terminal": "计算机终端",
"gui.computercraft.tooltip.computer_id": "计算机ID: %s",
"gui.computercraft.tooltip.copy": "复制到剪贴板",
"gui.computercraft.tooltip.disk_id": "盘ID: %s",
"gui.computercraft.tooltip.disk_id": "盘ID%s",
"gui.computercraft.tooltip.terminate": "停止当前运行的代码",
"gui.computercraft.tooltip.terminate.key": "按住Ctrl+T",
"gui.computercraft.tooltip.turn_off": "关闭这台计算机",
@@ -195,19 +195,19 @@
"gui.computercraft.upload.no_response": "传输文件",
"gui.computercraft.upload.no_response.msg": "你的计算机尚未使用你传输的文件。你可能需要运行%s程序并重试。",
"item.computercraft.disk": "软盘",
"item.computercraft.pocket_computer_advanced": "高级手提计算机",
"item.computercraft.pocket_computer_advanced.upgraded": "高级%s手提计算机",
"item.computercraft.pocket_computer_normal": "手提计算机",
"item.computercraft.pocket_computer_normal.upgraded": "%s手提计算机",
"item.computercraft.pocket_computer_advanced": "高级便携式计算机",
"item.computercraft.pocket_computer_advanced.upgraded": "高级便携式计算机(%s",
"item.computercraft.pocket_computer_normal": "便携式计算机",
"item.computercraft.pocket_computer_normal.upgraded": "便携式计算机(%s",
"item.computercraft.printed_book": "打印书",
"item.computercraft.printed_page": "打印纸",
"item.computercraft.printed_pages": "打印纸",
"item.computercraft.printed_pages": "一摞打印纸",
"item.computercraft.treasure_disk": "软盘",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "计算机",
"tag.item.computercraft.disks": "盘",
"tag.item.computercraft.disks": "盘",
"tag.item.computercraft.dyeable": "可染色物品",
"tag.item.computercraft.monitor": "监视器",
"tag.item.computercraft.monitor": "显示器",
"tag.item.computercraft.pocket_computers": "便携式计算机",
"tag.item.computercraft.turtle": "海龟",
"tag.item.computercraft.turtle_can_place": "可放置海龟物品",
@@ -220,12 +220,12 @@
"tracking_field.computercraft.http_requests.name": "HTTP请求",
"tracking_field.computercraft.http_upload.name": "HTTP上传",
"tracking_field.computercraft.max": "%s (最大)",
"tracking_field.computercraft.peripheral.name": "外部设备呼叫",
"tracking_field.computercraft.peripheral.name": "外部设备调用",
"tracking_field.computercraft.server_tasks.name": "服务器任务",
"tracking_field.computercraft.turtle_ops.name": "海龟行动",
"tracking_field.computercraft.turtle_ops.name": "海龟操作",
"tracking_field.computercraft.websocket_incoming.name": "Websocket传入",
"tracking_field.computercraft.websocket_outgoing.name": "Websocket传出",
"upgrade.computercraft.speaker.adjective": "喧闹",
"upgrade.computercraft.speaker.adjective": "嘈杂",
"upgrade.computercraft.wireless_modem_advanced.adjective": "末影",
"upgrade.computercraft.wireless_modem_normal.adjective": "无线",
"upgrade.minecraft.crafting_table.adjective": "合成",

View File

@@ -17,8 +17,8 @@ accessible field net/minecraft/client/gui/components/ChatComponent allMessages L
# ItemPocketRenderer/ItemPrintoutRenderer
accessible method net/minecraft/client/renderer/ItemInHandRenderer calculateMapTilt (F)F
accessible method net/minecraft/client/renderer/ItemInHandRenderer renderMapHand (Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/HumanoidArm;)V
accessible method net/minecraft/client/renderer/ItemInHandRenderer renderPlayerArm (Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IFFLnet/minecraft/world/entity/HumanoidArm;)V
accessible method net/minecraft/client/renderer/ItemInHandRenderer renderMapHand (Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/world/entity/HumanoidArm;)V
accessible method net/minecraft/client/renderer/ItemInHandRenderer renderPlayerArm (Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;IFFLnet/minecraft/world/entity/HumanoidArm;)V
# SpeakerInstance/SpeakerManager
accessible method com/mojang/blaze3d/audio/Channel pumpBuffers (I)V

View File

@@ -31,8 +31,8 @@ public @interface WithMinecraft {
}
public static void bootstrap() {
SharedConstants.tryDetectVersion();
ServiceLoader.load(SetupHook.class, SetupHook.class.getClassLoader()).forEach(SetupHook::run);
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
}

View File

@@ -10,7 +10,7 @@ import net.minecraft.gametest.framework.GameTestServer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
import net.minecraft.server.WorldStem;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.level.progress.LevelLoadListener;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.spongepowered.asm.mixin.Mixin;
@@ -22,7 +22,10 @@ import java.util.concurrent.locks.LockSupport;
@Mixin(GameTestServer.class)
abstract class GameTestServerMixin extends MinecraftServer {
GameTestServerMixin(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory) {
GameTestServerMixin(
Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository,
WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, LevelLoadListener progressListenerFactory
) {
super(serverThread, storageSource, packRepository, worldStem, proxy, fixerUpper, services, progressListenerFactory);
}

View File

@@ -13,6 +13,7 @@ import dan200.computercraft.gametest.api.*
import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.test.core.assertArrayEquals
import dan200.computercraft.test.core.computer.getApi
import net.minecraft.client.input.KeyEvent
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.gametest.framework.GameTestHelper
@@ -155,8 +156,8 @@ class Computer_Test {
// Press a key on the client
thenOnClient {
val screen = minecraft.screen as AbstractComputerScreen<*>
screen.keyPressed(GLFW.GLFW_KEY_A, 0, 0)
screen.keyReleased(GLFW.GLFW_KEY_A, 0, 0)
screen.keyPressed(KeyEvent(GLFW.GLFW_KEY_A, 0, 0))
screen.keyReleased(KeyEvent(GLFW.GLFW_KEY_A, 0, 0))
}
// And assert it is handled and sent back to the client
thenIdle(2)

View File

@@ -122,7 +122,7 @@ class Disk_Drive_Test {
thenWaitUntil {
val drive = helper.getBlockEntity(drivePos, DiskDriveBlockEntity::class.java)
if (!drive.getItem(0).has(ModRegistry.DataComponents.DISK_ID.get())) {
helper.fail("Disk has no item", drivePos)
helper.abort("Disk has no item", drivePos)
}
}
}

View File

@@ -45,7 +45,7 @@ class Monitor_Test {
val tile = context.getBlockEntity(pos, MonitorBlockEntity::class.java)
if (tile.width != 1 || tile.height != 1) {
context.fail("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos)
context.abort("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos)
}
}
}

View File

@@ -35,7 +35,9 @@ class Recipe_Test {
val profile = GameProfile(UUID.fromString("f3c8d69b-0776-4512-8434-d1b2165909eb"), "dan200")
val tag = DataComponentPatch.builder().set(DataComponents.PROFILE, ResolvableProfile(profile)).build()
val tag =
DataComponentPatch.builder().set(DataComponents.PROFILE, ResolvableProfile.createResolved(profile))
.build()
assertEquals(tag, result.componentsPatch, "Expected NBT tags to be the same")
}
}

View File

@@ -680,7 +680,7 @@ class Turtle_Test {
val villager = helper.getEntity(EntityType.VILLAGER)
val expectedY = helper.absolutePos(pos).y - 0.125
if (villager.y < expectedY) helper.fail("Expected villager at y>=$expectedY, but at ${villager.y}", pos)
if (villager.y < expectedY) helper.abort("Expected villager at y>=$expectedY, but at ${villager.y}", pos)
}
}
@@ -697,7 +697,7 @@ class Turtle_Test {
helper.assertEntityNotPresent(EntityType.SHEEP)
val count = helper.getBlockEntity(turtlePos, TurtleBlockEntity::class.java)
.countItem(Items.WHITE_WOOL)
if (count == 0) helper.fail("Expected turtle to have white wool", turtlePos)
if (count == 0) helper.abort("Expected turtle to have white wool", turtlePos)
}
}

View File

@@ -35,7 +35,7 @@ fun Minecraft.isRenderingStable(): Boolean = (this as MinecraftExtensions).`comp
fun GameTestSequence.thenOnClient(task: ClientTestHelper.() -> Unit): GameTestSequence {
var future: CompletableFuture<Void>? = null
thenExecute { future = Minecraft.getInstance().submit { task(ClientTestHelper()) } }
thenWaitUntil { if (!future!!.isDone) fail("Not done task yet") }
thenWaitUntil { if (!future!!.isDone) abort("Not done task yet") }
thenExecute {
try {
future!!.get()
@@ -59,10 +59,10 @@ fun GameTestSequence.thenScreenshot(name: String? = null, showGui: Boolean = fal
thenWaitUntil {
if (Minecraft.getInstance().isRenderingStable()) {
val idleFor = ++counter
if (idleFor <= 20) fail("Only idle for $idleFor ticks")
if (idleFor <= 20) abort("Only idle for $idleFor ticks")
} else {
counter = 0
fail("Waiting for client to finish rendering")
abort("Waiting for client to finish rendering")
}
}
@@ -74,7 +74,7 @@ fun GameTestSequence.thenScreenshot(name: String? = null, showGui: Boolean = fal
// Take a screenshot and wait for it to have finished.
val hasScreenshot = AtomicBoolean()
thenOnClient { screenshot("$fullName.png") { hasScreenshot.set(true) } }
thenWaitUntil { if (!hasScreenshot.get()) fail("Screenshot does not exist") }
thenWaitUntil { if (!hasScreenshot.get()) abort("Screenshot does not exist") }
thenOnClient { minecraft.options.hideGui = false }
return this
@@ -92,7 +92,7 @@ fun ServerPlayer.setupForTest() {
*/
fun GameTestHelper.positionAtArmorStand() {
val stand = getEntity(EntityType.ARMOR_STAND)
val player = level.randomPlayer ?: fail("Player does not exist")
val player = level.randomPlayer ?: abort("Player does not exist")
player.setupForTest()
player.connection.teleport(stand.x, stand.y, stand.z, stand.yRot, stand.xRot)
@@ -103,7 +103,7 @@ fun GameTestHelper.positionAtArmorStand() {
*/
fun GameTestHelper.positionAt(pos: BlockPos, yRot: Float = 0.0f, xRot: Float = 0.0f) {
val absolutePos = absolutePos(pos)
val player = level.randomPlayer ?: fail("Player does not exist")
val player = level.randomPlayer ?: abort("Player does not exist")
player.setupForTest()
player.connection.teleport(absolutePos.x + 0.5, absolutePos.y + 0.5, absolutePos.z + 0.5, yRot, xRot)

View File

@@ -97,11 +97,11 @@ fun GameTestSequence.thenComputerOk(name: String? = null, marker: String = Compu
thenWaitUntil {
val computer = ComputerState.get(label)
if (computer == null || !computer.isDone(marker)) fail("Computer '$label' has not reached $marker yet.")
if (computer == null || !computer.isDone(marker)) abort("Computer '$label' has not reached $marker yet.")
}
thenExecuteFailFast {
val error = ComputerState.get(label)!!.check(marker)
if (error != null) fail(error)
if (error != null) abort(error)
}
return this
}
@@ -128,7 +128,7 @@ fun GameTestSequence.thenOnComputer(name: String? = null, action: suspend LuaTas
thenWaitUntil {
if (!monitor!!.isFinished) {
val runningFor = (test as GameTestInfoAccessor).`computercraft$getTick`() - self.lastTick
fail("Computer '$label' has not finished yet (running for $runningFor ticks).")
abort("Computer '$label' has not finished yet (running for $runningFor ticks).")
}
}
thenExecuteFailFast { monitor!!.check() }
@@ -155,25 +155,25 @@ fun GameTestHelper.immediate(run: () -> Unit) {
// Helper functions for failing tests
/** Raise a [GameTestAssertException]. */
fun GameTestHelper.fail(message: String): Nothing = throw assertionException(Component.literal(message))
fun GameTestHelper.abort(message: String): Nothing = throw assertionException(Component.literal(message))
/** Raise a [GameTestAssertException] at a position. */
fun GameTestHelper.fail(message: String, pos: BlockPos): Nothing =
fun GameTestHelper.abort(message: String, pos: BlockPos): Nothing =
throw assertionException(pos, Component.literal(message))
/** Assert a condition is true, or raise a [GameTestAssertException] if not. */
fun GameTestHelper.assertTrue(condition: Boolean, message: String) = assertTrue(condition, Component.literal(message))
/** Raise a [GameTestAssertException]. */
fun GameTestSequence.fail(message: String): Nothing =
fun GameTestSequence.abort(message: String): Nothing =
throw GameTestAssertException(
Component.literal(message),
((this as GameTestSequenceAccessor).parent as GameTestInfoAccessor).`computercraft$getTick`(),
)
/** Fail with an optional context message. */
private fun GameTestHelper.fail(message: String?, detail: String, pos: BlockPos): Nothing {
fail(if (message.isNullOrEmpty()) detail else "$message: $detail", pos)
private fun GameTestHelper.abort(message: String?, detail: String, pos: BlockPos): Nothing {
abort(if (message.isNullOrEmpty()) detail else "$message: $detail", pos)
}
/**
@@ -186,7 +186,7 @@ fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boole
*/
fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean, message: String) {
val state = getBlockState(pos)
if (!predicate(state)) fail(message, state.toString(), pos)
if (!predicate(state)) abort(message, state.toString(), pos)
}
/**
@@ -196,9 +196,9 @@ fun <T : Comparable<T>> GameTestHelper.assertBlockHas(pos: BlockPos, property: P
val state = getBlockState(pos)
if (!state.hasProperty(property)) {
val id = RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK, state.block)
fail(message, "block $id does not have property ${property.name}", pos)
abort(message, "block $id does not have property ${property.name}", pos)
} else if (state.getValue(property) != value) {
fail(message, "${property.name} is ${state.getValue(property)}, expected $value", pos)
abort(message, "${property.name} is ${state.getValue(property)}, expected $value", pos)
}
}
@@ -208,8 +208,8 @@ fun <T : Comparable<T>> GameTestHelper.assertBlockHas(pos: BlockPos, property: P
fun GameTestHelper.getContainerAt(pos: BlockPos): Container =
when (val container: BlockEntity? = level.getBlockEntity(absolutePos(pos))) {
is Container -> container
null -> fail("Expected a container at $pos, found nothing", pos)
else -> fail("Expected a container at $pos, found ${getName(container.type)}", pos)
null -> abort("Expected a container at $pos, found nothing", pos)
else -> abort("Expected a container at $pos, found ${getName(container.type)}", pos)
}
/**
@@ -254,7 +254,7 @@ private fun GameTestHelper.assertContainerExactlyImpl(pos: BlockPos, container:
if (slot >= 0) {
val invItems = (0 until container.containerSize).map { container.getItem(it) }.dropLastWhile { it.isEmpty }
fail(
abort(
"""
Items do not match (first mismatch at slot $slot).
Expected: ${formatItems(items)}
@@ -278,15 +278,15 @@ fun GameTestHelper.assertPeripheral(pos: BlockPos, direction: Direction = Direct
val peripheral = getPeripheralAt(pos, direction)
val block = getBlockState(pos).block.name.string
when {
peripheral == null -> fail("No peripheral for '$block'", pos)
peripheral.type != type -> fail("Peripheral for '$block' is of type ${peripheral.type}, expected $type", pos)
peripheral == null -> abort("No peripheral for '$block'", pos)
peripheral.type != type -> abort("Peripheral for '$block' is of type ${peripheral.type}, expected $type", pos)
}
}
fun GameTestHelper.assertNoPeripheral(pos: BlockPos, direction: Direction = Direction.UP) {
val peripheral = getPeripheralAt(pos, direction)
val block = getBlockState(pos).block.name
if (peripheral != null) fail("Expected no peripheral for '$block', got a ${peripheral.type}", pos)
if (peripheral != null) abort("Expected no peripheral for '$block', got a ${peripheral.type}", pos)
}
fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: String? = null) {
@@ -295,7 +295,7 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin
if (!matcher.matches(actual)) {
val description = StringDescription()
matcher.describeMismatch(actual, description)
fail(if (message.isNullOrEmpty()) description.toString() else "$message: $description")
abort(if (message.isNullOrEmpty()) description.toString() else "$message: $description")
}
}
@@ -305,7 +305,7 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin
fun GameTestHelper.assertItemEntityCountIs(expected: Item, count: Int) {
val actualCount = getEntities(EntityType.ITEM).sumOf { if (it.item.`is`(expected)) it.item.count else 0 }
if (actualCount != count) {
fail("Expected $count ${ItemStack(expected).itemName.string} items to exist (found $actualCount)")
abort("Expected $count ${ItemStack(expected).itemName.string} items to exist (found $actualCount)")
}
}
@@ -318,9 +318,9 @@ private fun getName(type: BlockEntityType<*>): ResourceLocation =
fun <T : Entity> GameTestHelper.getEntity(type: EntityType<T>): T {
val entities = getEntities(type)
when (entities.size) {
0 -> fail("No $type entities")
0 -> abort("No $type entities")
1 -> return entities[0]
else -> fail("Multiple $type entities (${entities.size} in bounding box)")
else -> abort("Multiple $type entities (${entities.size} in bounding box)")
}
}
@@ -369,7 +369,7 @@ fun GameTestHelper.assertNotCraftable(vararg items: ItemStack) {
val recipe = level.server.recipeManager.getRecipeFor(RecipeType.CRAFTING, input, level)
if (recipe.isPresent) fail("Expected no recipe to match $items")
if (recipe.isPresent) abort("Expected no recipe to match $items")
}
/**
@@ -381,7 +381,7 @@ fun GameTestHelper.craftItem(vararg items: ItemStack): ItemStack {
val input = CraftingInput.of(3, 3, container)
val recipe = level.server.recipeManager.getRecipeFor(RecipeType.CRAFTING, input, level).getOrNull()
?: fail("No recipe matches $items")
?: throw assertionException("No recipe matches $items")
return recipe.value.assemble(input, level.registryAccess())
}

View File

@@ -75,7 +75,7 @@ object TestHooks {
}
fun getTestOrigin(server: MinecraftServer): BlockPos {
val spawn = server.overworld().sharedSpawnPos
val spawn = server.respawnData.pos()
return BlockPos(spawn.x, -59, spawn.y)
}
@@ -88,7 +88,7 @@ object TestHooks {
LOG.info("Cleaning up after last run")
val level = server.overworld()
StructureUtils.findTestBlocks(getTestOrigin(server), 200, level).forEach { pos ->
StructureUtils.findTestBlocks(getTestOrigin(server), 200, level).toList().forEach { pos ->
val test = level.getBlockEntity(pos, BlockEntityType.TEST_INSTANCE_BLOCK).getOrNull() ?: return@forEach
StructureUtils.clearSpaceForStructure(test.structureBoundingBox, level)
}

View File

@@ -1,3 +1,11 @@
# New features in CC: Tweaked 1.116.1
* Update translations.
Several bug fixes:
* Fix NPE when mcfunction files contain CC commands.
* Fix crash in `speaker.playAudio` on noisy pocket computers.
# New features in CC: Tweaked 1.116.0
* Add `turtle.getEquippedLeft()` and `turtle.getEquippedRight()`.
@@ -5,6 +13,7 @@
* Support multi-line strings and comments in `edit`.
* Computer and pocket computer terminal sizes can be set with the `computercraft:terminal_size` component.
* Border and sidebar textures now use vanilla's nine-sliced format.
* Allow equipping upgrades on the bottom of a pocket computer.
Several bug fixes:
* Ignore shader compilation errors when running with Pojav.

View File

@@ -1,21 +1,9 @@
New features in CC: Tweaked 1.116.0
New features in CC: Tweaked 1.116.1
* Add `turtle.getEquippedLeft()` and `turtle.getEquippedRight()`.
* Add item tags for floppy disks and pocket computers.
* Support multi-line strings and comments in `edit`.
* Computer and pocket computer terminal sizes can be set with the `computercraft:terminal_size` component.
* Border and sidebar textures now use vanilla's nine-sliced format.
* Update translations.
Several bug fixes:
* Ignore shader compilation errors when running with Pojav.
* Fix several issues with character input.
* Fix pocket computer dyes being lost when equipping/unequipping upgrades.
* Fix superflous warnings from allocation tracking.
* Fix `__lt`/`__le` not working on heterogeneous types.
* Many documentation fixes (Lemmmy, matematikaadit, McJack12).
* Fix `0` being treated as a valid colour in `window` and `colour.toBlit`.
* Fix out-of-bounds when pasting too lon text.
* Fix syntax highlighting of string escapes (LorneHyde).
* Fix sidebar texture of advanced computers being offset.
* Fix NPE when mcfunction files contain CC commands.
* Fix crash in `speaker.playAudio` on noisy pocket computers.
Type "help changelog" to see the full version history.

View File

@@ -74,7 +74,6 @@ configurations {
}
dependencies {
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
modCompileOnly(libs.bundles.externalMods.fabric.compile) {
exclude("net.fabricmc", "fabric-loader")
exclude("net.fabricmc.fabric-api")

View File

@@ -12,7 +12,6 @@ import dan200.computercraft.api.client.FabricComputerCraftAPIClient;
import dan200.computercraft.client.platform.ClientNetworkContextImpl;
import dan200.computercraft.client.platform.FabricModelKey;
import dan200.computercraft.client.platform.ModelKey;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.ComputerCraft;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.config.ConfigSpec;
@@ -25,12 +24,13 @@ import net.fabricmc.fabric.api.client.model.loading.v1.PreparableModelLoadingPlu
import net.fabricmc.fabric.api.client.model.loading.v1.UnbakedExtraModel;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.SpecialGuiElementRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemTintSources;
import net.minecraft.client.gui.components.debug.DebugScreenEntries;
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState;
import net.minecraft.client.gui.screens.MenuScreens;
@@ -41,7 +41,6 @@ import net.minecraft.client.renderer.item.properties.conditional.ConditionalItem
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperties;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ResolvableModel;
import net.minecraft.world.phys.BlockHitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -49,8 +48,6 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import static dan200.computercraft.core.util.Nullability.assertNonNull;
public class ComputerCraftClient {
public static void init() {
var clientNetwork = new ClientNetworkContextImpl();
@@ -67,9 +64,10 @@ public class ComputerCraftClient {
ClientRegistry.registerItemColours(ItemTintSources.ID_MAPPER::put);
ClientRegistry.registerSelectItemProperties(SelectItemModelProperties.ID_MAPPER::put);
ClientRegistry.registerConditionalItemProperties(ConditionalItemModelProperties.ID_MAPPER::put);
ClientRegistry.registerLayerDefinitions((id, factory) -> EntityModelLayerRegistry.registerModelLayer(id, factory::get));
PreparableModelLoadingPlugin.register(
ClientRegistry::gatherExtraModels,
(state, executor) -> ClientRegistry.gatherExtraModels(state.resourceManager(), executor),
(state, context) -> ClientRegistry.registerExtraModels(new ClientRegistry.RegisterExtraModels() {
@Override
public <U, T> void register(ModelKey<T> key, U unbaked, BiConsumer<U, ResolvableModel.Resolver> resolve, BiFunction<U, ModelBaker, T> bake) {
@@ -93,6 +91,7 @@ public class ComputerCraftClient {
ClientTickEvents.START_CLIENT_TICK.register(client -> ClientHooks.onTick());
// This isn't 100% consistent with Forge, but not worth a mixin.
/*
WorldRenderEvents.START.register(context -> ClientHooks.onRenderTick());
WorldRenderEvents.BLOCK_OUTLINE.register((context, hitResult) -> {
var hit = Minecraft.getInstance().hitResult;
@@ -102,6 +101,9 @@ public class ComputerCraftClient {
return true;
}
});
*/
ClientRegistry.registerDebugScreenEntries(DebugScreenEntries::register);
// Register our open folder command
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) ->

View File

@@ -1,23 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.mixin.client;
import dan200.computercraft.client.ClientHooks;
import net.minecraft.client.gui.components.DebugScreenOverlay;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
@Mixin(DebugScreenOverlay.class)
class DebugScreenOverlayMixin {
@Inject(method = "getSystemInformation", at = @At("RETURN"))
@SuppressWarnings("UnusedMethod")
private void appendBlockDebugInfo(CallbackInfoReturnable<List<String>> cir) {
ClientHooks.addBlockDebugInfo(cir.getReturnValue()::add);
}
}

View File

@@ -7,9 +7,10 @@ package dan200.computercraft.mixin.client;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.client.ClientHooks;
import dan200.computercraft.client.ExtendedItemFrameRenderStateHolder;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.entity.ItemFrameRenderer;
import net.minecraft.client.renderer.entity.state.ItemFrameRenderState;
import net.minecraft.client.renderer.state.CameraRenderState;
import net.minecraft.world.entity.decoration.ItemFrame;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
@@ -21,14 +22,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@SuppressWarnings("UnusedMethod")
class ItemFrameRendererMixin {
@Inject(
method = "render(Lnet/minecraft/client/renderer/entity/state/ItemFrameRenderState;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
method = "submit(Lnet/minecraft/client/renderer/entity/state/ItemFrameRenderState;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;Lnet/minecraft/client/renderer/state/CameraRenderState;)V",
at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/state/ItemFrameRenderState;mapId:Lnet/minecraft/world/level/saveddata/maps/MapId;", opcode = Opcodes.GETFIELD, ordinal = 1),
cancellable = true
)
@SuppressWarnings("unused")
private void render(ItemFrameRenderState frame, PoseStack pose, MultiBufferSource buffers, int light, CallbackInfo ci) {
private void submit(ItemFrameRenderState frame, PoseStack pose, SubmitNodeCollector buffers, CameraRenderState camera, CallbackInfo ci) {
var state = ((ExtendedItemFrameRenderStateHolder) frame).computercraft$state();
if (ClientHooks.onRenderItemFrame(pose, buffers, frame, state, light)) {
if (ClientHooks.onRenderItemFrame(pose, buffers, frame, state)) {
ci.cancel();
pose.popPose();
}

View File

@@ -8,7 +8,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.client.ClientHooks;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
@@ -22,9 +22,9 @@ class ItemInHandRendererMixin {
@SuppressWarnings("unused")
private void onRenderItem(
AbstractClientPlayer player, float partialTicks, float pitch, InteractionHand hand, float swingProgress, ItemStack stack,
float equippedProgress, PoseStack transform, MultiBufferSource buffer, int combinedLight, CallbackInfo ci
float equippedProgress, PoseStack transform, SubmitNodeCollector collector, int combinedLight, CallbackInfo ci
) {
if (ClientHooks.onRenderHeldItem(transform, buffer, combinedLight, hand, pitch, equippedProgress, swingProgress, stack)) {
if (ClientHooks.onRenderHeldItem(transform, collector, combinedLight, hand, pitch, equippedProgress, swingProgress, stack)) {
ci.cancel();
}
}

View File

@@ -7,7 +7,6 @@
"defaultRequire": 1
},
"client": [
"DebugScreenOverlayMixin",
"ItemFrameRendererMixin",
"ItemFrameRenderStateMixin",
"ItemInHandRendererMixin",

View File

@@ -40,18 +40,14 @@ import net.fabricmc.fabric.api.lookup.v1.item.ItemApiLookup;
import net.fabricmc.fabric.api.loot.v3.LootTableEvents;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.fabric.api.resource.v1.ResourceLoader;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.entity.BlockEntity;
@@ -59,8 +55,6 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.storage.LevelResource;
import org.jspecify.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
public class ComputerCraft {
@@ -137,33 +131,19 @@ public class ComputerCraft {
entries.getContext(), entries
));
CommonHooks.onDatapackReload((name, listener) -> ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new ReloadListener(name, listener)));
CommonHooks.onDatapackReload(ResourceLoader.get(PackType.SERVER_DATA)::registerReloader);
FabricDetailRegistries.FLUID_VARIANT.addProvider(FluidDetails::fill);
ComputerCraftAPI.registerGenericSource(new InventoryMethods());
Peripherals.addGenericLookup(InventoryMethods::extractContainer);
}
private static <B extends FriendlyByteBuf, T extends CustomPacketPayload> void registerPayloadType(PayloadTypeRegistry<B> registry, CustomPacketPayload.TypeAndCodec<B, T> type) {
registry.register(type.type(), type.codec());
if (FabricLoader.getInstance().isModLoaded(CreateIntegration.ID)) CreateIntegration.setup();
}
private record ReloadListener(ResourceLocation name, PreparableReloadListener listener)
implements IdentifiableResourceReloadListener {
@Override
public ResourceLocation getFabricId() {
return name;
}
@Override
public CompletableFuture<Void> reload(PreparationBarrier preparationBarrier, ResourceManager resourceManager, Executor backgroundExecutor, Executor gameExecutor) {
return listener.reload(preparationBarrier, resourceManager, backgroundExecutor, gameExecutor);
}
private static <B extends FriendlyByteBuf, T extends CustomPacketPayload> void registerPayloadType(PayloadTypeRegistry<B> registry, CustomPacketPayload.TypeAndCodec<B, T> type) {
registry.register(type.type(), type.codec());
}
private record BlockComponentImpl<T, C extends @Nullable Object>(

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