mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-06 00:22:58 +00:00
Compare commits
92 Commits
v1.20.1-1.
...
v1.20.4-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
240528cce5 | ||
|
|
83f1f86888 | ||
|
|
a0f759527d | ||
|
|
385e4210fa | ||
|
|
d2896473f2 | ||
|
|
f14cb2a3d1 | ||
|
|
8db5c6bc3a | ||
|
|
9c202bd1c2 | ||
|
|
fc834cd97f | ||
|
|
f26e443e81 | ||
|
|
033378333f | ||
|
|
ebeaa757a9 | ||
|
|
57b1a65db3 | ||
|
|
27c72a4571 | ||
|
|
f284328656 | ||
|
|
6b83c63991 | ||
|
|
b27526bd21 | ||
|
|
cb25f6c08a | ||
|
|
d38b1d04e7 | ||
|
|
9ccee75a99 | ||
|
|
359c8d6652 | ||
|
|
1788afacfc | ||
|
|
f695f22d8a | ||
|
|
bc03090ca4 | ||
|
|
a617d0d566 | ||
|
|
36599b321e | ||
|
|
1d6e3f4fc0 | ||
|
|
30dc4cb38c | ||
|
|
be4512d1c3 | ||
|
|
e6ee292850 | ||
|
|
9d36f72bad | ||
|
|
b5923c4462 | ||
|
|
4d1e689719 | ||
|
|
9d4af07568 | ||
|
|
89294f4a22 | ||
|
|
133b51b092 | ||
|
|
272010e945 | ||
|
|
e0889c613a | ||
|
|
f115d43d07 | ||
|
|
8be6b1b772 | ||
|
|
104d5e70de | ||
|
|
e3bda2f763 | ||
|
|
234f69e8e5 | ||
|
|
ed3a17f9b9 | ||
|
|
0349c2b1f9 | ||
|
|
03f9e6bd6d | ||
|
|
9d8c933a14 | ||
|
|
78bb3da58c | ||
|
|
39a5e40c92 | ||
|
|
763ba51919 | ||
|
|
cf6ec8c28f | ||
|
|
95d3b646b2 | ||
|
|
488f66eead | ||
|
|
1f7d245876 | ||
|
|
af12b3a0ea | ||
|
|
eb3e8ba677 | ||
|
|
2043939531 | ||
|
|
84a799d27a | ||
|
|
fe826f5c9c | ||
|
|
f8b7422294 | ||
|
|
b343c01216 | ||
|
|
76968f2f28 | ||
|
|
1d365f5a0b | ||
|
|
7b240cbf7e | ||
|
|
d272a327c7 | ||
|
|
0c0556a5bc | ||
|
|
87345c6b2e | ||
|
|
784e623776 | ||
|
|
bcb3e9bd53 | ||
|
|
c30bffbd0f | ||
|
|
91c41856c5 | ||
|
|
18c9723308 | ||
|
|
aee382ed70 | ||
|
|
6656da5877 | ||
|
|
09e521727f | ||
|
|
cab66a2d6e | ||
|
|
8eabd4f303 | ||
|
|
e3ced84885 | ||
|
|
0929ab577d | ||
|
|
2228733abc | ||
|
|
e67c94d1bd | ||
|
|
ae5a661a47 | ||
|
|
0ff58cdc3e | ||
|
|
1747c74770 | ||
|
|
71669cf49c | ||
|
|
bd327e37eb | ||
|
|
bdce9a8170 | ||
|
|
7e5598d084 | ||
|
|
440fca6535 | ||
|
|
6635edd35c | ||
|
|
93ad40efbb | ||
|
|
27dc8b5b2c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
|||||||
/logs
|
/logs
|
||||||
/build
|
/build
|
||||||
/projects/*/logs
|
/projects/*/logs
|
||||||
|
/projects/fabric/fabricloader.log
|
||||||
/projects/*/build
|
/projects/*/build
|
||||||
/buildSrc/build
|
/buildSrc/build
|
||||||
/out
|
/out
|
||||||
|
|||||||
15
.reuse/dep5
15
.reuse/dep5
@@ -53,6 +53,7 @@ Files:
|
|||||||
projects/common/src/main/resources/assets/computercraft/textures/*
|
projects/common/src/main/resources/assets/computercraft/textures/*
|
||||||
projects/common/src/main/resources/pack.mcmeta
|
projects/common/src/main/resources/pack.mcmeta
|
||||||
projects/common/src/main/resources/pack.png
|
projects/common/src/main/resources/pack.png
|
||||||
|
projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
||||||
@@ -62,11 +63,23 @@ Copyright: 2011 Daniel Ratcliffe
|
|||||||
License: LicenseRef-CCPL
|
License: LicenseRef-CCPL
|
||||||
|
|
||||||
Files:
|
Files:
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/*
|
projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
|
||||||
Comment: Community-contributed license files
|
Comment: Community-contributed license files
|
||||||
Copyright: 2017 The CC: Tweaked Developers
|
Copyright: 2017 The CC: Tweaked Developers
|
||||||
License: LicenseRef-CCPL
|
License: LicenseRef-CCPL
|
||||||
|
|
||||||
|
Files:
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/*
|
||||||
|
Comment: Community-contributed license files
|
||||||
|
Copyright: 2017 The CC: Tweaked Developers
|
||||||
|
License: MPL-2.0
|
||||||
|
|
||||||
Files:
|
Files:
|
||||||
.github/*
|
.github/*
|
||||||
Comment:
|
Comment:
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -42,7 +42,6 @@ repositories {
|
|||||||
url "https://squiddev.cc/maven/"
|
url "https://squiddev.cc/maven/"
|
||||||
content {
|
content {
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
includeModule("org.squiddev", "Cobalt")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,9 +51,8 @@ dependencies {
|
|||||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
||||||
|
|
||||||
// Forge Gradle
|
// Forge Gradle
|
||||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")
|
||||||
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion"))
|
runtimeOnly("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion")
|
||||||
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
|
|
||||||
|
|
||||||
// Fabric Loom
|
// Fabric Loom
|
||||||
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
||||||
@@ -76,8 +74,8 @@ minecraft {
|
|||||||
```
|
```
|
||||||
|
|
||||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||||
subject to change at any point. If you depend on functionality outside the API, file an issue, and we can look into
|
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
||||||
exposing more features.
|
an issue to let me know!
|
||||||
|
|
||||||
We bundle the API sources with the jar, so documentation should be easily viewable within your editor. Alternatively,
|
We bundle the API sources with the jar, so documentation should be easily viewable within your editor. Alternatively,
|
||||||
the generated documentation [can be browsed online](https://tweaked.cc/javadoc/).
|
the generated documentation [can be browsed online](https://tweaked.cc/javadoc/).
|
||||||
|
|||||||
@@ -2,13 +2,19 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.JUnitExt
|
||||||
|
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||||
|
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||||
import org.jetbrains.gradle.ext.compiler
|
import org.jetbrains.gradle.ext.compiler
|
||||||
|
import org.jetbrains.gradle.ext.runConfigurations
|
||||||
import org.jetbrains.gradle.ext.settings
|
import org.jetbrains.gradle.ext.settings
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
publishing
|
publishing
|
||||||
alias(libs.plugins.taskTree)
|
alias(libs.plugins.taskTree)
|
||||||
alias(libs.plugins.githubRelease)
|
alias(libs.plugins.githubRelease)
|
||||||
|
alias(libs.plugins.gradleVersions)
|
||||||
|
alias(libs.plugins.versionCatalogUpdate)
|
||||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||||
id("cc-tweaked")
|
id("cc-tweaked")
|
||||||
}
|
}
|
||||||
@@ -38,6 +44,50 @@ githubRelease {
|
|||||||
|
|
||||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
|
|
||||||
|
idea.project.settings.runConfigurations {
|
||||||
|
register<JUnitExt>("Core Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.core.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("CraftOS Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.core.test"
|
||||||
|
className = "dan200.computercraft.core.ComputerTestDelegate"
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("CraftOS Tests (Fast)") {
|
||||||
|
vmParameters = "-ea -Dcc.skip_keywords=slow"
|
||||||
|
moduleName = "${idea.project.name}.core.test"
|
||||||
|
className = "dan200.computercraft.core.ComputerTestDelegate"
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("Common Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.common.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("Fabric Tests") {
|
||||||
|
val fabricProject = evaluationDependsOn(":fabric")
|
||||||
|
val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods
|
||||||
|
.joinToString(File.pathSeparator + File.pathSeparator) { modSettings ->
|
||||||
|
SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath }
|
||||||
|
}
|
||||||
|
|
||||||
|
vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup"
|
||||||
|
moduleName = "${idea.project.name}.fabric.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("Forge Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.forge.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
idea.project.settings.compiler.javac {
|
idea.project.settings.compiler.javac {
|
||||||
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
||||||
// and errors. Loop through our source sets and find the appropriate flags.
|
// and errors. Loop through our source sets and find the appropriate flags.
|
||||||
@@ -54,3 +104,9 @@ idea.project.settings.compiler.javac {
|
|||||||
}
|
}
|
||||||
.toMap()
|
.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
versionCatalogUpdate {
|
||||||
|
sortByKey.set(false)
|
||||||
|
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
|
||||||
|
keep { keepUnusedLibraries.set(true) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
`java-gradle-plugin`
|
`java-gradle-plugin`
|
||||||
`kotlin-dsl`
|
`kotlin-dsl`
|
||||||
|
alias(libs.plugins.gradleVersions)
|
||||||
|
alias(libs.plugins.versionCatalogUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicated in settings.gradle.kts
|
// Duplicated in settings.gradle.kts
|
||||||
@@ -12,25 +14,14 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
|
||||||
maven("https://maven.minecraftforge.net") {
|
maven("https://maven.neoforged.net/releases") {
|
||||||
name = "Forge"
|
name = "NeoForge"
|
||||||
content {
|
content {
|
||||||
includeGroup("net.minecraftforge")
|
includeGroup("net.minecraftforge")
|
||||||
includeGroup("net.minecraftforge.gradle")
|
includeGroup("net.neoforged")
|
||||||
}
|
includeGroup("net.neoforged.gradle")
|
||||||
}
|
includeModule("codechicken", "DiffPatch")
|
||||||
|
includeModule("net.covers1624", "Quack")
|
||||||
maven("https://maven.parchmentmc.org") {
|
|
||||||
name = "Librarian"
|
|
||||||
content {
|
|
||||||
includeGroupByRegex("^org\\.parchmentmc.*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maven("https://repo.spongepowered.org/repository/maven-public/") {
|
|
||||||
name = "Sponge"
|
|
||||||
content {
|
|
||||||
includeGroup("org.spongepowered")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +31,13 @@ repositories {
|
|||||||
includeGroup("net.fabricmc")
|
includeGroup("net.fabricmc")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maven("https://squiddev.cc/maven") {
|
||||||
|
name = "SquidDev"
|
||||||
|
content {
|
||||||
|
includeGroup("cc.tweaked.vanilla-extract")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -49,11 +47,10 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.curseForgeGradle)
|
implementation(libs.curseForgeGradle)
|
||||||
implementation(libs.fabric.loom)
|
implementation(libs.fabric.loom)
|
||||||
implementation(libs.forgeGradle)
|
implementation(libs.ideaExt)
|
||||||
implementation(libs.librarian)
|
|
||||||
implementation(libs.minotaur)
|
implementation(libs.minotaur)
|
||||||
implementation(libs.vineflower)
|
implementation(libs.neoGradle.userdev)
|
||||||
implementation(libs.vanillaGradle)
|
implementation(libs.vanillaExtract)
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
@@ -74,3 +71,9 @@ gradlePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
versionCatalogUpdate {
|
||||||
|
sortByKey.set(false)
|
||||||
|
keep { keepUnusedLibraries.set(true) }
|
||||||
|
catalogFile.set(file("../gradle/libs.versions.toml"))
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import cc.tweaked.gradle.MinecraftConfigurations
|
|||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
id("fabric-loom")
|
id("fabric-loom")
|
||||||
id("io.github.juuxel.loom-vineflower")
|
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,8 @@ import cc.tweaked.gradle.IdeaRunConfigurations
|
|||||||
import cc.tweaked.gradle.MinecraftConfigurations
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("net.minecraftforge.gradle")
|
|
||||||
// We must apply java-convention after Forge, as we need the fg extension to be present.
|
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
id("org.parchmentmc.librarian.forgegradle")
|
id("net.neoforged.gradle.userdev")
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.apply(CCTweakedPlugin::class.java)
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
@@ -21,10 +19,20 @@ plugins.apply(CCTweakedPlugin::class.java)
|
|||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
modIdentifier("computercraft")
|
||||||
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
|
}
|
||||||
|
|
||||||
accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg"))
|
subsystems {
|
||||||
|
parchment {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
minecraftVersion = libs.findVersion("parchmentMc").get().toString()
|
||||||
|
mappingsVersion = libs.findVersion("parchment").get().toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
implementation("net.neoforged:neoforge:${libs.findVersion("neoForge").get()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftConfigurations.setup(project)
|
MinecraftConfigurations.setup(project)
|
||||||
@@ -32,13 +40,3 @@ MinecraftConfigurations.setup(project)
|
|||||||
extensions.configure(CCTweakedExtension::class.java) {
|
extensions.configure(CCTweakedExtension::class.java) {
|
||||||
linters(minecraft = true, loader = "forge")
|
linters(minecraft = true, loader = "forge")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
|
||||||
"minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.configureEach {
|
|
||||||
// genIntellijRuns isn't registered until much later, so we need this silly hijinks.
|
|
||||||
if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,24 +40,12 @@ repositories {
|
|||||||
|
|
||||||
val mainMaven = maven("https://squiddev.cc/maven") {
|
val mainMaven = maven("https://squiddev.cc/maven") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
content {
|
|
||||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
|
||||||
includeModule("org.spongepowered", "mixin")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exclusiveContent {
|
exclusiveContent {
|
||||||
forRepositories(mainMaven)
|
forRepositories(mainMaven)
|
||||||
|
|
||||||
// Include the ForgeGradle repository if present. This requires that ForgeGradle is already present, which we
|
|
||||||
// enforce in our Forge overlay.
|
|
||||||
val fg =
|
|
||||||
project.extensions.findByType(net.minecraftforge.gradle.userdev.DependencyManagementExtension::class.java)
|
|
||||||
if (fg != null) forRepositories(fg.repository)
|
|
||||||
|
|
||||||
filter {
|
filter {
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
includeModule("org.squiddev", "Cobalt")
|
|
||||||
// Things we mirror
|
// Things we mirror
|
||||||
includeGroup("commoble.morered")
|
includeGroup("commoble.morered")
|
||||||
includeGroup("dev.architectury")
|
includeGroup("dev.architectury")
|
||||||
@@ -77,6 +65,12 @@ dependencies {
|
|||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
checkstyle(libs.findLibrary("checkstyle").get())
|
checkstyle(libs.findLibrary("checkstyle").get())
|
||||||
|
|
||||||
|
constraints {
|
||||||
|
checkstyle("org.codehaus.plexus:plexus-container-default:2.1.1") {
|
||||||
|
because("2.1.0 depends on deprecated Google collections module")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errorprone(libs.findLibrary("errorProne-core").get())
|
errorprone(libs.findLibrary("errorProne-core").get())
|
||||||
errorprone(libs.findLibrary("nullAway").get())
|
errorprone(libs.findLibrary("nullAway").get())
|
||||||
}
|
}
|
||||||
@@ -202,6 +196,7 @@ spotless {
|
|||||||
val ktlintConfig = mapOf(
|
val ktlintConfig = mapOf(
|
||||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
"ktlint_standard_no-wildcard-imports" to "disabled",
|
||||||
"ktlint_standard_class-naming" to "disabled",
|
"ktlint_standard_class-naming" to "disabled",
|
||||||
|
"ktlint_standard_function-naming" to "disabled",
|
||||||
"ij_kotlin_allow_trailing_comma" to "true",
|
"ij_kotlin_allow_trailing_comma" to "true",
|
||||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,25 +10,31 @@ import cc.tweaked.gradle.MinecraftConfigurations
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
id("org.spongepowered.gradle.vanilla")
|
id("cc.tweaked.vanilla-extract")
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.apply(CCTweakedPlugin::class.java)
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
|
|
||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
version(mcVersion)
|
version(mcVersion)
|
||||||
|
|
||||||
|
mappings {
|
||||||
|
parchment(libs.findVersion("parchmentMc").get().toString(), libs.findVersion("parchment").get().toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
unpick(libs.findLibrary("yarn").get())
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
|
||||||
|
|
||||||
// Depend on error prone annotations to silence a lot of compile warnings.
|
// Depend on error prone annotations to silence a lot of compile warnings.
|
||||||
compileOnlyApi(libs.findLibrary("errorProne.annotations").get())
|
compileOnly(libs.findLibrary("errorProne.annotations").get())
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftConfigurations.setup(project)
|
MinecraftConfigurations.setupBasic(project)
|
||||||
|
|
||||||
extensions.configure(CCTweakedExtension::class.java) {
|
extensions.configure(CCTweakedExtension::class.java) {
|
||||||
linters(minecraft = true, loader = null)
|
linters(minecraft = true, loader = null)
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import org.gradle.api.GradleException
|
|||||||
import org.gradle.api.NamedDomainObjectProvider
|
import org.gradle.api.NamedDomainObjectProvider
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.Task
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.artifacts.Dependency
|
||||||
import org.gradle.api.attributes.TestSuiteType
|
import org.gradle.api.attributes.TestSuiteType
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.SetProperty
|
import org.gradle.api.provider.SetProperty
|
||||||
import org.gradle.api.reporting.ReportingExtension
|
import org.gradle.api.reporting.ReportingExtension
|
||||||
@@ -73,11 +75,17 @@ abstract class CCTweakedExtension(
|
|||||||
*/
|
*/
|
||||||
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependencies excluded from published artifacts.
|
||||||
|
*/
|
||||||
|
private val excludedDeps: ListProperty<Dependency> = project.objects.listProperty(Dependency::class.java)
|
||||||
|
|
||||||
/** All source sets referenced by this project. */
|
/** All source sets referenced by this project. */
|
||||||
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sourceDirectories.finalizeValueOnRead()
|
sourceDirectories.finalizeValueOnRead()
|
||||||
|
excludedDeps.finalizeValueOnRead()
|
||||||
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +181,7 @@ abstract class CCTweakedExtension(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||||
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
|
||||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||||
|
|
||||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||||
@@ -185,7 +193,7 @@ abstract class CCTweakedExtension(
|
|||||||
jacoco.applyTo(this)
|
jacoco.applyTo(this)
|
||||||
extensions.configure(JacocoTaskExtension::class.java) {
|
extensions.configure(JacocoTaskExtension::class.java) {
|
||||||
includes = listOf("dan200.computercraft.*")
|
includes = listOf("dan200.computercraft.*")
|
||||||
classDumpDir = classDump
|
classDumpDir = classDump.get().asFile
|
||||||
|
|
||||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
// Older versions of modlauncher don't include a protection domain (and thus no code
|
||||||
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
||||||
@@ -246,6 +254,20 @@ abstract class CCTweakedExtension(
|
|||||||
).resolve().single()
|
).resolve().single()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude a dependency from being published in Maven.
|
||||||
|
*/
|
||||||
|
fun exclude(dep: Dependency) {
|
||||||
|
excludedDeps.add(dep)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure a [MavenDependencySpec].
|
||||||
|
*/
|
||||||
|
fun configureExcludes(spec: MavenDependencySpec) {
|
||||||
|
for (dep in excludedDeps.get()) spec.exclude(dep)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
||||||
private val IGNORED_USERS = setOf(
|
private val IGNORED_USERS = setOf(
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.plugins.JavaPlugin
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||||
|
import org.jetbrains.gradle.ext.IdeaExtPlugin
|
||||||
|
import org.jetbrains.gradle.ext.runConfigurations
|
||||||
|
import org.jetbrains.gradle.ext.settings
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures projects to match a shared configuration.
|
* Configures projects to match a shared configuration.
|
||||||
@@ -21,6 +25,20 @@ class CCTweakedPlugin : Plugin<Project> {
|
|||||||
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
||||||
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.plugins.withType(IdeaExtPlugin::class.java) { extendIdea(project) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the [IdeaExtPlugin] plugin's `runConfiguration` container to also support [JUnitExt].
|
||||||
|
*/
|
||||||
|
private fun extendIdea(project: Project) {
|
||||||
|
val ideaModel = project.extensions.findByName("idea") as IdeaModel? ?: return
|
||||||
|
val ideaProject = ideaModel.project ?: return
|
||||||
|
|
||||||
|
ideaProject.settings.runConfigurations {
|
||||||
|
registerFactory(JUnitExt::class.java) { name -> project.objects.newInstance(JUnitExt::class.java, name) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||||
|
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||||
|
import org.gradle.api.artifacts.component.ModuleComponentSelector
|
||||||
|
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||||
|
import org.gradle.api.artifacts.result.DependencyResult
|
||||||
|
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
|
|
||||||
|
abstract class DependencyCheck : DefaultTask() {
|
||||||
|
@get:Input
|
||||||
|
abstract val configuration: ListProperty<Configuration>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping of module coordinates (`group:module`) to versions, overriding the requested version.
|
||||||
|
*/
|
||||||
|
@get:Input
|
||||||
|
abstract val overrides: MapProperty<String, String>
|
||||||
|
|
||||||
|
init {
|
||||||
|
description = "Check :core's dependencies are consistent with Minecraft's."
|
||||||
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
|
||||||
|
configuration.finalizeValueOnRead()
|
||||||
|
overrides.finalizeValueOnRead()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override a module with a different version.
|
||||||
|
*/
|
||||||
|
fun override(module: Provider<MinimalExternalModuleDependency>, version: String) {
|
||||||
|
overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun run() {
|
||||||
|
var ok = true
|
||||||
|
for (configuration in configuration.get()) {
|
||||||
|
configuration.incoming.resolutionResult.allDependencies {
|
||||||
|
if (!check(this@allDependencies)) ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
throw GradleException("Mismatched versions in Minecraft dependencies. gradle/libs.versions.toml may need updating.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun check(dependency: DependencyResult): Boolean {
|
||||||
|
if (dependency !is ResolvedDependencyResult) {
|
||||||
|
logger.warn("Found unexpected dependency result {}", dependency)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip dependencies on non-modules.
|
||||||
|
val requested = dependency.requested
|
||||||
|
if (requested !is ModuleComponentSelector) return true
|
||||||
|
|
||||||
|
// If this dependency is specified within some project (so is non-transitive), or is pulled in via Minecraft,
|
||||||
|
// then check for consistency.
|
||||||
|
// It would be nice to be smarter about transitive dependencies, but avoiding false positives is hard.
|
||||||
|
val from = dependency.from.id
|
||||||
|
if (
|
||||||
|
from is ProjectComponentIdentifier ||
|
||||||
|
from is ModuleComponentIdentifier && (from.group == "net.minecraft" || from.group == "io.netty")
|
||||||
|
) {
|
||||||
|
// If the version is different between the requested and selected version, report an error.
|
||||||
|
val selected = dependency.selected.moduleVersion!!.version
|
||||||
|
val requestedVersion = overrides.get()["${requested.group}:${requested.module}"] ?: requested.version
|
||||||
|
if (requestedVersion != selected) {
|
||||||
|
logger.error("Requested dependency {} (via {}) but got version {}", requested, from, selected)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ package cc.tweaked.gradle
|
|||||||
|
|
||||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
import org.gradle.api.file.FileSystemLocation
|
import org.gradle.api.file.FileSystemLocation
|
||||||
import org.gradle.api.file.FileSystemLocationProperty
|
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.JavaExec
|
import org.gradle.api.tasks.JavaExec
|
||||||
@@ -129,3 +128,30 @@ fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
|
|||||||
|
|
||||||
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
|
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
|
||||||
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath
|
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the version immediately after the provided version.
|
||||||
|
*
|
||||||
|
* For example, given "1.2.3", this will return "1.2.4".
|
||||||
|
*/
|
||||||
|
fun getNextVersion(version: String): String {
|
||||||
|
// Split a version like x.y.z-SNAPSHOT into x.y.z and -SNAPSHOT
|
||||||
|
val dashIndex = version.indexOf('-')
|
||||||
|
val mainVersion = if (dashIndex < 0) version else version.substring(0, dashIndex)
|
||||||
|
|
||||||
|
// Find the last component in x.y.z and increment it.
|
||||||
|
val lastIndex = mainVersion.lastIndexOf('.')
|
||||||
|
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
|
||||||
|
val lastVersion = try {
|
||||||
|
version.substring(lastIndex + 1).toInt()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then append all components together.
|
||||||
|
val out = StringBuilder()
|
||||||
|
out.append(version, 0, lastIndex + 1)
|
||||||
|
out.append(lastVersion + 1)
|
||||||
|
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
|
||||||
|
return out.toString()
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,23 +4,59 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
import net.neoforged.gradle.common.runs.run.RunImpl
|
||||||
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal
|
import net.neoforged.gradle.common.runs.tasks.RunExec
|
||||||
|
import net.neoforged.gradle.dsl.common.extensions.RunnableSourceSet
|
||||||
|
import net.neoforged.gradle.dsl.common.runs.run.Run
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.api.tasks.JavaExec
|
import org.gradle.api.tasks.JavaExec
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||||
|
import org.gradle.kotlin.dsl.assign
|
||||||
|
import org.gradle.kotlin.dsl.create
|
||||||
|
import org.gradle.kotlin.dsl.findByType
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set [JavaExec] task to run a given [RunConfig].
|
* Set [JavaExec] task to run a given [RunConfig].
|
||||||
|
*
|
||||||
|
* See also [RunExec].
|
||||||
*/
|
*/
|
||||||
fun JavaExec.setRunConfig(config: RunConfig) {
|
fun JavaExec.setRunConfig(config: Run) {
|
||||||
dependsOn("prepareRuns")
|
mainClass.set(config.mainClass)
|
||||||
setRunConfigInternal(project, this, config)
|
workingDir = config.workingDirectory.get().asFile
|
||||||
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
|
argumentProviders.add { config.programArguments.get() }
|
||||||
|
jvmArgumentProviders.add { config.jvmArguments.get() }
|
||||||
|
|
||||||
|
environment(config.environmentVariables.get())
|
||||||
|
systemProperties(config.systemProperties.get())
|
||||||
|
|
||||||
|
config.modSources.get().forEach { classpath(it.runtimeClasspath) }
|
||||||
|
classpath(config.classpath)
|
||||||
|
classpath(config.dependencies.get().configuration)
|
||||||
|
|
||||||
|
(config as RunImpl).taskDependencies.forEach { dependsOn(it) }
|
||||||
|
|
||||||
javaLauncher.set(
|
javaLauncher.set(
|
||||||
project.extensions.getByType(JavaToolchainService::class.java)
|
project.extensions.getByType(JavaToolchainService::class.java)
|
||||||
.launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
|
.launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new [Run.modSource] with a specific mod id.
|
||||||
|
*/
|
||||||
|
fun Run.modSourceAs(sourceSet: SourceSet, mod: String) {
|
||||||
|
// NeoGradle requires a RunnableSourceSet to be present, so we inject it into other project's source sets.
|
||||||
|
val runnable = sourceSet.extensions.findByType<RunnableSourceSet>() ?: run {
|
||||||
|
val extension = sourceSet.extensions.create<RunnableSourceSet>(RunnableSourceSet.NAME, project)
|
||||||
|
extension.modIdentifier = mod
|
||||||
|
extension.modIdentifier.finalizeValueOnRead()
|
||||||
|
extension
|
||||||
|
}
|
||||||
|
if (runnable.modIdentifier.get() != mod) throw IllegalArgumentException("Multiple mod identifiers")
|
||||||
|
|
||||||
|
modSource(sourceSet)
|
||||||
}
|
}
|
||||||
|
|||||||
23
buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
Normal file
23
buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.jetbrains.gradle.ext.JUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of [JUnit] with a functional [className].
|
||||||
|
*
|
||||||
|
* See [#92](https://github.com/JetBrains/gradle-idea-ext-plugin/issues/92).
|
||||||
|
*/
|
||||||
|
open class JUnitExt @Inject constructor(nameParam: String) : JUnit(nameParam) {
|
||||||
|
override fun toMap(): MutableMap<String, *> {
|
||||||
|
val map = HashMap(super.toMap())
|
||||||
|
// Should be "class" instead of "className".
|
||||||
|
// See https://github.com/JetBrains/intellij-community/blob/9ba394021dc73a3926f13d6d6cdf434f9ee7046d/plugins/junit/src/com/intellij/execution/junit/JUnitRunConfigurationImporter.kt#L39
|
||||||
|
map["class"] = className
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ package cc.tweaked.gradle
|
|||||||
|
|
||||||
import org.gradle.api.artifacts.Dependency
|
import org.gradle.api.artifacts.Dependency
|
||||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||||
|
import org.gradle.api.artifacts.ProjectDependency
|
||||||
|
import org.gradle.api.plugins.BasePluginExtension
|
||||||
import org.gradle.api.publish.maven.MavenPublication
|
import org.gradle.api.publish.maven.MavenPublication
|
||||||
import org.gradle.api.specs.Spec
|
import org.gradle.api.specs.Spec
|
||||||
|
|
||||||
@@ -26,8 +28,13 @@ class MavenDependencySpec {
|
|||||||
|
|
||||||
fun exclude(dep: Dependency) {
|
fun exclude(dep: Dependency) {
|
||||||
exclude {
|
exclude {
|
||||||
|
// We have to cheat a little for project dependencies, as the project name doesn't match the artifact group.
|
||||||
|
val name = when (dep) {
|
||||||
|
is ProjectDependency -> dep.dependencyProject.extensions.getByType(BasePluginExtension::class.java).archivesName.get()
|
||||||
|
else -> dep.name
|
||||||
|
}
|
||||||
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
||||||
(dep.name.isNullOrEmpty() || dep.name == it.artifactId) &&
|
(name.isNullOrEmpty() || name == it.artifactId) &&
|
||||||
(dep.version.isNullOrEmpty() || dep.version == it.version)
|
(dep.version.isNullOrEmpty() || dep.version == it.version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,17 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import cc.tweaked.vanillaextract.configurations.Capabilities
|
||||||
|
import cc.tweaked.vanillaextract.configurations.MinecraftSetup
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.Configuration
|
|
||||||
import org.gradle.api.artifacts.ModuleDependency
|
import org.gradle.api.artifacts.ModuleDependency
|
||||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
import org.gradle.api.attributes.Bundling
|
|
||||||
import org.gradle.api.attributes.Category
|
|
||||||
import org.gradle.api.attributes.LibraryElements
|
|
||||||
import org.gradle.api.attributes.Usage
|
|
||||||
import org.gradle.api.attributes.java.TargetJvmVersion
|
|
||||||
import org.gradle.api.capabilities.Capability
|
|
||||||
import org.gradle.api.plugins.BasePlugin
|
import org.gradle.api.plugins.BasePlugin
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.api.tasks.SourceSet
|
import org.gradle.api.tasks.SourceSet
|
||||||
import org.gradle.api.tasks.bundling.Jar
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
import org.gradle.api.tasks.javadoc.Javadoc
|
import org.gradle.api.tasks.javadoc.Javadoc
|
||||||
import org.gradle.kotlin.dsl.get
|
import org.gradle.kotlin.dsl.get
|
||||||
import org.gradle.kotlin.dsl.named
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This sets up a separate client-only source set, and extends that and the main/common source set with additional
|
* This sets up a separate client-only source set, and extends that and the main/common source set with additional
|
||||||
@@ -59,31 +53,13 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
}
|
}
|
||||||
configurations.named(client.implementationConfigurationName) { extendsFrom(clientApi) }
|
configurations.named(client.implementationConfigurationName) { extendsFrom(clientApi) }
|
||||||
|
|
||||||
/*
|
|
||||||
Now add outgoing variants for the main and common source sets that we can consume downstream. This is possibly
|
|
||||||
the worst way to do things, but unfortunately the alternatives don't actually work very well:
|
|
||||||
|
|
||||||
- Just using source set outputs: This means dependencies don't propagate, which means when :fabric depends
|
|
||||||
on :fabric-api, we don't inherit the fake :common-api in IDEA.
|
|
||||||
|
|
||||||
- Having separate common/main jars: Nice in principle, but unfortunately Forge needs a separate deobf jar
|
|
||||||
task (as the original jar is obfuscated), and IDEA is not able to map its output back to a source set.
|
|
||||||
|
|
||||||
This works for now, but is incredibly brittle. It's part of the reason we can't use testFixtures inside our
|
|
||||||
MC projects, as that adds a project(self) -> test dependency, which would pull in the jar instead.
|
|
||||||
|
|
||||||
Note we register a fake client jar here. It's not actually needed, but is there to make sure IDEA has
|
|
||||||
a way to tell that client classes are needed at runtime.
|
|
||||||
|
|
||||||
I'm so sorry, deeply aware how cursed this is.
|
|
||||||
*/
|
|
||||||
setupOutgoing(main, "CommonOnly")
|
|
||||||
project.tasks.register(client.jarTaskName, Jar::class.java) {
|
project.tasks.register(client.jarTaskName, Jar::class.java) {
|
||||||
description = "An empty jar standing in for the client classes."
|
description = "An empty jar standing in for the client classes."
|
||||||
group = BasePlugin.BUILD_GROUP
|
group = BasePlugin.BUILD_GROUP
|
||||||
archiveClassifier.set("client")
|
archiveClassifier.set("client")
|
||||||
}
|
}
|
||||||
setupOutgoing(client)
|
|
||||||
|
MinecraftSetup(project).setupOutgoingConfigurations()
|
||||||
|
|
||||||
// Reset the client classpath (Loom configures it slightly differently to this) and add a main -> client
|
// Reset the client classpath (Loom configures it slightly differently to this) and add a main -> client
|
||||||
// dependency. Here we /can/ use source set outputs as we add transitive deps by patching the classpath. Nasty,
|
// dependency. Here we /can/ use source set outputs as we add transitive deps by patching the classpath. Nasty,
|
||||||
@@ -106,88 +82,39 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
project.tasks.named("jar", Jar::class.java) { from(client.output) }
|
project.tasks.named("jar", Jar::class.java) { from(client.output) }
|
||||||
project.tasks.named("sourcesJar", Jar::class.java) { from(client.allSource) }
|
project.tasks.named("sourcesJar", Jar::class.java) { from(client.allSource) }
|
||||||
|
|
||||||
|
setupBasic()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupBasic() {
|
||||||
|
val client = sourceSets["client"]
|
||||||
|
|
||||||
project.extensions.configure(CCTweakedExtension::class.java) {
|
project.extensions.configure(CCTweakedExtension::class.java) {
|
||||||
sourceDirectories.add(SourceSetReference.internal(client))
|
sourceDirectories.add(SourceSetReference.internal(client))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupOutgoing(sourceSet: SourceSet, suffix: String = "") {
|
// Register a task to check there are no conflicts with the core project.
|
||||||
setupOutgoing("${sourceSet.apiElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_API)) {
|
val checkDependencyConsistency =
|
||||||
description = "API elements for ${sourceSet.name}"
|
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
|
||||||
extendsFrom(configurations[sourceSet.apiConfigurationName])
|
// We need to check both the main and client classpath *configurations*, as the actual configuration
|
||||||
}
|
configuration.add(configurations.named(main.runtimeClasspathConfigurationName))
|
||||||
|
configuration.add(configurations.named(client.runtimeClasspathConfigurationName))
|
||||||
setupOutgoing("${sourceSet.runtimeElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_RUNTIME)) {
|
|
||||||
description = "Runtime elements for ${sourceSet.name}"
|
|
||||||
extendsFrom(configurations[sourceSet.implementationConfigurationName], configurations[sourceSet.runtimeOnlyConfigurationName])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up an outgoing configuration for a specific source set. We set an additional "main" or "client" capability
|
|
||||||
* (depending on the source set name) which allows downstream projects to consume them separately (see
|
|
||||||
* [DependencyHandler.commonClasses] and [DependencyHandler.clientClasses]).
|
|
||||||
*/
|
|
||||||
private fun setupOutgoing(name: String, sourceSet: SourceSet, usage: Usage, configure: Configuration.() -> Unit) {
|
|
||||||
configurations.register(name) {
|
|
||||||
isVisible = false
|
|
||||||
isCanBeConsumed = true
|
|
||||||
isCanBeResolved = false
|
|
||||||
|
|
||||||
configure(this)
|
|
||||||
|
|
||||||
attributes {
|
|
||||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
|
|
||||||
attribute(Usage.USAGE_ATTRIBUTE, usage)
|
|
||||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
|
|
||||||
attributeProvider(
|
|
||||||
TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE,
|
|
||||||
java.toolchain.languageVersion.map { it.asInt() },
|
|
||||||
)
|
|
||||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
|
||||||
}
|
}
|
||||||
|
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
||||||
outgoing {
|
|
||||||
capability(BasicOutgoingCapability(project, sourceSet.name))
|
|
||||||
|
|
||||||
// We have two outgoing variants here: the original jar and the classes.
|
|
||||||
artifact(project.tasks.named(sourceSet.jarTaskName))
|
|
||||||
|
|
||||||
variants.create("classes") {
|
|
||||||
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
|
|
||||||
sourceSet.output.classesDirs.forEach { artifact(it) { builtBy(sourceSet.output) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun setupBasic(project: Project) {
|
||||||
|
MinecraftConfigurations(project).setupBasic()
|
||||||
|
}
|
||||||
|
|
||||||
fun setup(project: Project) {
|
fun setup(project: Project) {
|
||||||
MinecraftConfigurations(project).setup()
|
MinecraftConfigurations(project).setup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BasicIncomingCapability(private val module: ModuleDependency, private val name: String) : Capability {
|
fun DependencyHandler.clientClasses(notation: Any): ModuleDependency =
|
||||||
override fun getGroup(): String = module.group!!
|
Capabilities.clientClasses(create(notation) as ModuleDependency)
|
||||||
override fun getName(): String = "${module.name}-$name"
|
|
||||||
override fun getVersion(): String? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BasicOutgoingCapability(private val project: Project, private val name: String) : Capability {
|
fun DependencyHandler.commonClasses(notation: Any): ModuleDependency =
|
||||||
override fun getGroup(): String = project.group.toString()
|
Capabilities.commonClasses(create(notation) as ModuleDependency)
|
||||||
override fun getName(): String = "${project.name}-$name"
|
|
||||||
override fun getVersion(): String = project.version.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DependencyHandler.clientClasses(notation: Any): ModuleDependency {
|
|
||||||
val dep = create(notation) as ModuleDependency
|
|
||||||
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "client")) }
|
|
||||||
return dep
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DependencyHandler.commonClasses(notation: Any): ModuleDependency {
|
|
||||||
val dep = create(notation) as ModuleDependency
|
|
||||||
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "main")) }
|
|
||||||
return dep
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.invocation.Gradle
|
||||||
@@ -58,21 +57,13 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
if (!clientDebug) systemProperty("cctest.client", "")
|
if (!clientDebug) systemProperty("cctest.client", "")
|
||||||
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
|
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
|
||||||
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||||
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
workingDir(project.layout.buildDirectory.dir("gametest/$name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setTestProperties()
|
setTestProperties()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set this task to run a given [RunConfig].
|
|
||||||
*/
|
|
||||||
fun setRunConfig(config: RunConfig) {
|
|
||||||
(this as JavaExec).setRunConfig(config)
|
|
||||||
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy configuration from a task with the given name.
|
* Copy configuration from a task with the given name.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package net.minecraftforge.gradle.common.util.runs
|
|
||||||
|
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
|
||||||
import org.gradle.api.Project
|
|
||||||
import org.gradle.process.CommandLineArgumentProvider
|
|
||||||
import org.gradle.process.JavaExecSpec
|
|
||||||
import java.io.File
|
|
||||||
import java.util.function.Supplier
|
|
||||||
import java.util.stream.Collectors
|
|
||||||
import java.util.stream.Stream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up a [JavaExecSpec] to execute a [RunConfig].
|
|
||||||
*
|
|
||||||
* [MinecraftRunTask] sets up all its properties when the task is executed, rather than when configured. As such, it's
|
|
||||||
* not possible to use [cc.tweaked.gradle.copyToFull] like we do for Fabric. Instead, we set up the task manually.
|
|
||||||
*
|
|
||||||
* Unfortunately most of the functionality we need is package-private, and so we have to put our code into the package.
|
|
||||||
*/
|
|
||||||
internal fun setRunConfigInternal(project: Project, spec: JavaExecSpec, config: RunConfig) {
|
|
||||||
spec.workingDir = File(config.workingDirectory)
|
|
||||||
|
|
||||||
spec.mainClass.set(config.main)
|
|
||||||
for (source in config.allSources) spec.classpath(source.runtimeClasspath)
|
|
||||||
|
|
||||||
val originalTask = project.tasks.named(config.taskName, MinecraftRunTask::class.java)
|
|
||||||
|
|
||||||
// Add argument and JVM argument via providers, to be as lazy as possible with fetching artifacts.
|
|
||||||
val lazyTokens = RunConfigGenerator.configureTokensLazy(
|
|
||||||
project, config, RunConfigGenerator.mapModClassesToGradle(project, config),
|
|
||||||
originalTask.get().minecraftArtifacts,
|
|
||||||
originalTask.get().runtimeClasspathArtifacts,
|
|
||||||
)
|
|
||||||
spec.argumentProviders.add(
|
|
||||||
CommandLineArgumentProvider {
|
|
||||||
RunConfigGenerator.getArgsStream(config, lazyTokens, false).toList()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
spec.jvmArgumentProviders.add(
|
|
||||||
CommandLineArgumentProvider {
|
|
||||||
(if (config.isClient) config.jvmArgs + originalTask.get().additionalClientArgs.get() else config.jvmArgs).map { config.replace(lazyTokens, it) } +
|
|
||||||
config.properties.map { (k, v) -> "-D${k}=${config.replace(lazyTokens, v)}" }
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
for ((key, value) in config.environment) spec.environment(key, config.replace(lazyTokens, value))
|
|
||||||
}
|
|
||||||
@@ -112,7 +112,9 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="LambdaParameterName" />
|
<module name="LambdaParameterName" />
|
||||||
<module name="LocalFinalVariableName" />
|
<module name="LocalFinalVariableName" />
|
||||||
<module name="LocalVariableName" />
|
<module name="LocalVariableName" />
|
||||||
<module name="MemberName" />
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^\$?[a-z][a-zA-Z0-9]*$" />
|
||||||
|
</module>
|
||||||
<module name="MethodName">
|
<module name="MethodName">
|
||||||
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
||||||
</module>
|
</module>
|
||||||
@@ -122,7 +124,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
</module>
|
</module>
|
||||||
<module name="ParameterName" />
|
<module name="ParameterName" />
|
||||||
<module name="StaticVariableName">
|
<module name="StaticVariableName">
|
||||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
<property name="format" value="^[a-z][a-zA-Z0-9]*$" />
|
||||||
</module>
|
</module>
|
||||||
<module name="TypeName" />
|
<module name="TypeName" />
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ see: key To listen to any key press.
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`char`] event is fired when a character is typed on the keyboard.
|
The [`char`] event is fired when a character is typed on the keyboard.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
|
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
|
||||||
|
|
||||||
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
|
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
|
||||||
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.BinaryReadHandle`] with an
|
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.ReadHandle`] with an
|
||||||
additional [getName][`TransferredFile.getName`] method.
|
additional [getName][`TransferredFile.getName`] method.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module: [kind=event] key
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when any key is pressed while the terminal is focused.
|
This event is fired when any key is pressed while the terminal is focused.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ see: keys For a lookup table of the given keys.
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module: [kind=event] mouse_click
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when the terminal is clicked with a mouse. This event is only fired on advanced computers (including
|
This event is fired when the terminal is clicked with a mouse. This event is only fired on advanced computers (including
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ see: mouse_click For when a mouse button is initially pressed.
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired every time the mouse is moved while a mouse button is being held.
|
This event is fired every time the mouse is moved while a mouse button is being held.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module: [kind=event] mouse_scroll
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when a mouse wheel is scrolled in the terminal.
|
This event is fired when a mouse wheel is scrolled in the terminal.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module: [kind=event] mouse_up
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, whic
|
|||||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
||||||
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||||
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
||||||
[`fs.BinaryReadHandle.read`] if you prefer.
|
[`fs.ReadHandle.read`] if you prefer.
|
||||||
|
|
||||||
## Processing audio
|
## Processing audio
|
||||||
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
||||||
|
|||||||
81
doc/reference/breaking_changes.md
Normal file
81
doc/reference/breaking_changes.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
module: [kind=reference] breaking_changes
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Incompatibilities between versions
|
||||||
|
|
||||||
|
CC: Tweaked tries to remain as compatible between versions as possible, meaning most programs written for older version
|
||||||
|
of the mod should run fine on later versions.
|
||||||
|
|
||||||
|
> [External peripherals][!WARNING]
|
||||||
|
>
|
||||||
|
> While CC: Tweaked is relatively stable across versions, this may not be true for other mods which add their own
|
||||||
|
> peripherals. Older programs which interact with external blocks may not work on newer versions of the game.
|
||||||
|
|
||||||
|
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
|
||||||
|
as documentation for breaking changes and "gotchas" one should look out for between versions.
|
||||||
|
|
||||||
|
## CC: Tweaked 1.109.0 to 1.109.3 {#cct-1.109}
|
||||||
|
|
||||||
|
- Update to Lua 5.2:
|
||||||
|
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
|
||||||
|
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
|
||||||
|
now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
|
||||||
|
with other functions, and `setfenv` will have no effect.
|
||||||
|
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
|
||||||
|
environment.
|
||||||
|
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
|
||||||
|
- `math.random` now uses Lua 5.4's random number generator.
|
||||||
|
|
||||||
|
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
|
||||||
|
|
||||||
|
## Minecraft 1.13 {#mc-1.13}
|
||||||
|
- The "key code" for [`key`] and [`key_up`] events has changed, due to Minecraft updating to LWJGL 3. Make sure you're
|
||||||
|
using the constants provided by the [`keys`] API, rather than hard-coding numerical values.
|
||||||
|
|
||||||
|
Related to this change, the numpad enter key now has a different key code to the enter key. You may need to adjust
|
||||||
|
your programs to handle both. (Note, the `keys.numpadEnter` constant was defined in pre-1.13 versions of CC, but the
|
||||||
|
`keys.enter` constant was queued when the key was pressed)
|
||||||
|
|
||||||
|
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
|
||||||
|
result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
|
||||||
|
|
||||||
|
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
|
||||||
|
more understandable format.
|
||||||
|
|
||||||
|
- Item and block names now represent a unique item type. For instance, wool is split into 16 separate items
|
||||||
|
(`minecraft:white_wool`, etc...) rather than a single `minecraft:wool` with each meta/damage value specifying the
|
||||||
|
colour.
|
||||||
|
|
||||||
|
- Custom ROMs are now provided using data packs rather than resource packs. This should mostly be a matter of renaming
|
||||||
|
the "assets" folder to "data", and placing it in "datapacks", but there are a couple of other gotchas to look out
|
||||||
|
for:
|
||||||
|
|
||||||
|
- Data packs [impose some restrictions on file names][legal_data_pack]. As a result, your programs and directories
|
||||||
|
must all be lower case.
|
||||||
|
- Due to how data packs are read by CC: Tweaked, you may need to use the `/reload` command to see changes to your
|
||||||
|
pack show up on the computer.
|
||||||
|
|
||||||
|
See [the example datapack][datapack-example] for how to get started.
|
||||||
|
|
||||||
|
- Turtles can now be waterlogged and move "through" water sources rather than breaking them.
|
||||||
|
|
||||||
|
## CC: Tweaked 1.88.0 {#cc-1.88}
|
||||||
|
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
|
||||||
|
|
||||||
|
## ComputerCraft 1.80pr1 {#cc-1.80}
|
||||||
|
- Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
|
||||||
|
will not be accessible outside of this program.
|
||||||
|
|
||||||
|
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
||||||
|
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
||||||
|
|
||||||
|
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
|
||||||
|
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
||||||
|
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
||||||
@@ -9,17 +9,19 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
# Lua 5.2/5.3 features in CC: Tweaked
|
# Lua 5.2/5.3 features in CC: Tweaked
|
||||||
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However, Cobalt and CC:T implement additional features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 features) that are not available in base 5.1. This page lists all of the compatibility for these newer versions.
|
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.2. However, Cobalt and CC:T implement additional
|
||||||
|
features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 and 5.1 features). This page lists all of the
|
||||||
|
compatibility for these newer versions.
|
||||||
|
|
||||||
## Lua 5.2
|
## Lua 5.2
|
||||||
| Feature | Supported? | Notes |
|
| Feature | Supported? | Notes |
|
||||||
|---------------------------------------------------------------|------------|-------------------------------------------------------------------|
|
|---------------------------------------------------------------|------------|-------------------------------------------------------------------|
|
||||||
| `goto`/labels | ❌ | |
|
| `goto`/labels | ✔ | |
|
||||||
| `_ENV` | 🔶 | The `_ENV` global points to `getfenv()`, but it cannot be set. |
|
| `_ENV` | ✔ | |
|
||||||
| `\z` escape | ✔ | |
|
| `\z` escape | ✔ | |
|
||||||
| `\xNN` escape | ✔ | |
|
| `\xNN` escape | ✔ | |
|
||||||
| Hex literal fractional/exponent parts | ✔ | |
|
| Hex literal fractional/exponent parts | ✔ | |
|
||||||
| Empty statements | ❌ | |
|
| Empty statements | ✔ | |
|
||||||
| `__len` metamethod | ✔ | |
|
| `__len` metamethod | ✔ | |
|
||||||
| `__ipairs` metamethod | ❌ | Deprecated in Lua 5.3. `ipairs` uses `__len`/`__index` instead. |
|
| `__ipairs` metamethod | ❌ | Deprecated in Lua 5.3. `ipairs` uses `__len`/`__index` instead. |
|
||||||
| `__pairs` metamethod | ✔ | |
|
| `__pairs` metamethod | ✔ | |
|
||||||
@@ -27,12 +29,12 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
|
|||||||
| `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. |
|
| `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. |
|
||||||
| New `load` syntax | ✔ | |
|
| New `load` syntax | ✔ | |
|
||||||
| `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. |
|
| `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. |
|
||||||
| Removed `loadstring` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
| Removed `loadstring` | ❌ | |
|
||||||
| Removed `getfenv`, `setfenv` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
| Removed `getfenv`, `setfenv` | 🔶 | Only supports closures with an `_ENV` upvalue. |
|
||||||
| `rawlen` function | ✔ | |
|
| `rawlen` function | ✔ | |
|
||||||
| Negative index to `select` | ✔ | |
|
| Negative index to `select` | ✔ | |
|
||||||
| Removed `unpack` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
| Removed `unpack` | ❌ | |
|
||||||
| Arguments to `xpcall` | ✔ | |
|
| Arguments to `xpcall` | ✔ | |
|
||||||
| Second return value from `coroutine.running` | ✔ | |
|
| Second return value from `coroutine.running` | ✔ | |
|
||||||
| Removed `module` | ✔ | |
|
| Removed `module` | ✔ | |
|
||||||
| `package.loaders` -> `package.searchers` | ❌ | |
|
| `package.loaders` -> `package.searchers` | ❌ | |
|
||||||
@@ -40,14 +42,14 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
|
|||||||
| `package.config` | ✔ | |
|
| `package.config` | ✔ | |
|
||||||
| `package.searchpath` | ✔ | |
|
| `package.searchpath` | ✔ | |
|
||||||
| Removed `package.seeall` | ✔ | |
|
| Removed `package.seeall` | ✔ | |
|
||||||
| `string.dump` on functions with upvalues (blanks them out) | ✔ | |
|
| `string.dump` on functions with upvalues (blanks them out) | ❌ | `string.dump` is not supported |
|
||||||
| `string.rep` separator | ✔ | |
|
| `string.rep` separator | ✔ | |
|
||||||
| `%g` match group | ❌ | |
|
| `%g` match group | ❌ | |
|
||||||
| Removal of `%z` match group | ❌ | |
|
| Removal of `%z` match group | ❌ | |
|
||||||
| Removed `table.maxn` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
| Removed `table.maxn` | ❌ | |
|
||||||
| `table.pack`/`table.unpack` | ✔ | |
|
| `table.pack`/`table.unpack` | ✔ | |
|
||||||
| `math.log` base argument | ✔ | |
|
| `math.log` base argument | ✔ | |
|
||||||
| Removed `math.log10` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
| Removed `math.log10` | ❌ | |
|
||||||
| `*L` mode to `file:read` | ✔ | |
|
| `*L` mode to `file:read` | ✔ | |
|
||||||
| `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. |
|
| `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. |
|
||||||
| `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. |
|
| `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. |
|
||||||
@@ -61,7 +63,7 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
|
|||||||
| Tail call hooks | ❌ | |
|
| Tail call hooks | ❌ | |
|
||||||
| `=` prefix for chunks | ✔ | |
|
| `=` prefix for chunks | ✔ | |
|
||||||
| Yield across C boundary | ✔ | |
|
| Yield across C boundary | ✔ | |
|
||||||
| Removal of ambiguity error | ❌ | |
|
| Removal of ambiguity error | ✔ | |
|
||||||
| Identifiers may no longer use locale-dependent letters | ✔ | |
|
| Identifiers may no longer use locale-dependent letters | ✔ | |
|
||||||
| Ephemeron tables | ❌ | |
|
| Ephemeron tables | ❌ | |
|
||||||
| Identical functions may be reused | ❌ | Removed in Lua 5.4 |
|
| Identical functions may be reused | ❌ | Removed in Lua 5.4 |
|
||||||
|
|||||||
@@ -95,10 +95,10 @@ function pullEventRaw(filter) end
|
|||||||
-- nearest multiple of 0.05.
|
-- nearest multiple of 0.05.
|
||||||
function sleep(time) end
|
function sleep(time) end
|
||||||
|
|
||||||
--- Get the current CraftOS version (for example, `CraftOS 1.8`).
|
--- Get the current CraftOS version (for example, `CraftOS 1.9`).
|
||||||
--
|
--
|
||||||
-- This is defined by `bios.lua`. For the current version of CC:Tweaked, this
|
-- This is defined by `bios.lua`. For the current version of CC:Tweaked, this
|
||||||
-- should return `CraftOS 1.8`.
|
-- should return `CraftOS 1.9`.
|
||||||
--
|
--
|
||||||
-- @treturn string The current CraftOS version.
|
-- @treturn string The current CraftOS version.
|
||||||
-- @usage os.version()
|
-- @usage os.version()
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ kotlin.stdlib.default.dependency=false
|
|||||||
kotlin.jvm.target.validation.mode=error
|
kotlin.jvm.target.validation.mode=error
|
||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=false
|
isUnstable=true
|
||||||
modVersion=1.108.3
|
modVersion=1.109.6
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.20.1
|
mcVersion=1.20.4
|
||||||
|
|||||||
@@ -7,69 +7,71 @@
|
|||||||
# Minecraft
|
# Minecraft
|
||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
||||||
fabric-api = "0.86.1+1.20.1"
|
fabric-api = "0.93.1+1.20.4"
|
||||||
fabric-loader = "0.14.21"
|
fabric-loader = "0.15.3"
|
||||||
forge = "47.1.0"
|
neoForge = "20.4.161-beta"
|
||||||
forgeSpi = "6.0.0"
|
neoForgeSpi = "8.0.1"
|
||||||
mixin = "0.8.5"
|
mixin = "0.8.5"
|
||||||
parchment = "2023.08.20"
|
parchment = "2023.12.31"
|
||||||
parchmentMc = "1.20.1"
|
parchmentMc = "1.20.3"
|
||||||
|
yarn = "1.20.4+build.3"
|
||||||
|
|
||||||
# Normal dependencies
|
# Core dependencies (these versions are tied to the version Minecraft uses)
|
||||||
asm = "9.5"
|
fastutil = "8.5.12"
|
||||||
|
guava = "32.1.2-jre"
|
||||||
|
netty = "4.1.97.Final"
|
||||||
|
slf4j = "2.0.7"
|
||||||
|
|
||||||
|
# Core dependencies (independent of Minecraft)
|
||||||
|
asm = "9.6"
|
||||||
autoService = "1.1.1"
|
autoService = "1.1.1"
|
||||||
checkerFramework = "3.32.0"
|
checkerFramework = "3.42.0"
|
||||||
cobalt = "0.7.3"
|
cobalt = "0.9.1"
|
||||||
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
|
commonsCli = "1.6.0"
|
||||||
fastutil = "8.5.9"
|
jetbrainsAnnotations = "24.1.0"
|
||||||
guava = "31.1-jre"
|
|
||||||
jetbrainsAnnotations = "24.0.1"
|
|
||||||
jsr305 = "3.0.2"
|
jsr305 = "3.0.2"
|
||||||
jzlib = "1.1.3"
|
jzlib = "1.1.3"
|
||||||
kotlin = "1.8.10"
|
kotlin = "1.9.21"
|
||||||
kotlin-coroutines = "1.6.4"
|
kotlin-coroutines = "1.7.3"
|
||||||
netty = "4.1.82.Final"
|
|
||||||
nightConfig = "3.6.7"
|
nightConfig = "3.6.7"
|
||||||
slf4j = "2.0.1"
|
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
emi = "1.0.8+1.20.1"
|
emi = "1.0.30+1.20.4"
|
||||||
fabricPermissions = "0.3.20230723"
|
fabricPermissions = "0.3.20230723"
|
||||||
iris = "1.6.4+1.20"
|
iris = "1.6.14+1.20.4"
|
||||||
jei = "15.2.0.22"
|
jei = "17.3.0.48"
|
||||||
modmenu = "7.1.0"
|
modmenu = "9.0.0"
|
||||||
moreRed = "4.0.0.4"
|
moreRed = "4.0.0.4"
|
||||||
oculus = "1.2.5"
|
oculus = "1.2.5"
|
||||||
rei = "12.0.626"
|
rei = "14.0.688"
|
||||||
rubidium = "0.6.1"
|
rubidium = "0.6.1"
|
||||||
sodium = "mc1.20-0.4.10"
|
sodium = "mc1.20-0.4.10"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
byteBuddy = "1.14.7"
|
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
jqwik = "1.7.4"
|
jqwik = "1.8.2"
|
||||||
junit = "5.10.0"
|
junit = "5.10.1"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.8.0"
|
cctJavadoc = "1.8.2"
|
||||||
checkstyle = "10.12.3"
|
checkstyle = "10.12.6"
|
||||||
curseForgeGradle = "1.0.14"
|
curseForgeGradle = "1.1.18"
|
||||||
errorProne-core = "2.21.1"
|
errorProne-core = "2.23.0"
|
||||||
errorProne-plugin = "3.1.0"
|
errorProne-plugin = "3.1.0"
|
||||||
fabric-loom = "1.3.7"
|
fabric-loom = "1.5.7"
|
||||||
forgeGradle = "6.0.8"
|
githubRelease = "2.5.2"
|
||||||
githubRelease = "2.4.1"
|
gradleVersions = "0.50.0"
|
||||||
ideaExt = "1.1.7"
|
ideaExt = "1.1.7"
|
||||||
illuaminate = "0.1.0-44-g9ee0055"
|
illuaminate = "0.1.0-44-g9ee0055"
|
||||||
librarian = "1.+"
|
lwjgl = "3.3.3"
|
||||||
minotaur = "2.+"
|
minotaur = "2.8.7"
|
||||||
mixinGradle = "0.7.+"
|
neoGradle = "7.0.93"
|
||||||
nullAway = "0.9.9"
|
nullAway = "0.9.9"
|
||||||
spotless = "6.21.0"
|
spotless = "6.23.3"
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
teavm = "0.10.0-SQUID.2"
|
||||||
vineflower = "1.11.0"
|
vanillaExtract = "0.1.1"
|
||||||
teavm = "0.9.0-SQUID.1"
|
versionCatalogUpdate = "0.8.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Normal dependencies
|
# Normal dependencies
|
||||||
@@ -77,9 +79,10 @@ asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
|||||||
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
|
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
|
||||||
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||||
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
||||||
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||||
|
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||||
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
neoForgeSpi = { module = "net.neoforged:neoforgespi", version.ref = "neoForgeSpi" }
|
||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||||
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
||||||
@@ -97,12 +100,13 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
|||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
|
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||||
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
|
jei-api = { module = "mezz.jei:jei-1.20.4-common-api", version.ref = "jei" }
|
||||||
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
|
jei-fabric = { module = "mezz.jei:jei-1.20.4-fabric", version.ref = "jei" }
|
||||||
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
|
jei-forge = { module = "mezz.jei:jei-1.20.4-forge", version.ref = "jei" }
|
||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
||||||
@@ -114,8 +118,6 @@ rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
|
|||||||
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
byteBuddyAgent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
|
|
||||||
byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
|
|
||||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||||
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
||||||
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
||||||
@@ -124,6 +126,12 @@ junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", vers
|
|||||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
|
|
||||||
|
# LWJGL
|
||||||
|
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
||||||
|
lwjgl-core = { module = "org.lwjgl:lwjgl" }
|
||||||
|
lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl" }
|
||||||
|
lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw" }
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||||
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||||
@@ -134,10 +142,10 @@ errorProne-core = { module = "com.google.errorprone:error_prone_core", version.r
|
|||||||
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
|
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
|
||||||
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
||||||
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
||||||
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
||||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
|
||||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
||||||
|
neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "neoGradle" }
|
||||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
||||||
@@ -148,17 +156,15 @@ teavm-metaprogramming-api = { module = "org.teavm:teavm-metaprogramming-api", ve
|
|||||||
teavm-metaprogramming-impl = { module = "org.teavm:teavm-metaprogramming-impl", version.ref = "teavm" }
|
teavm-metaprogramming-impl = { module = "org.teavm:teavm-metaprogramming-impl", version.ref = "teavm" }
|
||||||
teavm-platform = { module = "org.teavm:teavm-platform", version.ref = "teavm" }
|
teavm-platform = { module = "org.teavm:teavm-platform", version.ref = "teavm" }
|
||||||
teavm-tooling = { module = "org.teavm:teavm-tooling", version.ref = "teavm" }
|
teavm-tooling = { module = "org.teavm:teavm-tooling", version.ref = "teavm" }
|
||||||
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = "vanillaExtract" }
|
||||||
vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vineflower" }
|
yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
|
||||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||||
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
|
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
|
||||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
|
||||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||||
|
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
|
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
|
||||||
@@ -167,8 +173,7 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
|||||||
# Minecraft
|
# Minecraft
|
||||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
||||||
externalMods-forge-runtime = ["jei-forge"]
|
externalMods-forge-runtime = []
|
||||||
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
|
||||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
||||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
||||||
|
|
||||||
@@ -177,5 +182,5 @@ test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
|||||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
teavm-api = [ "teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api" ]
|
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
||||||
teavm-tooling = [ "teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl" ]
|
teavm-tooling = ["teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl"]
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
22
gradlew
vendored
22
gradlew
vendored
@@ -83,7 +83,8 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -130,10 +131,13 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
@@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -198,11 +202,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command:
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
;
|
;
|
||||||
; SPDX-License-Identifier: LicenseRef-CCPL
|
; SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
(sources
|
(sources
|
||||||
/doc/
|
/doc/
|
||||||
/projects/forge/build/docs/luaJavadoc/
|
/projects/common/build/docs/luaJavadoc/
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
||||||
/projects/core/src/test/resources/test-rom
|
/projects/core/src/test/resources/test-rom
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
(library-path
|
(library-path
|
||||||
/doc/stub/
|
/doc/stub/
|
||||||
/projects/forge/build/docs/luaJavadoc/
|
/projects/common/build/docs/luaJavadoc/
|
||||||
|
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
|
||||||
@@ -77,7 +77,6 @@
|
|||||||
(globals
|
(globals
|
||||||
:max
|
:max
|
||||||
_CC_DEFAULT_SETTINGS
|
_CC_DEFAULT_SETTINGS
|
||||||
_CC_DISABLE_LUA51_FEATURES
|
|
||||||
_HOST
|
_HOST
|
||||||
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
|
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
|
||||||
;; isn't smart enough.
|
;; isn't smart enough.
|
||||||
@@ -89,7 +88,7 @@
|
|||||||
(/doc/stub/
|
(/doc/stub/
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||||
/projects/forge/build/docs/luaJavadoc/)
|
/projects/common/build/docs/luaJavadoc/)
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
@@ -106,6 +105,10 @@
|
|||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
|
||||||
(linters -var:deprecated))
|
(linters -var:deprecated))
|
||||||
|
|
||||||
|
;; Suppress unused variable warnings in the parser.
|
||||||
|
(at /projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua
|
||||||
|
(linters -var:unused))
|
||||||
|
|
||||||
(at /projects/core/src/test/resources/test-rom
|
(at /projects/core/src/test/resources/test-rom
|
||||||
; We should still be able to test deprecated members.
|
; We should still be able to test deprecated members.
|
||||||
(linters -var:deprecated)
|
(linters -var:deprecated)
|
||||||
|
|||||||
1376
package-lock.json
generated
1376
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
|||||||
"rehype-highlight": "^7.0.0",
|
"rehype-highlight": "^7.0.0",
|
||||||
"rehype-react": "^8.0.0",
|
"rehype-react": "^8.0.0",
|
||||||
"rollup": "^4.0.0",
|
"rollup": "^4.0.0",
|
||||||
"tsx": "^3.12.10",
|
"tsx": "^4.7.0",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ mentioning:
|
|||||||
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
||||||
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
||||||
|
|
||||||
|
- `standalone`: This contains a standalone UI for computers, allowing debugging and development of CraftOS without
|
||||||
|
launching Minecraft.
|
||||||
|
|
||||||
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
||||||
rendering recipes
|
rendering recipes
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||||
|
* <p>
|
||||||
|
* This interface is largely intended to be used from multi-loader code, to allow sharing registration code between
|
||||||
|
* multiple loaders.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface RegisterTurtleUpgradeModeller {
|
||||||
|
/**
|
||||||
|
* Register a {@link TurtleUpgradeModeller}.
|
||||||
|
*
|
||||||
|
* @param serialiser The turtle upgrade serialiser.
|
||||||
|
* @param modeller The upgrade modeller.
|
||||||
|
* @param <T> The type of the turtle upgrade.
|
||||||
|
*/
|
||||||
|
<T extends ITurtleUpgrade> void register(UpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||||
|
}
|
||||||
@@ -4,13 +4,10 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.client.turtle;
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
|
||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -21,38 +18,27 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for a {@link ITurtleUpgrade}.
|
* Provides models for a {@link ITurtleUpgrade}.
|
||||||
|
* <p>
|
||||||
|
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
||||||
|
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
||||||
|
* on Forge
|
||||||
*
|
*
|
||||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||||
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
|
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
||||||
*/
|
*/
|
||||||
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||||
/**
|
/**
|
||||||
* Obtain the model to be used when rendering a turtle peripheral.
|
* Obtain the model to be used when rendering a turtle peripheral.
|
||||||
* <p>
|
* <p>
|
||||||
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
|
* When the current turtle is {@literal null}, this function should be constant for a given upgrade, side and data.
|
||||||
*
|
*
|
||||||
* @param upgrade The upgrade that you're getting the model for.
|
* @param upgrade The upgrade that you're getting the model for.
|
||||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
|
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models.
|
||||||
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
|
|
||||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
|
||||||
*/
|
|
||||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the model to be used when rendering a turtle peripheral.
|
|
||||||
* <p>
|
|
||||||
* This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available.
|
|
||||||
*
|
|
||||||
* @param upgrade The upgrade that you're getting the model for.
|
|
||||||
* @param data Upgrade data instance for current turtle side.
|
* @param data Upgrade data instance for current turtle side.
|
||||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
* @return The model that you wish to be used to render your upgrade.
|
||||||
*/
|
*/
|
||||||
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
|
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data);
|
||||||
return getModel(upgrade, (ITurtleAccess) null, side);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of models that this turtle modeller depends on.
|
* Get a list of models that this turtle modeller depends on.
|
||||||
@@ -83,19 +69,6 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
|
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
|
|
||||||
*
|
|
||||||
* @param left The model to use on the left.
|
|
||||||
* @param right The model to use on the right.
|
|
||||||
* @param <T> The type of the turtle upgrade.
|
|
||||||
* @return The constructed modeller.
|
|
||||||
*/
|
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
|
||||||
// TODO(1.21.0): Remove this.
|
|
||||||
return sided((ResourceLocation) left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
|
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
|
||||||
*
|
*
|
||||||
@@ -107,7 +80,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||||
return new TurtleUpgradeModeller<>() {
|
return new TurtleUpgradeModeller<>() {
|
||||||
@Override
|
@Override
|
||||||
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
||||||
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
|||||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -37,16 +36,8 @@ final class TurtleUpgradeModellers {
|
|||||||
|
|
||||||
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||||
@Override
|
@Override
|
||||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
||||||
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
|
var stack = upgrade.getUpgradeItem(data);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
|
||||||
return getModel(upgrade.getUpgradeItem(data), side);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TransformedModel getModel(ItemStack stack, TurtleSide side) {
|
|
||||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
||||||
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
||||||
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||||
|
|||||||
@@ -4,15 +4,12 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for pocket computers.
|
* Wrapper class for pocket computers.
|
||||||
@@ -90,13 +87,4 @@ public interface IPocketAccess {
|
|||||||
* entity} changes.
|
* entity} changes.
|
||||||
*/
|
*/
|
||||||
void invalidatePeripheral();
|
void invalidatePeripheral();
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of all upgrades for the pocket computer.
|
|
||||||
*
|
|
||||||
* @return A collection of all upgrade names.
|
|
||||||
* @deprecated This is a relic of a previous API, which no longer makes sense with newer versions of ComputerCraft.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
Map<ResourceLocation, IPeripheral> getUpgrades();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ package dan200.computercraft.api.pocket;
|
|||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -13,16 +17,46 @@ import javax.annotation.Nullable;
|
|||||||
/**
|
/**
|
||||||
* A peripheral which can be equipped to the back side of a pocket computer.
|
* A peripheral which can be equipped to the back side of a pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
* Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding
|
* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
|
||||||
* {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry.
|
* {@link UpgradeSerialiser} instance, which are then registered in a registry.
|
||||||
* <p>
|
* <p>
|
||||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||||
* the upgrade registered internally. See the documentation in {@link PocketUpgradeSerialiser} for details on this process
|
* the upgrade automatically registered. It is recommended this is done via {@linkplain PocketUpgradeDataProvider data
|
||||||
* and where files should be located.
|
* generators}.
|
||||||
*
|
*
|
||||||
* @see PocketUpgradeSerialiser For how to register a pocket computer upgrade.
|
* <h2>Example</h2>
|
||||||
|
* <pre>{@code
|
||||||
|
* // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
|
||||||
|
* static final DeferredRegister<UpgradeSerialiser<? extends IPocketUpgrade>> SERIALISERS = DeferredRegister.create(IPocketUpgrade.serialiserRegistryKey(), "my_mod");
|
||||||
|
*
|
||||||
|
* // Register a new upgrade serialiser called "my_upgrade".
|
||||||
|
* public static final RegistryObject<UpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
|
||||||
|
* SERIALISERS.register("my_upgrade", () -> UpgradeSerialiser.simple(MyUpgrade::new));
|
||||||
|
*
|
||||||
|
* // Then in your constructor
|
||||||
|
* SERIALISERS.register(bus);
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* We can then define a new upgrade using JSON by placing the following in
|
||||||
|
* {@code data/<my_mod>/computercraft/pocket_upgrades/<my_upgrade_id>.json}.
|
||||||
|
* <pre>{@code
|
||||||
|
* {
|
||||||
|
* "type": my_mod:my_upgrade",
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* {@link PocketUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
|
||||||
*/
|
*/
|
||||||
public interface IPocketUpgrade extends UpgradeBase {
|
public interface IPocketUpgrade extends UpgradeBase {
|
||||||
|
/**
|
||||||
|
* The registry key for upgrade serialisers.
|
||||||
|
*
|
||||||
|
* @return The registry key.
|
||||||
|
*/
|
||||||
|
static ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> serialiserRegistryKey() {
|
||||||
|
return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a peripheral for the pocket computer.
|
* Creates a peripheral for the pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.data.DataGenerator;
|
import net.minecraft.data.DataGenerator;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
|
|
||||||
@@ -17,10 +18,11 @@ import java.util.function.Consumer;
|
|||||||
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
||||||
* generate them.
|
* generate them.
|
||||||
*
|
*
|
||||||
* @see PocketUpgradeSerialiser
|
* @see IPocketUpgrade
|
||||||
|
* @see UpgradeSerialiser
|
||||||
*/
|
*/
|
||||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {
|
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade> {
|
||||||
public PocketUpgradeDataProvider(PackOutput output) {
|
public PocketUpgradeDataProvider(PackOutput output) {
|
||||||
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId());
|
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.pocket;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
|
||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
|
||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
|
||||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a {@link IPocketUpgrade} from disk and reads/writes it to a network packet.
|
|
||||||
* <p>
|
|
||||||
* This follows the same format as {@link dan200.computercraft.api.turtle.TurtleUpgradeSerialiser} - consult the
|
|
||||||
* documentation there for more information.
|
|
||||||
*
|
|
||||||
* @param <T> The type of pocket computer upgrade this is responsible for serialising.
|
|
||||||
* @see IPocketUpgrade
|
|
||||||
* @see PocketUpgradeDataProvider
|
|
||||||
*/
|
|
||||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
|
|
||||||
/**
|
|
||||||
* The ID for the associated registry.
|
|
||||||
*
|
|
||||||
* @return The registry key.
|
|
||||||
*/
|
|
||||||
static ResourceKey<Registry<PocketUpgradeSerialiser<?>>> registryId() {
|
|
||||||
return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
|
||||||
* but for upgrades.
|
|
||||||
* <p>
|
|
||||||
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
|
||||||
*
|
|
||||||
* @param factory Generate a new upgrade with a specific ID.
|
|
||||||
* @param <T> The type of the generated upgrade.
|
|
||||||
* @return The serialiser for this upgrade
|
|
||||||
*/
|
|
||||||
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
|
||||||
final class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T> {
|
|
||||||
private Impl(Function<ResourceLocation, T> constructor) {
|
|
||||||
super(constructor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Impl(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
|
|
||||||
*
|
|
||||||
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
|
|
||||||
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
|
||||||
* @param <T> The type of the generated upgrade.
|
|
||||||
* @return The serialiser for this upgrade.
|
|
||||||
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
|
||||||
*/
|
|
||||||
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
|
||||||
final class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T> {
|
|
||||||
private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
|
||||||
super(factory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Impl(factory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,8 +6,12 @@ package dan200.computercraft.api.turtle;
|
|||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -16,15 +20,54 @@ import javax.annotation.Nullable;
|
|||||||
* peripheral.
|
* peripheral.
|
||||||
* <p>
|
* <p>
|
||||||
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
|
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
|
||||||
* {@link TurtleUpgradeSerialiser} instance, which are then registered in a Forge registry.
|
* {@link UpgradeSerialiser} instance, which are then registered in a registry.
|
||||||
* <p>
|
* <p>
|
||||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||||
* the upgrade registered internally. See the documentation in {@link TurtleUpgradeSerialiser} for details on this process
|
* the upgrade automatically registered. It is recommended this is done via {@linkplain TurtleUpgradeDataProvider data
|
||||||
* and where files should be located.
|
* generators}.
|
||||||
*
|
*
|
||||||
* @see TurtleUpgradeSerialiser For how to register a turtle upgrade.
|
* <h2>Example</h2>
|
||||||
|
* <pre>{@code
|
||||||
|
* // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
|
||||||
|
* static final DeferredRegister<UpgradeSerialiser<? extends ITurtleUpgrade>> SERIALISERS = DeferredRegister.create(ITurtleUpgrade.serialiserRegistryKey(), "my_mod");
|
||||||
|
*
|
||||||
|
* // Register a new upgrade serialiser called "my_upgrade".
|
||||||
|
* public static final RegistryObject<UpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
|
||||||
|
* SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
|
||||||
|
*
|
||||||
|
* // Then in your constructor
|
||||||
|
* SERIALISERS.register( bus );
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* We can then define a new upgrade using JSON by placing the following in
|
||||||
|
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* {
|
||||||
|
* "type": my_mod:my_upgrade",
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
|
||||||
|
* <p>
|
||||||
|
* Finally, we need to register a model for our upgrade, see
|
||||||
|
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* // Register our model inside FMLClientSetupEvent
|
||||||
|
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
|
||||||
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
public interface ITurtleUpgrade extends UpgradeBase {
|
public interface ITurtleUpgrade extends UpgradeBase {
|
||||||
|
/**
|
||||||
|
* The registry key for upgrade serialisers.
|
||||||
|
*
|
||||||
|
* @return The registry key.
|
||||||
|
*/
|
||||||
|
static ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> serialiserRegistryKey() {
|
||||||
|
return ComputerCraftAPIService.get().turtleUpgradeRegistryId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this turtle adds a tool or a peripheral to the turtle.
|
* Return whether this turtle adds a tool or a peripheral to the turtle.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ package dan200.computercraft.api.turtle;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.ComputerCraftTags;
|
import dan200.computercraft.api.ComputerCraftTags;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||||
import dan200.computercraft.impl.PlatformHelper;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.core.registries.Registries;
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.data.DataGenerator;
|
import net.minecraft.data.DataGenerator;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -29,13 +30,13 @@ import java.util.function.Consumer;
|
|||||||
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
||||||
* generate them.
|
* generate them.
|
||||||
*
|
*
|
||||||
* @see TurtleUpgradeSerialiser
|
* @see ITurtleUpgrade
|
||||||
*/
|
*/
|
||||||
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> {
|
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade> {
|
||||||
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
|
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
|
||||||
|
|
||||||
public TurtleUpgradeDataProvider(PackOutput output) {
|
public TurtleUpgradeDataProvider(PackOutput output) {
|
||||||
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId());
|
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +58,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
*/
|
*/
|
||||||
public static class ToolBuilder {
|
public static class ToolBuilder {
|
||||||
private final ResourceLocation id;
|
private final ResourceLocation id;
|
||||||
private final TurtleUpgradeSerialiser<?> serialiser;
|
private final UpgradeSerialiser<? extends ITurtleUpgrade> serialiser;
|
||||||
private final Item toolItem;
|
private final Item toolItem;
|
||||||
private @Nullable String adjective;
|
private @Nullable String adjective;
|
||||||
private @Nullable Item craftingItem;
|
private @Nullable Item craftingItem;
|
||||||
@@ -66,7 +67,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
private boolean allowEnchantments = false;
|
private boolean allowEnchantments = false;
|
||||||
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
|
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
|
||||||
|
|
||||||
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
ToolBuilder(ResourceLocation id, UpgradeSerialiser<? extends ITurtleUpgrade> serialiser, Item toolItem) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.serialiser = serialiser;
|
this.serialiser = serialiser;
|
||||||
this.toolItem = toolItem;
|
this.toolItem = toolItem;
|
||||||
@@ -149,12 +150,12 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
*
|
*
|
||||||
* @param add The callback given to {@link #addUpgrades(Consumer)}.
|
* @param add The callback given to {@link #addUpgrades(Consumer)}.
|
||||||
*/
|
*/
|
||||||
public void add(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) {
|
public void add(Consumer<Upgrade<UpgradeSerialiser<? extends ITurtleUpgrade>>> add) {
|
||||||
add.accept(new Upgrade<>(id, serialiser, s -> {
|
add.accept(new Upgrade<>(id, serialiser, s -> {
|
||||||
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString());
|
s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, toolItem).toString());
|
||||||
if (adjective != null) s.addProperty("adjective", adjective);
|
if (adjective != null) s.addProperty("adjective", adjective);
|
||||||
if (craftingItem != null) {
|
if (craftingItem != null) {
|
||||||
s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString());
|
s.addProperty("craftItem", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, craftingItem).toString());
|
||||||
}
|
}
|
||||||
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
||||||
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.turtle;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
|
||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
|
||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
|
||||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
|
||||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
|
|
||||||
* <p>
|
|
||||||
* These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
|
|
||||||
* <p>
|
|
||||||
* If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
|
|
||||||
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
|
|
||||||
*
|
|
||||||
* <h2>Example (Forge)</h2>
|
|
||||||
* <pre>{@code
|
|
||||||
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
|
|
||||||
*
|
|
||||||
* // Register a new upgrade serialiser called "my_upgrade".
|
|
||||||
* public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
|
|
||||||
* SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
|
|
||||||
*
|
|
||||||
* // Then in your constructor
|
|
||||||
* SERIALISERS.register( bus );
|
|
||||||
* }</pre>
|
|
||||||
* <p>
|
|
||||||
* We can then define a new upgrade using JSON by placing the following in
|
|
||||||
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* {
|
|
||||||
* "type": my_mod:my_upgrade",
|
|
||||||
* }
|
|
||||||
* }</pre>
|
|
||||||
* <p>
|
|
||||||
* Finally, we need to register a model for our upgrade. This is done with
|
|
||||||
* {@link dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller}:
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* // Register our model inside FMLClientSetupEvent
|
|
||||||
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
|
|
||||||
* }</pre>
|
|
||||||
* <p>
|
|
||||||
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
|
|
||||||
*
|
|
||||||
* @param <T> The type of turtle upgrade this is responsible for serialising.
|
|
||||||
* @see ITurtleUpgrade
|
|
||||||
* @see TurtleUpgradeDataProvider
|
|
||||||
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
|
|
||||||
*/
|
|
||||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
|
|
||||||
/**
|
|
||||||
* The ID for the associated registry.
|
|
||||||
*
|
|
||||||
* @return The registry key.
|
|
||||||
*/
|
|
||||||
static ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> registryId() {
|
|
||||||
return ComputerCraftAPIService.get().turtleUpgradeRegistryId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
|
||||||
* but for upgrades.
|
|
||||||
* <p>
|
|
||||||
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
|
||||||
*
|
|
||||||
* @param factory Generate a new upgrade with a specific ID.
|
|
||||||
* @param <T> The type of the generated upgrade.
|
|
||||||
* @return The serialiser for this upgrade
|
|
||||||
*/
|
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
|
||||||
final class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T> {
|
|
||||||
private Impl(Function<ResourceLocation, T> constructor) {
|
|
||||||
super(constructor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Impl(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
|
|
||||||
*
|
|
||||||
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
|
|
||||||
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
|
||||||
* @param <T> The type of the generated upgrade.
|
|
||||||
* @return The serialiser for this upgrade.
|
|
||||||
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
|
||||||
*/
|
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
|
||||||
final class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T> {
|
|
||||||
private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
|
||||||
super(factory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Impl(factory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
|
|||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.impl.PlatformHelper;
|
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -100,7 +99,7 @@ public interface UpgradeBase {
|
|||||||
* The default check requires that any non-capability NBT is exactly the same as the
|
* The default check requires that any non-capability NBT is exactly the same as the
|
||||||
* crafting item, but this may be relaxed for your upgrade.
|
* crafting item, but this may be relaxed for your upgrade.
|
||||||
* <p>
|
* <p>
|
||||||
* This is based on {@code net.minecraftforge.common.crafting.StrictNBTIngredient}'s check.
|
* This is based on {@code net.neoforged.common.crafting.StrictNBTIngredient}'s check.
|
||||||
*
|
*
|
||||||
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
||||||
* {@link #getCraftingItem()}.
|
* {@link #getCraftingItem()}.
|
||||||
@@ -111,12 +110,12 @@ public interface UpgradeBase {
|
|||||||
|
|
||||||
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
||||||
// null one.
|
// null one.
|
||||||
var shareTag = PlatformHelper.get().getShareTag(stack);
|
var tag = stack.getTag();
|
||||||
var craftingShareTag = PlatformHelper.get().getShareTag(crafting);
|
var craftingTag = crafting.getTag();
|
||||||
if (shareTag == craftingShareTag) return true;
|
if (tag == craftingTag) return true;
|
||||||
if (shareTag == null) return Objects.requireNonNull(craftingShareTag).isEmpty();
|
if (tag == null) return Objects.requireNonNull(craftingTag).isEmpty();
|
||||||
if (craftingShareTag == null) return shareTag.isEmpty();
|
if (craftingTag == null) return tag.isEmpty();
|
||||||
return shareTag.equals(craftingShareTag);
|
return tag.equals(craftingTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,21 +6,20 @@ package dan200.computercraft.api.upgrades;
|
|||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|
||||||
import dan200.computercraft.impl.PlatformHelper;
|
import dan200.computercraft.impl.PlatformHelper;
|
||||||
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.data.CachedOutput;
|
import net.minecraft.data.CachedOutput;
|
||||||
import net.minecraft.data.DataProvider;
|
import net.minecraft.data.DataProvider;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -33,33 +32,31 @@ import java.util.function.Function;
|
|||||||
* the other subclasses.
|
* the other subclasses.
|
||||||
*
|
*
|
||||||
* @param <T> The base class of upgrades.
|
* @param <T> The base class of upgrades.
|
||||||
* @param <R> The upgrade serialiser to register for.
|
|
||||||
*/
|
*/
|
||||||
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
|
public abstract class UpgradeDataProvider<T extends UpgradeBase> implements DataProvider {
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
|
||||||
|
|
||||||
private final PackOutput output;
|
private final PackOutput output;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String folder;
|
private final String folder;
|
||||||
private final ResourceKey<Registry<R>> registry;
|
private final Registry<UpgradeSerialiser<? extends T>> registry;
|
||||||
|
|
||||||
private @Nullable List<T> upgrades;
|
private @Nullable List<T> upgrades;
|
||||||
|
|
||||||
protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<R>> registry) {
|
@ApiStatus.Internal
|
||||||
|
protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.folder = folder;
|
this.folder = folder;
|
||||||
this.registry = registry;
|
this.registry = RegistryHelper.getRegistry(registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
|
* Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) "simple" serialiser}.
|
||||||
*
|
*
|
||||||
* @param id The ID of the upgrade to create.
|
* @param id The ID of the upgrade to create.
|
||||||
* @param serialiser The simple serialiser.
|
* @param serialiser The simple serialiser.
|
||||||
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
||||||
*/
|
*/
|
||||||
public final Upgrade<R> simple(ResourceLocation id, R serialiser) {
|
public final Upgrade<UpgradeSerialiser<? extends T>> simple(ResourceLocation id, UpgradeSerialiser<? extends T> serialiser) {
|
||||||
if (!(serialiser instanceof SimpleSerialiser)) {
|
if (!(serialiser instanceof SimpleSerialiser)) {
|
||||||
throw new IllegalStateException(serialiser + " must be a simple() seriaiser.");
|
throw new IllegalStateException(serialiser + " must be a simple() seriaiser.");
|
||||||
}
|
}
|
||||||
@@ -69,20 +66,20 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
|
* Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) simple serialiser}.
|
||||||
*
|
*
|
||||||
* @param id The ID of the upgrade to create.
|
* @param id The ID of the upgrade to create.
|
||||||
* @param serialiser The simple serialiser.
|
* @param serialiser The simple serialiser.
|
||||||
* @param item The crafting upgrade for this item.
|
* @param item The crafting upgrade for this item.
|
||||||
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
||||||
*/
|
*/
|
||||||
public final Upgrade<R> simpleWithCustomItem(ResourceLocation id, R serialiser, Item item) {
|
public final Upgrade<UpgradeSerialiser<? extends T>> simpleWithCustomItem(ResourceLocation id, UpgradeSerialiser<? extends T> serialiser, Item item) {
|
||||||
if (!(serialiser instanceof SerialiserWithCraftingItem)) {
|
if (!(serialiser instanceof SerialiserWithCraftingItem)) {
|
||||||
throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser.");
|
throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Upgrade<>(id, serialiser, s ->
|
return new Upgrade<>(id, serialiser, s ->
|
||||||
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, item).toString())
|
s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).toString())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +95,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
*
|
*
|
||||||
* @param addUpgrade A callback used to register an upgrade.
|
* @param addUpgrade A callback used to register an upgrade.
|
||||||
*/
|
*/
|
||||||
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
protected abstract void addUpgrades(Consumer<Upgrade<UpgradeSerialiser<? extends T>>> addUpgrade);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<?> run(CachedOutput cache) {
|
public CompletableFuture<?> run(CachedOutput cache) {
|
||||||
@@ -111,7 +108,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
|
if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
|
||||||
|
|
||||||
var json = new JsonObject();
|
var json = new JsonObject();
|
||||||
json.addProperty("type", PlatformHelper.get().getRegistryKey(registry, upgrade.serialiser()).toString());
|
json.addProperty("type", RegistryHelper.getKeyOrThrow(registry, upgrade.serialiser()).toString());
|
||||||
upgrade.serialise().accept(json);
|
upgrade.serialise().accept(json);
|
||||||
|
|
||||||
futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json")));
|
futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json")));
|
||||||
@@ -133,9 +130,9 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final R existingSerialiser(ResourceLocation id) {
|
public final UpgradeSerialiser<? extends T> existingSerialiser(ResourceLocation id) {
|
||||||
var result = PlatformHelper.get().getRegistryObject(registry, id);
|
var result = registry.get(id);
|
||||||
if (result == null) throw new IllegalArgumentException("No such serialiser " + registry);
|
if (result == null) throw new IllegalArgumentException("No such serialiser " + id);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,21 +5,40 @@
|
|||||||
package dan200.computercraft.api.upgrades;
|
package dan200.computercraft.api.upgrades;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||||
|
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||||
|
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
|
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one
|
* A serialiser for {@link ITurtleUpgrade} or {@link IPocketUpgrade}s.
|
||||||
* of {@link TurtleUpgradeSerialiser} or {@link PocketUpgradeSerialiser}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* However, it may sometimes be useful to implement this if you have some shared logic between upgrade types.
|
* These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
|
||||||
|
* <p>
|
||||||
|
* This interface is very similar to {@link RecipeSerializer}; each serialiser should correspond to a specific upgrade
|
||||||
|
* class. Upgrades are then read from JSON files in datapacks, allowing multiple instances of the upgrade to be
|
||||||
|
* registered.
|
||||||
|
* <p>
|
||||||
|
* If your upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
|
||||||
|
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
|
||||||
|
* <p>
|
||||||
|
* Upgrades may be data generated via a {@link UpgradeDataProvider} (see {@link TurtleUpgradeDataProvider} and
|
||||||
|
* {@link PocketUpgradeDataProvider}).
|
||||||
*
|
*
|
||||||
* @param <T> The upgrade that this class can serialise and deserialise.
|
* @param <T> The upgrade that this class can serialise and deserialise.
|
||||||
* @see TurtleUpgradeSerialiser
|
* @see ITurtleUpgrade
|
||||||
* @see PocketUpgradeSerialiser
|
* @see IPocketUpgrade
|
||||||
*/
|
*/
|
||||||
public interface UpgradeSerialiser<T extends UpgradeBase> {
|
public interface UpgradeSerialiser<T extends UpgradeBase> {
|
||||||
/**
|
/**
|
||||||
@@ -49,4 +68,30 @@ public interface UpgradeSerialiser<T extends UpgradeBase> {
|
|||||||
*/
|
*/
|
||||||
void toNetwork(FriendlyByteBuf buffer, T upgrade);
|
void toNetwork(FriendlyByteBuf buffer, T upgrade);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||||
|
* but for upgrades.
|
||||||
|
* <p>
|
||||||
|
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
||||||
|
*
|
||||||
|
* @param factory Generate a new upgrade with a specific ID.
|
||||||
|
* @param <T> The type of the generated upgrade.
|
||||||
|
* @return The serialiser for this upgrade
|
||||||
|
*/
|
||||||
|
static <T extends UpgradeBase> UpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
||||||
|
return new SimpleSerialiser<>(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
|
||||||
|
*
|
||||||
|
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
|
||||||
|
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
||||||
|
* @param <T> The type of the generated upgrade.
|
||||||
|
* @return The serialiser for this upgrade.
|
||||||
|
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
||||||
|
*/
|
||||||
|
static <T extends UpgradeBase> UpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||||
|
return new SerialiserWithCraftingItem<>(factory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ import dan200.computercraft.api.media.MediaProvider;
|
|||||||
import dan200.computercraft.api.network.PacketNetwork;
|
import dan200.computercraft.api.network.PacketNetwork;
|
||||||
import dan200.computercraft.api.network.wired.WiredElement;
|
import dan200.computercraft.api.network.wired.WiredElement;
|
||||||
import dan200.computercraft.api.network.wired.WiredNode;
|
import dan200.computercraft.api.network.wired.WiredNode;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
@@ -67,9 +68,9 @@ public interface ComputerCraftAPIService {
|
|||||||
|
|
||||||
void registerRefuelHandler(TurtleRefuelHandler handler);
|
void registerRefuelHandler(TurtleRefuelHandler handler);
|
||||||
|
|
||||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId();
|
ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> turtleUpgradeRegistryId();
|
||||||
|
|
||||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId();
|
ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> pocketUpgradeRegistryId();
|
||||||
|
|
||||||
DetailRegistry<ItemStack> getItemStackDetailRegistry();
|
DetailRegistry<ItemStack> getItemStackDetailRegistry();
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,6 @@ package dan200.computercraft.impl;
|
|||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -32,39 +27,6 @@ public interface PlatformHelper {
|
|||||||
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
|
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the unique ID for a registered object.
|
|
||||||
*
|
|
||||||
* @param registry The registry to look up this object in.
|
|
||||||
* @param object The object to look up.
|
|
||||||
* @param <T> The type of object the registry stores.
|
|
||||||
* @return The registered object's ID.
|
|
||||||
* @throws IllegalArgumentException If the registry or object are not registered.
|
|
||||||
*/
|
|
||||||
<T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Look up an ID in a registry, returning the registered object.
|
|
||||||
*
|
|
||||||
* @param registry The registry to look up this object in.
|
|
||||||
* @param id The ID to look up.
|
|
||||||
* @param <T> The type of object the registry stores.
|
|
||||||
* @return The resolved registry object.
|
|
||||||
* @throws IllegalArgumentException If the registry or object are not registered.
|
|
||||||
*/
|
|
||||||
<T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the subset of an {@link ItemStack}'s {@linkplain ItemStack#getTag() tag} which is synced to the client.
|
|
||||||
*
|
|
||||||
* @param item The stack.
|
|
||||||
* @return The item's tag.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
default CompoundTag getShareTag(ItemStack item) {
|
|
||||||
return item.getTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
|
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
|
||||||
* {@link UpgradeDataProvider}.
|
* {@link UpgradeDataProvider}.
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additioanl functions for working with {@linkplain Registry registries}.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public final class RegistryHelper {
|
||||||
|
private RegistryHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a registry from a {@link ResourceKey}, throwing if it does not exist.
|
||||||
|
*
|
||||||
|
* @param id The id of the registry.
|
||||||
|
* @param <T> The contents of the registry
|
||||||
|
* @return The associated registry.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> id) {
|
||||||
|
var registry = (Registry<T>) BuiltInRegistries.REGISTRY.get(id.location());
|
||||||
|
if (registry == null) throw new IllegalArgumentException("Unknown registry " + id);
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key of a registry entry, throwing if it is not registered.
|
||||||
|
*
|
||||||
|
* @param registry The registry to look up in.
|
||||||
|
* @param object The object to look up.
|
||||||
|
* @param <T> The type of this registry.
|
||||||
|
* @return The ID of this object
|
||||||
|
* @see Registry#getResourceKey(Object)
|
||||||
|
*/
|
||||||
|
public static <T> ResourceLocation getKeyOrThrow(Registry<T> registry, T object) {
|
||||||
|
var key = registry.getResourceKey(object);
|
||||||
|
if (key.isEmpty()) throw new IllegalArgumentException(object + " was not registered in " + registry.key());
|
||||||
|
return key.get().location();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,27 +23,27 @@ import java.util.function.BiFunction;
|
|||||||
* @param <T> The upgrade that this class can serialise and deserialise.
|
* @param <T> The upgrade that this class can serialise and deserialise.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public abstract class SerialiserWithCraftingItem<T extends UpgradeBase> implements UpgradeSerialiser<T> {
|
public final class SerialiserWithCraftingItem<T extends UpgradeBase> implements UpgradeSerialiser<T> {
|
||||||
private final BiFunction<ResourceLocation, ItemStack, T> factory;
|
private final BiFunction<ResourceLocation, ItemStack, T> factory;
|
||||||
|
|
||||||
protected SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
public SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final T fromJson(ResourceLocation id, JsonObject object) {
|
public T fromJson(ResourceLocation id, JsonObject object) {
|
||||||
var item = GsonHelper.getAsItem(object, "item");
|
var item = GsonHelper.getAsItem(object, "item");
|
||||||
return factory.apply(id, new ItemStack(item));
|
return factory.apply(id, new ItemStack(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||||
var item = buffer.readItem();
|
var item = buffer.readItem();
|
||||||
return factory.apply(id, item);
|
return factory.apply(id, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||||
buffer.writeItem(upgrade.getCraftingItem());
|
buffer.writeItem(upgrade.getCraftingItem());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import java.util.function.Function;
|
|||||||
* @param <T> The upgrade that this class can serialise and deserialise.
|
* @param <T> The upgrade that this class can serialise and deserialise.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public abstract class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSerialiser<T> {
|
public final class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSerialiser<T> {
|
||||||
private final Function<ResourceLocation, T> constructor;
|
private final Function<ResourceLocation, T> constructor;
|
||||||
|
|
||||||
public SimpleSerialiser(Function<ResourceLocation, T> constructor) {
|
public SimpleSerialiser(Function<ResourceLocation, T> constructor) {
|
||||||
@@ -29,16 +29,16 @@ public abstract class SimpleSerialiser<T extends UpgradeBase> implements Upgrade
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final T fromJson(ResourceLocation id, JsonObject object) {
|
public T fromJson(ResourceLocation id, JsonObject object) {
|
||||||
return constructor.apply(id);
|
return constructor.apply(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||||
return constructor.apply(id);
|
return constructor.apply(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,13 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
import cc.tweaked.gradle.annotationProcessorEverywhere
|
import cc.tweaked.gradle.*
|
||||||
import cc.tweaked.gradle.clientClasses
|
|
||||||
import cc.tweaked.gradle.commonClasses
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.publishing")
|
|
||||||
id("cc-tweaked.vanilla")
|
id("cc-tweaked.vanilla")
|
||||||
id("cc-tweaked.gametest")
|
id("cc-tweaked.gametest")
|
||||||
|
id("cc-tweaked.illuaminate")
|
||||||
|
id("cc-tweaked.publishing")
|
||||||
}
|
}
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
@@ -19,6 +18,18 @@ minecraft {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
register("cctJavadoc")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven("https://maven.minecraftforge.net/") {
|
||||||
|
content {
|
||||||
|
includeModule("org.spongepowered", "mixin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
@@ -28,7 +39,6 @@ dependencies {
|
|||||||
compileOnly(libs.bundles.externalMods.common)
|
compileOnly(libs.bundles.externalMods.common)
|
||||||
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||||
|
|
||||||
compileOnly(libs.mixin)
|
|
||||||
annotationProcessorEverywhere(libs.autoService)
|
annotationProcessorEverywhere(libs.autoService)
|
||||||
testFixturesAnnotationProcessor(libs.autoService)
|
testFixturesAnnotationProcessor(libs.autoService)
|
||||||
|
|
||||||
@@ -36,9 +46,59 @@ dependencies {
|
|||||||
testImplementation(libs.bundles.test)
|
testImplementation(libs.bundles.test)
|
||||||
testRuntimeOnly(libs.bundles.testRuntime)
|
testRuntimeOnly(libs.bundles.testRuntime)
|
||||||
|
|
||||||
|
testModCompileOnly(libs.mixin)
|
||||||
testModImplementation(testFixtures(project(":core")))
|
testModImplementation(testFixtures(project(":core")))
|
||||||
testModImplementation(testFixtures(project(":common")))
|
testModImplementation(testFixtures(project(":common")))
|
||||||
testModImplementation(libs.bundles.kotlin)
|
testModImplementation(libs.bundles.kotlin)
|
||||||
|
|
||||||
testFixturesImplementation(testFixtures(project(":core")))
|
testFixturesImplementation(testFixtures(project(":core")))
|
||||||
|
|
||||||
|
"cctJavadoc"(libs.cctJavadoc)
|
||||||
|
}
|
||||||
|
|
||||||
|
illuaminate {
|
||||||
|
version.set(libs.versions.illuaminate)
|
||||||
|
}
|
||||||
|
|
||||||
|
val luaJavadoc by tasks.registering(Javadoc::class) {
|
||||||
|
description = "Generates documentation for Java-side Lua functions."
|
||||||
|
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||||
|
|
||||||
|
val sourceSets = listOf(sourceSets.main.get(), project(":core").sourceSets.main.get())
|
||||||
|
for (sourceSet in sourceSets) {
|
||||||
|
source(sourceSet.java)
|
||||||
|
classpath += sourceSet.compileClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
destinationDir = layout.buildDirectory.dir("docs/luaJavadoc").get().asFile
|
||||||
|
|
||||||
|
val options = options as StandardJavadocDocletOptions
|
||||||
|
options.docletpath = configurations["cctJavadoc"].files.toList()
|
||||||
|
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
|
||||||
|
options.addStringOption("project-root", rootProject.file(".").absolutePath)
|
||||||
|
options.noTimestamp(false)
|
||||||
|
|
||||||
|
javadocTool.set(
|
||||||
|
javaToolchains.javadocToolFor {
|
||||||
|
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||||
|
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||||
|
description = "Lint Lua (and Lua docs) with illuaminate"
|
||||||
|
|
||||||
|
// Config files
|
||||||
|
inputs.file(rootProject.file("illuaminate.sexp")).withPropertyName("illuaminate.sexp")
|
||||||
|
// Sources
|
||||||
|
inputs.files(rootProject.fileTree("doc")).withPropertyName("docs")
|
||||||
|
inputs.files(project(":core").fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||||
|
inputs.files(luaJavadoc)
|
||||||
|
|
||||||
|
args = listOf("lint")
|
||||||
|
workingDir = rootProject.projectDir
|
||||||
|
|
||||||
|
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||||
|
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import dan200.computercraft.client.render.monitor.MonitorRenderState;
|
|||||||
import dan200.computercraft.client.sound.SpeakerManager;
|
import dan200.computercraft.client.sound.SpeakerManager;
|
||||||
import dan200.computercraft.shared.CommonHooks;
|
import dan200.computercraft.shared.CommonHooks;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
||||||
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
||||||
@@ -28,7 +26,6 @@ import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
|||||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||||
import dan200.computercraft.shared.util.WorldUtil;
|
import dan200.computercraft.shared.util.WorldUtil;
|
||||||
import net.minecraft.Util;
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
@@ -43,7 +40,6 @@ import net.minecraft.world.phys.BlockHitResult;
|
|||||||
import net.minecraft.world.phys.HitResult;
|
import net.minecraft.world.phys.HitResult;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,10 +67,6 @@ public final class ClientHooks {
|
|||||||
ClientPocketComputers.reset();
|
ClientPocketComputers.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean onChatMessage(String message) {
|
|
||||||
return handleOpenComputerCommand(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
|
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
|
||||||
return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit)
|
return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit)
|
||||||
|| MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit);
|
|| MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit);
|
||||||
@@ -109,34 +101,6 @@ public final class ClientHooks {
|
|||||||
SpeakerManager.onPlayStreaming(engine, channel, stream);
|
SpeakerManager.onPlayStreaming(engine, channel, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the {@link CommandComputerCraft#OPEN_COMPUTER} "clientside command". This isn't a true command, as we
|
|
||||||
* don't want it to actually be visible to the user.
|
|
||||||
*
|
|
||||||
* @param message The current chat message.
|
|
||||||
* @return Whether to cancel sending this message.
|
|
||||||
*/
|
|
||||||
private static boolean handleOpenComputerCommand(String message) {
|
|
||||||
if (!message.startsWith(CommandComputerCraft.OPEN_COMPUTER)) return false;
|
|
||||||
|
|
||||||
var server = Minecraft.getInstance().getSingleplayerServer();
|
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
var idStr = message.substring(CommandComputerCraft.OPEN_COMPUTER.length()).trim();
|
|
||||||
int id;
|
|
||||||
try {
|
|
||||||
id = Integer.parseInt(idStr);
|
|
||||||
} catch (NumberFormatException ignore) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id);
|
|
||||||
if (!file.isDirectory()) return false;
|
|
||||||
|
|
||||||
Util.getPlatform().openFile(file);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add additional information about the currently targeted block to the debug screen.
|
* Add additional information about the currently targeted block to the debug screen.
|
||||||
*
|
*
|
||||||
@@ -144,7 +108,7 @@ public final class ClientHooks {
|
|||||||
*/
|
*/
|
||||||
public static void addBlockDebugInfo(Consumer<String> addText) {
|
public static void addBlockDebugInfo(Consumer<String> addText) {
|
||||||
var minecraft = Minecraft.getInstance();
|
var minecraft = Minecraft.getInstance();
|
||||||
if (!minecraft.options.renderDebug || minecraft.level == null) return;
|
if (!minecraft.getDebugOverlay().showDebugScreen() || minecraft.level == null) return;
|
||||||
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
|
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
|
||||||
|
|
||||||
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
|
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
|
||||||
@@ -174,7 +138,7 @@ public final class ClientHooks {
|
|||||||
* @param addText A callback which adds a single line of text.
|
* @param addText A callback which adds a single line of text.
|
||||||
*/
|
*/
|
||||||
public static void addGameDebugInfo(Consumer<String> addText) {
|
public static void addGameDebugInfo(Consumer<String> addText) {
|
||||||
if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().options.renderDebug) {
|
if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().getDebugOverlay().showDebugScreen()) {
|
||||||
addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer());
|
addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,12 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client;
|
package dan200.computercraft.client;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||||
import dan200.computercraft.client.gui.*;
|
import dan200.computercraft.client.gui.*;
|
||||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||||
@@ -16,29 +20,38 @@ import dan200.computercraft.client.turtle.TurtleModemModeller;
|
|||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
import dan200.computercraft.shared.common.IColouredItem;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
||||||
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.color.item.ItemColor;
|
import net.minecraft.client.color.item.ItemColor;
|
||||||
import net.minecraft.client.gui.screens.MenuScreens;
|
import net.minecraft.client.gui.screens.MenuScreens;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.ShaderInstance;
|
import net.minecraft.client.renderer.ShaderInstance;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
||||||
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
||||||
import net.minecraft.client.renderer.item.ItemProperties;
|
import net.minecraft.client.renderer.item.ItemProperties;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.ItemLike;
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -60,18 +73,6 @@ public final class ClientRegistry {
|
|||||||
* Register any client-side objects which don't have to be done on the main thread.
|
* Register any client-side objects which don't have to be done on the main thread.
|
||||||
*/
|
*/
|
||||||
public static void register() {
|
public static void register() {
|
||||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
|
||||||
));
|
|
||||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
|
||||||
));
|
|
||||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
|
||||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
|
||||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
|
||||||
|
|
||||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), MonitorBlockEntityRenderer::new);
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), MonitorBlockEntityRenderer::new);
|
||||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||||
@@ -82,17 +83,6 @@ public final class ClientRegistry {
|
|||||||
* Register any client-side objects which must be done on the main thread.
|
* Register any client-side objects which must be done on the main thread.
|
||||||
*/
|
*/
|
||||||
public static void registerMainThread() {
|
public static void registerMainThread() {
|
||||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
|
||||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
|
||||||
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
|
||||||
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
|
|
||||||
|
|
||||||
MenuScreens.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
|
|
||||||
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
|
||||||
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
|
||||||
|
|
||||||
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
|
||||||
|
|
||||||
registerItemProperty("state",
|
registerItemProperty("state",
|
||||||
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
||||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
@@ -103,6 +93,37 @@ public final class ClientRegistry {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void registerMenuScreens(RegisterMenuScreen register) {
|
||||||
|
register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
||||||
|
register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
||||||
|
register.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
||||||
|
register.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
|
||||||
|
|
||||||
|
register.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
|
||||||
|
register.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
||||||
|
register.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
||||||
|
|
||||||
|
register.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface RegisterMenuScreen {
|
||||||
|
<M extends AbstractContainerMenu, U extends Screen & MenuAccess<M>> void register(MenuType<? extends M> type, MenuScreens.ScreenConstructor<M, U> factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
|
||||||
|
register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
||||||
|
));
|
||||||
|
register.register(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
||||||
|
));
|
||||||
|
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
||||||
|
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
||||||
|
register.register(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
||||||
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||||
@@ -179,4 +200,45 @@ public final class ClientRegistry {
|
|||||||
return function.unclampedCall(stack, level, entity, layer);
|
return function.unclampedCall(stack, level, entity, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register client-side commands.
|
||||||
|
*
|
||||||
|
* @param dispatcher The dispatcher to register the commands to.
|
||||||
|
* @param sendError A function to send an error message.
|
||||||
|
* @param <T> The type of the client-side command context.
|
||||||
|
*/
|
||||||
|
public static <T> void registerClientCommands(CommandDispatcher<T> dispatcher, BiConsumer<T, Component> sendError) {
|
||||||
|
dispatcher.register(LiteralArgumentBuilder.<T>literal(CommandComputerCraft.CLIENT_OPEN_FOLDER)
|
||||||
|
.requires(x -> Minecraft.getInstance().getSingleplayerServer() != null)
|
||||||
|
.then(RequiredArgumentBuilder.<T, Integer>argument("computer_id", IntegerArgumentType.integer(0))
|
||||||
|
.executes(c -> handleOpenComputerCommand(c.getSource(), sendError, c.getArgument("computer_id", Integer.class)))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the {@link CommandComputerCraft#CLIENT_OPEN_FOLDER} command.
|
||||||
|
*
|
||||||
|
* @param context The command context.
|
||||||
|
* @param sendError A function to send an error message.
|
||||||
|
* @param id The computer's id.
|
||||||
|
* @param <T> The type of the client-side command context.
|
||||||
|
* @return {@code 1} if a folder was opened, {@code 0} otherwise.
|
||||||
|
*/
|
||||||
|
private static <T> int handleOpenComputerCommand(T context, BiConsumer<T, Component> sendError, int id) {
|
||||||
|
var server = Minecraft.getInstance().getSingleplayerServer();
|
||||||
|
if (server == null) {
|
||||||
|
sendError.accept(context, Component.literal("Not on a single-player server"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id);
|
||||||
|
if (!file.isDirectory()) {
|
||||||
|
sendError.accept(context, Component.literal("Computer's folder does not exist"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.getPlatform().openFile(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package dan200.computercraft.client.gui;
|
|||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
import dan200.computercraft.client.network.ClientNetworking;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
@@ -19,6 +19,7 @@ import dan200.computercraft.shared.network.server.UploadFileMessage;
|
|||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
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.gui.screens.inventory.AbstractContainerScreen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
@@ -33,7 +34,6 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -125,7 +125,6 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(graphics);
|
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
renderTooltip(graphics, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
@@ -145,6 +144,11 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
|| super.mouseDragged(x, y, button, deltaX, deltaY);
|
|| super.mouseDragged(x, y, button, deltaX, deltaY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(@Nullable GuiEventListener listener) {
|
||||||
|
// Don't clear and re-focus if we're already focused.
|
||||||
|
if (listener != getFocused()) super.setFocused(listener);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||||
@@ -202,7 +206,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientPlatformHelper.get()::sendToServer);
|
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void uploadResult(UploadResult result, @Nullable Component message) {
|
public void uploadResult(UploadResult result, @Nullable Component message) {
|
||||||
@@ -219,7 +223,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
|
|
||||||
private void alert(Component title, Component message) {
|
private void alert(Component title, Component message) {
|
||||||
OptionScreen.show(minecraft, title, message,
|
OptionScreen.show(minecraft, title, message,
|
||||||
Collections.singletonList(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||||
() -> minecraft.setScreen(this)
|
() -> minecraft.setScreen(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
import dan200.computercraft.client.network.ClientNetworking;
|
||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||||
import dan200.computercraft.shared.network.server.ComputerActionServerMessage;
|
import dan200.computercraft.shared.network.server.ComputerActionServerMessage;
|
||||||
@@ -29,51 +29,51 @@ public final class ClientInputHandler implements InputHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void turnOn() {
|
public void turnOn() {
|
||||||
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
|
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN));
|
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reboot() {
|
public void reboot() {
|
||||||
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
|
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEvent(String event, @Nullable Object[] arguments) {
|
public void queueEvent(String event, @Nullable Object[] arguments) {
|
||||||
ClientPlatformHelper.get().sendToServer(new QueueEventServerMessage(menu, event, arguments));
|
ClientNetworking.sendToServer(new QueueEventServerMessage(menu, event, arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void keyDown(int key, boolean repeat) {
|
public void keyDown(int key, boolean repeat) {
|
||||||
ClientPlatformHelper.get().sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
|
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void keyUp(int key) {
|
public void keyUp(int key) {
|
||||||
ClientPlatformHelper.get().sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
|
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClick(int button, int x, int y) {
|
public void mouseClick(int button, int x, int y) {
|
||||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
|
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseUp(int button, int x, int y) {
|
public void mouseUp(int button, int x, int y) {
|
||||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
|
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDrag(int button, int x, int y) {
|
public void mouseDrag(int button, int x, int y) {
|
||||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
|
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseScroll(int direction, int x, int y) {
|
public void mouseScroll(int direction, int x, int y) {
|
||||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
|
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(graphics);
|
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
renderTooltip(graphics, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import net.minecraft.client.gui.GuiGraphics;
|
|||||||
import net.minecraft.client.gui.components.toasts.Toast;
|
import net.minecraft.client.gui.components.toasts.Toast;
|
||||||
import net.minecraft.client.gui.components.toasts.ToastComponent;
|
import net.minecraft.client.gui.components.toasts.ToastComponent;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.util.FormattedCharSequence;
|
import net.minecraft.util.FormattedCharSequence;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ import java.util.List;
|
|||||||
* A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}.
|
* A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}.
|
||||||
*/
|
*/
|
||||||
public class ItemToast implements Toast {
|
public class ItemToast implements Toast {
|
||||||
|
private static final ResourceLocation TEXTURE = new ResourceLocation("toast/recipe");
|
||||||
public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object();
|
public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object();
|
||||||
|
|
||||||
private static final long DISPLAY_TIME = 7000L;
|
private static final long DISPLAY_TIME = 7000L;
|
||||||
@@ -79,7 +81,7 @@ public class ItemToast implements Toast {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (width == 160 && message.size() <= 1) {
|
if (width == 160 && message.size() <= 1) {
|
||||||
graphics.blit(TEXTURE, 0, 0, 0, 64, width, height());
|
graphics.blitSprite(TEXTURE, 0, 0, width, height());
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
var height = height();
|
var height = height();
|
||||||
@@ -109,14 +111,14 @@ public class ItemToast implements Toast {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
|
private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
|
||||||
var leftOffset = 5;
|
var leftOffset = u == 0 ? 20 : 5;
|
||||||
var rightOffset = Math.min(60, x - leftOffset);
|
var rightOffset = Math.min(60, x - leftOffset);
|
||||||
|
|
||||||
graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height);
|
graphics.blitSprite(TEXTURE, 160, 32, 0, u, 0, y, leftOffset, height);
|
||||||
for (var k = leftOffset; k < x - rightOffset; k += 64) {
|
for (var k = leftOffset; k < x - rightOffset; k += 64) {
|
||||||
graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
|
graphics.blitSprite(TEXTURE, 160, 32, 32, u, k, y, Math.min(64, x - k - rightOffset), height);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
|
graphics.blitSprite(TEXTURE, 160, 32, 160 - rightOffset, u, x - rightOffset, y, rightOffset, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
|
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
|
||||||
minecraft.player.getInventory().swapPaint(pDelta);
|
minecraft.player.getInventory().swapPaint(scrollX);
|
||||||
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
|
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -86,8 +86,6 @@ public final class OptionScreen extends Screen {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(graphics);
|
|
||||||
|
|
||||||
// Render the actual texture.
|
// Render the actual texture.
|
||||||
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
|
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
|
||||||
graphics.blit(BACKGROUND,
|
graphics.blit(BACKGROUND,
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(graphics);
|
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
renderTooltip(graphics, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,15 +64,15 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseScrolled(double x, double y, double delta) {
|
public boolean mouseScrolled(double x, double y, double deltaX, double deltaY) {
|
||||||
if (super.mouseScrolled(x, y, delta)) return true;
|
if (super.mouseScrolled(x, y, deltaX, deltaY)) return true;
|
||||||
if (delta < 0) {
|
if (deltaX < 0) {
|
||||||
// Scroll up goes to the next page
|
// Scroll up goes to the next page
|
||||||
if (page < pages - 1) page++;
|
if (page < pages - 1) page++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delta > 0) {
|
if (deltaX > 0) {
|
||||||
// Scroll down goes to the previous page
|
// Scroll down goes to the previous page
|
||||||
if (page > 0) page--;
|
if (page > 0) page--;
|
||||||
return true;
|
return true;
|
||||||
@@ -91,14 +91,12 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
// We must take the background further back in order to not overlap with our printed pages.
|
// We must take the background further back in order to not overlap with our printed pages.
|
||||||
graphics.pose().pushPose();
|
graphics.pose().pushPose();
|
||||||
graphics.pose().translate(0, 0, -1);
|
graphics.pose().translate(0, 0, -1);
|
||||||
renderBackground(graphics);
|
super.renderBackground(graphics, mouseX, mouseY, partialTicks);
|
||||||
graphics.pose().popPose();
|
graphics.pose().popPose();
|
||||||
|
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ public class DynamicImageButton extends Button {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
var message = this.message.get();
|
||||||
|
setMessage(message.message());
|
||||||
|
setTooltip(message.tooltip());
|
||||||
|
|
||||||
var texture = this.texture.get(isHoveredOrFocused());
|
var texture = this.texture.get(isHoveredOrFocused());
|
||||||
|
|
||||||
RenderSystem.disableDepthTest();
|
RenderSystem.disableDepthTest();
|
||||||
@@ -50,14 +54,6 @@ public class DynamicImageButton extends Button {
|
|||||||
RenderSystem.enableDepthTest();
|
RenderSystem.enableDepthTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
|
||||||
var message = this.message.get();
|
|
||||||
setMessage(message.message());
|
|
||||||
setTooltip(message.tooltip());
|
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public record HintedMessage(Component message, Tooltip tooltip) {
|
public record HintedMessage(Component message, Tooltip tooltip) {
|
||||||
public HintedMessage(Component message, @Nullable Component hint) {
|
public HintedMessage(Component message, @Nullable Component hint) {
|
||||||
this(
|
this(
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import com.mojang.blaze3d.vertex.Tesselator;
|
|||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
import dan200.computercraft.core.util.StringUtil;
|
||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
@@ -112,26 +112,8 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void paste() {
|
private void paste() {
|
||||||
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
var clipboard = StringUtil.normaliseClipboardString(Minecraft.getInstance().keyboardHandler.getClipboard());
|
||||||
|
if (!clipboard.isEmpty()) computer.queueEvent("paste", new Object[]{ clipboard });
|
||||||
// Clip to the first occurrence of \r or \n
|
|
||||||
var newLineIndex1 = clipboard.indexOf('\r');
|
|
||||||
var newLineIndex2 = clipboard.indexOf('\n');
|
|
||||||
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
|
|
||||||
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
|
|
||||||
} else if (newLineIndex1 >= 0) {
|
|
||||||
clipboard = clipboard.substring(0, newLineIndex1);
|
|
||||||
} else if (newLineIndex2 >= 0) {
|
|
||||||
clipboard = clipboard.substring(0, newLineIndex2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter the string
|
|
||||||
clipboard = SharedConstants.filterText(clipboard);
|
|
||||||
if (!clipboard.isEmpty()) {
|
|
||||||
// Clip to 512 characters and queue the event
|
|
||||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
|
||||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -213,7 +195,7 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
|
public boolean mouseScrolled(double mouseX, double mouseY, double delta, double deltaY) {
|
||||||
if (!inTermRegion(mouseX, mouseY)) return false;
|
if (!inTermRegion(mouseX, mouseY)) return false;
|
||||||
if (!hasMouseSupport() || delta == 0) return false;
|
if (!hasMouseSupport() || delta == 0) return false;
|
||||||
|
|
||||||
@@ -264,7 +246,7 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
keysDown.clear();
|
keysDown.clear();
|
||||||
|
|
||||||
// When blurring, we should make the last mouse button go up
|
// When blurring, we should make the last mouse button go up
|
||||||
if (lastMouseButton > 0) {
|
if (lastMouseButton >= 0) {
|
||||||
computer.mouseUp(lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1);
|
computer.mouseUp(lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1);
|
||||||
lastMouseButton = -1;
|
lastMouseButton = -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.network;
|
||||||
|
|
||||||
|
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||||
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
|
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods for sending packets from clients to the server.
|
||||||
|
*/
|
||||||
|
public final class ClientNetworking {
|
||||||
|
private ClientNetworking() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a network message to the server.
|
||||||
|
*
|
||||||
|
* @param message The message to send.
|
||||||
|
*/
|
||||||
|
public static void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
||||||
|
var connection = Minecraft.getInstance().getConnection();
|
||||||
|
if (connection != null) connection.send(ClientPlatformHelper.get().createPacket(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.platform;
|
package dan200.computercraft.client.platform;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
import dan200.computercraft.client.ClientTableFormatter;
|
import dan200.computercraft.client.ClientTableFormatter;
|
||||||
import dan200.computercraft.client.gui.AbstractComputerScreen;
|
import dan200.computercraft.client.gui.AbstractComputerScreen;
|
||||||
import dan200.computercraft.client.gui.OptionScreen;
|
import dan200.computercraft.client.gui.OptionScreen;
|
||||||
@@ -17,30 +18,30 @@ import dan200.computercraft.shared.computer.upload.UploadResult;
|
|||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base implementation of {@link ClientNetworkContext}.
|
* The client-side implementation of {@link ClientNetworkContext}.
|
||||||
* <p>
|
|
||||||
* This should be extended by mod loader specific modules with the remaining abstract methods.
|
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractClientNetworkContext implements ClientNetworkContext {
|
@AutoService(ClientNetworkContext.class)
|
||||||
|
public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||||
@Override
|
@Override
|
||||||
public final void handleChatTable(TableBuilder table) {
|
public void handleChatTable(TableBuilder table) {
|
||||||
ClientTableFormatter.INSTANCE.display(table);
|
ClientTableFormatter.INSTANCE.display(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleComputerTerminal(int containerId, TerminalState terminal) {
|
public void handleComputerTerminal(int containerId, TerminalState terminal) {
|
||||||
Player player = Minecraft.getInstance().player;
|
Player player = Minecraft.getInstance().player;
|
||||||
if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) {
|
if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) {
|
||||||
menu.updateTerminal(terminal);
|
menu.updateTerminal(terminal);
|
||||||
@@ -48,7 +49,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleMonitorData(BlockPos pos, TerminalState terminal) {
|
public void handleMonitorData(BlockPos pos, TerminalState terminal) {
|
||||||
var player = Minecraft.getInstance().player;
|
var player = Minecraft.getInstance().player;
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
|
||||||
@@ -59,44 +60,46 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
public void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name) {
|
||||||
|
var mc = Minecraft.getInstance();
|
||||||
|
ClientPlatformHelper.get().playStreamingMusic(pos, sound);
|
||||||
|
if (name != null) mc.gui.setNowPlaying(Component.literal(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||||
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
||||||
computer.setState(state, lightState);
|
computer.setState(state, lightState);
|
||||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handlePocketComputerDeleted(int instanceId) {
|
public void handlePocketComputerDeleted(int instanceId) {
|
||||||
ClientPocketComputers.remove(instanceId);
|
ClientPocketComputers.remove(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume) {
|
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
|
||||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume);
|
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleSpeakerAudioPush(UUID source, ByteBuf buffer) {
|
public void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
|
||||||
SpeakerManager.getSound(source).pushAudio(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
|
|
||||||
SpeakerManager.moveSound(source, reifyPosition(position));
|
SpeakerManager.moveSound(source, reifyPosition(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, ResourceLocation sound, float volume, float pitch) {
|
public void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, ResourceLocation sound, float volume, float pitch) {
|
||||||
SpeakerManager.getSound(source).playSound(reifyPosition(position), sound, volume, pitch);
|
SpeakerManager.getSound(source).playSound(reifyPosition(position), sound, volume, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleSpeakerStop(UUID source) {
|
public void handleSpeakerStop(UUID source) {
|
||||||
SpeakerManager.stopSound(source);
|
SpeakerManager.stopSound(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) {
|
public void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) {
|
||||||
var minecraft = Minecraft.getInstance();
|
var minecraft = Minecraft.getInstance();
|
||||||
|
|
||||||
var screen = OptionScreen.unwrap(minecraft.screen);
|
var screen = OptionScreen.unwrap(minecraft.screen);
|
||||||
@@ -9,6 +9,10 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
|||||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.common.ServerCommonPacketListener;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -18,11 +22,12 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a network message to the server.
|
* Convert a serverbound {@link NetworkMessage} to a Minecraft {@link Packet}.
|
||||||
*
|
*
|
||||||
* @param message The message to send.
|
* @param message The messsge to convert.
|
||||||
|
* @return The converted message.
|
||||||
*/
|
*/
|
||||||
void sendToServer(NetworkMessage<ServerNetworkContext> message);
|
Packet<ServerCommonPacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a {@link BakedModel}, using any loader-specific hooks.
|
* Render a {@link BakedModel}, using any loader-specific hooks.
|
||||||
@@ -35,4 +40,13 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
|||||||
* @param tints Block colour tints to apply to the model.
|
* @param tints Block colour tints to apply to the model.
|
||||||
*/
|
*/
|
||||||
void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints);
|
void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play a record at a particular position.
|
||||||
|
*
|
||||||
|
* @param pos The position to play this record.
|
||||||
|
* @param sound The record to play, or {@code null} to stop it.
|
||||||
|
* @see net.minecraft.client.renderer.LevelRenderer#playStreamingMusic(SoundEvent, BlockPos)
|
||||||
|
*/
|
||||||
|
void playStreamingMusic(BlockPos pos, @Nullable SoundEvent sound);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,10 +125,10 @@ public class SpriteRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static float u(TextureAtlasSprite sprite, int x, int width) {
|
public static float u(TextureAtlasSprite sprite, int x, int width) {
|
||||||
return sprite.getU((double) x / width * 16);
|
return sprite.getU((float) x / width);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float v(TextureAtlasSprite sprite, int y, int height) {
|
public static float v(TextureAtlasSprite sprite, int y, int height) {
|
||||||
return sprite.getV((double) y / height * 16);
|
return sprite.getV((float) y / height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
||||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||||
import dan200.computercraft.shared.util.Holiday;
|
import dan200.computercraft.shared.util.Holiday;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
@@ -21,7 +20,6 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
|||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.HitResult;
|
import net.minecraft.world.phys.HitResult;
|
||||||
@@ -29,8 +27,6 @@ import net.minecraft.world.phys.HitResult;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
|
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
|
||||||
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
|
|
||||||
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced", "inventory");
|
|
||||||
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
||||||
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
|
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
|
||||||
|
|
||||||
@@ -42,13 +38,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
|||||||
font = context.getFont();
|
font = context.getFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceLocation getTurtleModel(ComputerFamily family, boolean coloured) {
|
|
||||||
return switch (family) {
|
|
||||||
default -> coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
|
|
||||||
case ADVANCED -> coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @Nullable ResourceLocation getTurtleOverlayModel(@Nullable ResourceLocation overlay, boolean christmas) {
|
public static @Nullable ResourceLocation getTurtleOverlayModel(@Nullable ResourceLocation overlay, boolean christmas) {
|
||||||
if (overlay != null) return overlay;
|
if (overlay != null) return overlay;
|
||||||
if (christmas) return ELF_OVERLAY_MODEL;
|
if (christmas) return ELF_OVERLAY_MODEL;
|
||||||
@@ -78,7 +67,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
|||||||
var matrix = transform.last().pose();
|
var matrix = transform.last().pose();
|
||||||
var opacity = (int) (mc.options.getBackgroundOpacity(0.25f) * 255) << 24;
|
var opacity = (int) (mc.options.getBackgroundOpacity(0.25f) * 255) << 24;
|
||||||
var width = -font.width(label) / 2.0f;
|
var width = -font.width(label) / 2.0f;
|
||||||
// TODO: Check this looks okay
|
|
||||||
font.drawInBatch(label, width, (float) 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
|
font.drawInBatch(label, width, (float) 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
|
||||||
font.drawInBatch(label, width, (float) 0, 0xffffffff, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
|
font.drawInBatch(label, width, (float) 0, 0xffffffff, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
|
||||||
|
|
||||||
@@ -96,10 +84,18 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
|||||||
|
|
||||||
// Render the turtle
|
// Render the turtle
|
||||||
var colour = turtle.getColour();
|
var colour = turtle.getColour();
|
||||||
var family = turtle.getFamily();
|
|
||||||
var overlay = turtle.getOverlay();
|
var overlay = turtle.getOverlay();
|
||||||
|
|
||||||
renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
|
if (colour == -1) {
|
||||||
|
// Render the turtle using its item model.
|
||||||
|
var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper();
|
||||||
|
var model = modelManager.getItemModel(turtle.getBlockState().getBlock().asItem());
|
||||||
|
if (model == null) model = modelManager.getModelManager().getMissingModel();
|
||||||
|
renderModel(transform, buffers, lightmapCoord, overlayLight, model, null);
|
||||||
|
} else {
|
||||||
|
// Otherwise render it using the colour item.
|
||||||
|
renderModel(transform, buffers, lightmapCoord, overlayLight, COLOUR_TURTLE_MODEL, new int[]{ colour });
|
||||||
|
}
|
||||||
|
|
||||||
// Render the overlay
|
// Render the overlay
|
||||||
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.mojang.blaze3d.platform.MemoryTracker;
|
|||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.blaze3d.vertex.*;
|
import com.mojang.blaze3d.vertex.*;
|
||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
|
import dan200.computercraft.annotations.ForgeOverride;
|
||||||
import dan200.computercraft.client.FrameInfo;
|
import dan200.computercraft.client.FrameInfo;
|
||||||
import dan200.computercraft.client.integration.ShaderMod;
|
import dan200.computercraft.client.integration.ShaderMod;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
@@ -25,6 +26,7 @@ import dan200.computercraft.shared.util.DirectionUtil;
|
|||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
import org.joml.Matrix3f;
|
import org.joml.Matrix3f;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
@@ -255,6 +257,11 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
|||||||
return Config.monitorDistance;
|
return Config.monitorDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ForgeOverride
|
||||||
|
public AABB getRenderBoundingBox(MonitorBlockEntity monitor) {
|
||||||
|
return monitor.getRenderBoundingBox();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if any monitors were rendered this frame.
|
* Determine if any monitors were rendered this frame.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import dan200.computercraft.core.terminal.TextBuffer;
|
|||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import net.minecraft.client.renderer.ShaderInstance;
|
import net.minecraft.client.renderer.ShaderInstance;
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.lwjgl.opengl.GL13;
|
import org.lwjgl.opengl.GL13;
|
||||||
import org.lwjgl.opengl.GL31;
|
import org.lwjgl.opengl.GL31;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -36,12 +36,12 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.get
|
|||||||
* @see RenderTypes#getMonitorTextureBufferShader()
|
* @see RenderTypes#getMonitorTextureBufferShader()
|
||||||
*/
|
*/
|
||||||
public class MonitorTextureBufferShader extends ShaderInstance {
|
public class MonitorTextureBufferShader extends ShaderInstance {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MonitorTextureBufferShader.class);
|
||||||
|
|
||||||
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
|
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
|
||||||
|
|
||||||
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
|
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
|
||||||
|
|
||||||
private final int monitorData;
|
private final int monitorData;
|
||||||
private int uniformBuffer = 0;
|
private int uniformBuffer = 0;
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ public class MonitorTextureBufferShader extends ShaderInstance {
|
|||||||
private Uniform getUniformChecked(String name) {
|
private Uniform getUniformChecked(String name) {
|
||||||
var uniform = getUniform(name);
|
var uniform = getUniform(name);
|
||||||
if (uniform == null) {
|
if (uniform == null) {
|
||||||
LOGGER.warn("Monitor shader {} should have uniform {}, but it was not present.", getName(), name);
|
LOG.warn("Monitor shader {} should have uniform {}, but it was not present.", getName(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return uniform;
|
return uniform;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package dan200.computercraft.client.sound;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.audio.Channel;
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||||
import io.netty.buffer.ByteBuf;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.client.sounds.AudioStream;
|
import net.minecraft.client.sounds.AudioStream;
|
||||||
import net.minecraft.client.sounds.SoundEngine;
|
import net.minecraft.client.sounds.SoundEngine;
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
@@ -36,7 +36,7 @@ class DfpwmStream implements AudioStream {
|
|||||||
/**
|
/**
|
||||||
* The {@link Channel} which this sound is playing on.
|
* The {@link Channel} which this sound is playing on.
|
||||||
*
|
*
|
||||||
* @see SpeakerInstance#pushAudio(ByteBuf)
|
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Channel channel;
|
Channel channel;
|
||||||
@@ -44,7 +44,7 @@ class DfpwmStream implements AudioStream {
|
|||||||
/**
|
/**
|
||||||
* The underlying {@link SoundEngine} executor.
|
* The underlying {@link SoundEngine} executor.
|
||||||
*
|
*
|
||||||
* @see SpeakerInstance#pushAudio(ByteBuf)
|
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||||
* @see SoundEngine#executor
|
* @see SoundEngine#executor
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -58,12 +58,12 @@ class DfpwmStream implements AudioStream {
|
|||||||
DfpwmStream() {
|
DfpwmStream() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(ByteBuf input) {
|
void push(ByteBuffer input) {
|
||||||
var readable = input.readableBytes();
|
var readable = input.remaining();
|
||||||
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
||||||
|
|
||||||
for (var i = 0; i < readable; i++) {
|
for (var i = 0; i < readable; i++) {
|
||||||
var inputByte = input.readByte();
|
var inputByte = input.get();
|
||||||
for (var j = 0; j < 8; j++) {
|
for (var j = 0; j < 8; j++) {
|
||||||
var currentBit = (inputByte & 1) != 0;
|
var currentBit = (inputByte & 1) != 0;
|
||||||
var target = currentBit ? 127 : -128;
|
var target = currentBit ? 127 : -128;
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ package dan200.computercraft.client.sound;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.core.util.Nullability;
|
import dan200.computercraft.core.util.Nullability;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
||||||
@@ -25,7 +25,7 @@ public class SpeakerInstance {
|
|||||||
SpeakerInstance() {
|
SpeakerInstance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void pushAudio(ByteBuf buffer) {
|
private void pushAudio(ByteBuffer buffer) {
|
||||||
var sound = this.sound;
|
var sound = this.sound;
|
||||||
|
|
||||||
var stream = currentStream;
|
var stream = currentStream;
|
||||||
@@ -43,7 +43,9 @@ public class SpeakerInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playAudio(SpeakerPosition position, float volume) {
|
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
|
||||||
|
pushAudio(buffer);
|
||||||
|
|
||||||
var soundManager = Minecraft.getInstance().getSoundManager();
|
var soundManager = Minecraft.getInstance().getSoundManager();
|
||||||
|
|
||||||
if (sound != null && sound.stream != currentStream) {
|
if (sound != null && sound.stream != currentStream) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
|||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
||||||
var active = false;
|
var active = false;
|
||||||
if (turtle != null) {
|
if (turtle != null) {
|
||||||
var turtleNBT = turtle.getUpgradeNBTData(side);
|
var turtleNBT = turtle.getUpgradeNBTData(side);
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
|||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import dan200.computercraft.impl.TurtleUpgrades;
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
import dan200.computercraft.impl.UpgradeManager;
|
import dan200.computercraft.impl.UpgradeManager;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
@@ -24,14 +25,13 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A registry of {@link TurtleUpgradeModeller}s.
|
* A registry of {@link TurtleUpgradeModeller}s.
|
||||||
*
|
|
||||||
* @see dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)
|
|
||||||
*/
|
*/
|
||||||
public final class TurtleUpgradeModellers {
|
public final class TurtleUpgradeModellers {
|
||||||
private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side) ->
|
private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side, data) ->
|
||||||
new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
|
new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
|
||||||
|
|
||||||
private static final Map<TurtleUpgradeSerialiser<?>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
|
private static final Map<UpgradeSerialiser<? extends ITurtleUpgrade>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
|
||||||
|
private static volatile boolean fetchedModels;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
|
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
|
||||||
@@ -44,26 +44,29 @@ public final class TurtleUpgradeModellers {
|
|||||||
private TurtleUpgradeModellers() {
|
private TurtleUpgradeModellers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
public static <T extends ITurtleUpgrade> void register(UpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||||
synchronized (turtleModels) {
|
if (fetchedModels) {
|
||||||
if (turtleModels.containsKey(serialiser)) {
|
throw new IllegalStateException(String.format(
|
||||||
throw new IllegalStateException("Modeller already registered for serialiser");
|
"Turtle upgrade serialiser %s must be registered before models are baked.",
|
||||||
}
|
RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.serialiserRegistryKey()), serialiser)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
turtleModels.put(serialiser, modeller);
|
if (turtleModels.putIfAbsent(serialiser, modeller) != null) {
|
||||||
|
throw new IllegalStateException("Modeller already registered for serialiser");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||||
return modeller.getModel(upgrade, access, side);
|
return modeller.getModel(upgrade, access, side, access.getUpgradeNBTData(side));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||||
return modeller.getModel(upgrade, data, side);
|
return modeller.getModel(upgrade, null, side, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) {
|
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) {
|
||||||
@@ -75,6 +78,7 @@ public final class TurtleUpgradeModellers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<ResourceLocation> getDependencies() {
|
public static Stream<ResourceLocation> getDependencies() {
|
||||||
|
fetchedModels = true;
|
||||||
return turtleModels.values().stream().flatMap(x -> x.getDependencies().stream());
|
return turtleModels.values().stream().flatMap(x -> x.getDependencies().stream());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"required": true,
|
|
||||||
"package": "dan200.computercraft.mixin.client",
|
|
||||||
"minVersion": "0.8",
|
|
||||||
"compatibilityLevel": "JAVA_17",
|
|
||||||
"injectors": {
|
|
||||||
"defaultRequire": 1
|
|
||||||
},
|
|
||||||
"client": [
|
|
||||||
"ClientPacketListenerMixin"
|
|
||||||
],
|
|
||||||
"refmap": "client-computercraft.refmap.json"
|
|
||||||
}
|
|
||||||
@@ -17,13 +17,12 @@ import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
|||||||
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
import dan200.computercraft.shared.config.ConfigFile;
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.config.ConfigSpec;
|
import dan200.computercraft.shared.config.ConfigSpec;
|
||||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.data.CachedOutput;
|
import net.minecraft.data.CachedOutput;
|
||||||
import net.minecraft.data.DataProvider;
|
import net.minecraft.data.DataProvider;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -174,6 +173,7 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
// Metrics
|
// Metrics
|
||||||
add(Metrics.COMPUTER_TASKS, "Tasks");
|
add(Metrics.COMPUTER_TASKS, "Tasks");
|
||||||
add(Metrics.SERVER_TASKS, "Server tasks");
|
add(Metrics.SERVER_TASKS, "Server tasks");
|
||||||
|
add(Metrics.JAVA_ALLOCATION, "Java Allocations");
|
||||||
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
|
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
|
||||||
add(Metrics.FS_OPS, "Filesystem operations");
|
add(Metrics.FS_OPS, "Filesystem operations");
|
||||||
add(Metrics.HTTP_REQUESTS, "HTTP requests");
|
add(Metrics.HTTP_REQUESTS, "HTTP requests");
|
||||||
@@ -213,7 +213,6 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
|
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
|
||||||
addConfigEntry(ConfigSpec.uploadMaxSize, "File upload size limit (bytes)");
|
addConfigEntry(ConfigSpec.uploadMaxSize, "File upload size limit (bytes)");
|
||||||
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
|
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
|
||||||
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
|
|
||||||
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
|
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
|
||||||
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
|
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
|
||||||
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");
|
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");
|
||||||
@@ -272,12 +271,12 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
|
|
||||||
private Stream<String> getExpectedKeys() {
|
private Stream<String> getExpectedKeys() {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
RegistryWrappers.BLOCKS.stream()
|
BuiltInRegistries.BLOCK.holders()
|
||||||
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||||
.map(Block::getDescriptionId),
|
.map(x -> x.value().getDescriptionId()),
|
||||||
RegistryWrappers.ITEMS.stream()
|
BuiltInRegistries.ITEM.holders()
|
||||||
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||||
.map(Item::getDescriptionId),
|
.map(x -> x.value().getDescriptionId()),
|
||||||
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||||
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||||
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.data.CachedOutput;
|
import net.minecraft.data.CachedOutput;
|
||||||
import net.minecraft.data.DataProvider;
|
import net.minecraft.data.DataProvider;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
@@ -67,7 +68,7 @@ public class ModelProvider implements DataProvider {
|
|||||||
blocks.accept(new BlockModelGenerators(addBlockState, addModel, explicitItems::add));
|
blocks.accept(new BlockModelGenerators(addBlockState, addModel, explicitItems::add));
|
||||||
items.accept(new ItemModelGenerators(addModel));
|
items.accept(new ItemModelGenerators(addModel));
|
||||||
|
|
||||||
for (var block : RegistryWrappers.BLOCKS) {
|
for (var block : BuiltInRegistries.BLOCK) {
|
||||||
if (!blockStates.containsKey(block)) continue;
|
if (!blockStates.containsKey(block)) continue;
|
||||||
|
|
||||||
var item = Item.BY_BLOCK.get(block);
|
var item = Item.BY_BLOCK.get(block);
|
||||||
@@ -80,7 +81,7 @@ public class ModelProvider implements DataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||||
saveCollection(output, futures, blockStates, x -> blockStatePath.json(RegistryWrappers.BLOCKS.getKey(x)));
|
saveCollection(output, futures, blockStates, x -> blockStatePath.json(RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK, x)));
|
||||||
saveCollection(output, futures, models, modelPath::json);
|
saveCollection(output, futures, models, modelPath::json);
|
||||||
return Util.sequenceFailFast(futures);
|
return Util.sequenceFailFast(futures);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ class PocketUpgradeProvider extends PocketUpgradeDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addUpgrades(Consumer<Upgrade<PocketUpgradeSerialiser<?>>> addUpgrade) {
|
protected void addUpgrades(Consumer<Upgrade<UpgradeSerialiser<? extends IPocketUpgrade>>> addUpgrade) {
|
||||||
addUpgrade.accept(simpleWithCustomItem(id("speaker"), PocketUpgradeSerialisers.SPEAKER.get(), Items.SPEAKER.get()));
|
addUpgrade.accept(simpleWithCustomItem(id("speaker"), PocketUpgradeSerialisers.SPEAKER.get(), Items.SPEAKER.get()));
|
||||||
simpleWithCustomItem(id("wireless_modem_normal"), PocketUpgradeSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade);
|
simpleWithCustomItem(id("wireless_modem_normal"), PocketUpgradeSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade);
|
||||||
simpleWithCustomItem(id("wireless_modem_advanced"), PocketUpgradeSerialisers.WIRELESS_MODEM_ADVANCED.get(), Items.WIRELESS_MODEM_ADVANCED.get()).add(addUpgrade);
|
simpleWithCustomItem(id("wireless_modem_advanced"), PocketUpgradeSerialisers.WIRELESS_MODEM_ADVANCED.get(), Items.WIRELESS_MODEM_ADVANCED.get()).add(addUpgrade);
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.HashFunction;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
|
import net.minecraft.data.CachedOutput;
|
||||||
|
import net.minecraft.data.DataProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an existing {@link DataProvider}, passing generated JSON through {@link PrettyJsonWriter}.
|
||||||
|
*
|
||||||
|
* @param provider The provider to wrap.
|
||||||
|
* @param <T> The type of the provider to wrap.
|
||||||
|
*/
|
||||||
|
public record PrettyDataProvider<T extends DataProvider>(T provider) implements DataProvider {
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<?> run(CachedOutput cachedOutput) {
|
||||||
|
return provider.run(new Output(cachedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return provider.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Output(CachedOutput output) implements CachedOutput {
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static final HashFunction HASH_FUNCTION = Hashing.sha1();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeIfNeeded(Path path, byte[] bytes, HashCode hashCode) throws IOException {
|
||||||
|
if (path.getFileName().toString().endsWith(".json")) {
|
||||||
|
bytes = PrettyJsonWriter.reformat(bytes);
|
||||||
|
hashCode = HASH_FUNCTION.hashBytes(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.writeIfNeeded(path, bytes, hashCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,11 +22,10 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Alternative version of {@link JsonWriter} which attempts to lay out the JSON in a more compact format.
|
* Alternative version of {@link JsonWriter} which attempts to lay out the JSON in a more compact format.
|
||||||
* <p>
|
*
|
||||||
* Yes, this is at least a little deranged.
|
* @see PrettyDataProvider
|
||||||
*/
|
*/
|
||||||
public class PrettyJsonWriter extends JsonWriter {
|
public class PrettyJsonWriter extends JsonWriter {
|
||||||
public static final boolean ENABLED = System.getProperty("cct.pretty-json") != null;
|
|
||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||||
|
|
||||||
private static final int MAX_WIDTH = 120;
|
private static final int MAX_WIDTH = 120;
|
||||||
@@ -44,17 +43,6 @@ public class PrettyJsonWriter extends JsonWriter {
|
|||||||
this.out = out;
|
this.out = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a JSON writer. This will either be a pretty or normal version, depending on whether the global flag is
|
|
||||||
* set.
|
|
||||||
*
|
|
||||||
* @param out The writer to emit to.
|
|
||||||
* @return The constructed JSON writer.
|
|
||||||
*/
|
|
||||||
public static JsonWriter createWriter(Writer out) {
|
|
||||||
return ENABLED ? new PrettyJsonWriter(out) : new JsonWriter(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reformat a JSON string with our pretty printer.
|
* Reformat a JSON string with our pretty printer.
|
||||||
*
|
*
|
||||||
@@ -62,8 +50,6 @@ public class PrettyJsonWriter extends JsonWriter {
|
|||||||
* @return The reformatted string.
|
* @return The reformatted string.
|
||||||
*/
|
*/
|
||||||
public static byte[] reformat(byte[] contents) {
|
public static byte[] reformat(byte[] contents) {
|
||||||
if (!ENABLED) return contents;
|
|
||||||
|
|
||||||
JsonElement object;
|
JsonElement object;
|
||||||
try (var reader = new InputStreamReader(new ByteArrayInputStream(contents), StandardCharsets.UTF_8)) {
|
try (var reader = new InputStreamReader(new ByteArrayInputStream(contents), StandardCharsets.UTF_8)) {
|
||||||
object = GSON.fromJson(reader, JsonElement.class);
|
object = GSON.fromJson(reader, JsonElement.class);
|
||||||
|
|||||||
@@ -5,47 +5,67 @@
|
|||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.serialization.JsonOps;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
|
import dan200.computercraft.data.recipe.ShapedSpecBuilder;
|
||||||
|
import dan200.computercraft.data.recipe.ShapelessSpecBuilder;
|
||||||
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||||
|
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
||||||
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
|
import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
||||||
|
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.platform.RecipeIngredients;
|
import dan200.computercraft.shared.platform.RecipeIngredients;
|
||||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
|
||||||
|
import dan200.computercraft.shared.recipe.CustomShapelessRecipe;
|
||||||
|
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
||||||
|
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
|
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
||||||
|
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
||||||
|
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||||
import dan200.computercraft.shared.util.ColourUtils;
|
import dan200.computercraft.shared.util.ColourUtils;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.advancements.Criterion;
|
||||||
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
||||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.data.recipes.*;
|
import net.minecraft.data.recipes.RecipeCategory;
|
||||||
|
import net.minecraft.data.recipes.RecipeOutput;
|
||||||
|
import net.minecraft.data.recipes.ShapedRecipeBuilder;
|
||||||
|
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.NbtUtils;
|
import net.minecraft.nbt.NbtUtils;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.world.item.*;
|
import net.minecraft.world.item.*;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||||
import net.minecraft.world.item.crafting.Ingredient;
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
import net.minecraft.world.item.crafting.ShapedRecipe;
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
|
||||||
import net.minecraft.world.level.ItemLike;
|
import net.minecraft.world.level.ItemLike;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
||||||
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
|
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
|
||||||
|
|
||||||
class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||||
private final RecipeIngredients ingredients = PlatformHelper.get().getRecipeIngredients();
|
private final RecipeIngredients ingredients = PlatformHelper.get().getRecipeIngredients();
|
||||||
private final TurtleUpgradeDataProvider turtleUpgrades;
|
private final TurtleUpgradeDataProvider turtleUpgrades;
|
||||||
private final PocketUpgradeDataProvider pocketUpgrades;
|
private final PocketUpgradeDataProvider pocketUpgrades;
|
||||||
@@ -57,40 +77,37 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void buildRecipes(Consumer<FinishedRecipe> add) {
|
public void buildRecipes(RecipeOutput add) {
|
||||||
basicRecipes(add);
|
basicRecipes(add);
|
||||||
diskColours(add);
|
diskColours(add);
|
||||||
pocketUpgrades(add);
|
pocketUpgrades(add);
|
||||||
turtleUpgrades(add);
|
turtleUpgrades(add);
|
||||||
turtleOverlays(add);
|
turtleOverlays(add);
|
||||||
|
|
||||||
addSpecial(add, ModRegistry.RecipeSerializers.PRINTOUT.get());
|
addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC));
|
||||||
addSpecial(add, ModRegistry.RecipeSerializers.DISK.get());
|
addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC));
|
||||||
addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM.get());
|
addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC));
|
||||||
addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM_CLEAR.get());
|
addSpecial(add, new ClearColourRecipe(CraftingBookCategory.MISC));
|
||||||
addSpecial(add, ModRegistry.RecipeSerializers.TURTLE_UPGRADE.get());
|
addSpecial(add, new TurtleUpgradeRecipe(CraftingBookCategory.MISC));
|
||||||
addSpecial(add, ModRegistry.RecipeSerializers.POCKET_COMPUTER_UPGRADE.get());
|
addSpecial(add, new PocketComputerUpgradeRecipe(CraftingBookCategory.MISC));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a crafting recipe for a disk of every dye colour.
|
* Register a crafting recipe for a disk of every dye colour.
|
||||||
*
|
*
|
||||||
* @param add The callback to add recipes.
|
* @param output The callback to add recipes.
|
||||||
*/
|
*/
|
||||||
private void diskColours(Consumer<FinishedRecipe> add) {
|
private void diskColours(RecipeOutput output) {
|
||||||
for (var colour : Colour.VALUES) {
|
for (var colour : Colour.VALUES) {
|
||||||
ShapelessRecipeBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.DISK.get())
|
.shapeless(RecipeCategory.REDSTONE, DiskItem.createFromIDAndColour(-1, null, colour.getHex()))
|
||||||
.requires(ingredients.redstone())
|
.requires(ingredients.redstone())
|
||||||
.requires(Items.PAPER)
|
.requires(Items.PAPER)
|
||||||
.requires(DyeItem.byColor(ofColour(colour)))
|
.requires(DyeItem.byColor(ofColour(colour)))
|
||||||
.group("computercraft:disk")
|
.group("computercraft:disk")
|
||||||
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get()))
|
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get()))
|
||||||
.save(
|
.build(ImpostorShapelessRecipe::new)
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add)
|
.save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1)));
|
||||||
.withResultTag(x -> x.putInt(IColouredItem.NBT_COLOUR, colour.getHex())),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,27 +120,23 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
*
|
*
|
||||||
* @param add The callback to add recipes.
|
* @param add The callback to add recipes.
|
||||||
*/
|
*/
|
||||||
private void turtleUpgrades(Consumer<FinishedRecipe> add) {
|
private void turtleUpgrades(RecipeOutput add) {
|
||||||
for (var turtleItem : turtleItems()) {
|
for (var turtleItem : turtleItems()) {
|
||||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||||
|
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
|
||||||
|
|
||||||
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
||||||
var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null);
|
ShapedSpecBuilder
|
||||||
ShapedRecipeBuilder
|
.shaped(RecipeCategory.REDSTONE, turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null))
|
||||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
.group(name.toString())
|
||||||
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
|
|
||||||
.pattern("#T")
|
.pattern("#T")
|
||||||
.define('T', base.getItem())
|
.define('T', base.getItem())
|
||||||
.define('#', upgrade.getCraftingItem().getItem())
|
.define('#', upgrade.getCraftingItem().getItem())
|
||||||
.unlockedBy("has_items",
|
.unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||||
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
.build(ImpostorShapedRecipe::new)
|
||||||
.save(
|
.save(
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
|
add,
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("turtle_%s/%s/%s",
|
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
|
||||||
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,35 +151,30 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
*
|
*
|
||||||
* @param add The callback to add recipes.
|
* @param add The callback to add recipes.
|
||||||
*/
|
*/
|
||||||
private void pocketUpgrades(Consumer<FinishedRecipe> add) {
|
private void pocketUpgrades(RecipeOutput add) {
|
||||||
for (var pocket : pocketComputerItems()) {
|
for (var pocket : pocketComputerItems()) {
|
||||||
var base = pocket.create(-1, null, -1, null);
|
var base = pocket.create(-1, null, -1, null);
|
||||||
if (base.isEmpty()) continue;
|
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
|
||||||
|
|
||||||
var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT);
|
|
||||||
|
|
||||||
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
|
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
|
||||||
var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade));
|
ShapedSpecBuilder
|
||||||
ShapedRecipeBuilder
|
.shaped(RecipeCategory.REDSTONE, pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade)))
|
||||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
.group(name.toString())
|
||||||
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
|
|
||||||
.pattern("#")
|
.pattern("#")
|
||||||
.pattern("P")
|
.pattern("P")
|
||||||
.define('P', base.getItem())
|
.define('P', base.getItem())
|
||||||
.define('#', upgrade.getCraftingItem().getItem())
|
.define('#', upgrade.getCraftingItem().getItem())
|
||||||
.unlockedBy("has_items",
|
.unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||||
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
.build(ImpostorShapedRecipe::new)
|
||||||
.save(
|
.save(
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
|
add,
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("pocket_%s/%s/%s",
|
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
|
||||||
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void turtleOverlays(Consumer<FinishedRecipe> add) {
|
private void turtleOverlays(RecipeOutput add) {
|
||||||
turtleOverlay(add, "turtle_trans_overlay", x -> x
|
turtleOverlay(add, "turtle_trans_overlay", x -> x
|
||||||
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
|
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
|
||||||
.requires(ColourUtils.getDyeTag(DyeColor.LIGHT_BLUE))
|
.requires(ColourUtils.getDyeTag(DyeColor.LIGHT_BLUE))
|
||||||
@@ -187,30 +195,24 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consumer<ShapelessRecipeBuilder> build) {
|
private void turtleOverlay(RecipeOutput add, String overlay, Consumer<ShapelessSpecBuilder> build) {
|
||||||
for (var turtleItem : turtleItems()) {
|
for (var turtleItem : turtleItems()) {
|
||||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||||
|
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||||
|
|
||||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
var builder = ShapelessSpecBuilder.shapeless(RecipeCategory.REDSTONE, base)
|
||||||
var group = "%s:turtle_%s_overlay".formatted(ComputerCraftAPI.MOD_ID, nameId);
|
.group(name.withSuffix("_overlay").toString())
|
||||||
|
|
||||||
var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem())
|
|
||||||
.group(group)
|
|
||||||
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
|
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
|
||||||
build.accept(builder);
|
build.accept(builder);
|
||||||
builder
|
builder
|
||||||
.requires(base.getItem())
|
.requires(base.getItem())
|
||||||
.save(
|
.build(s -> new TurtleOverlayRecipe(s, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay)))
|
||||||
RecipeWrapper
|
.save(add, name.withSuffix("_overlays/" + overlay));
|
||||||
.wrap(ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get(), add)
|
|
||||||
.withExtraData(x -> x.addProperty("overlay", new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay).toString())),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_%s_overlays/%s".formatted(nameId, overlay))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void basicRecipes(Consumer<FinishedRecipe> add) {
|
private void basicRecipes(RecipeOutput add) {
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.CABLE.get(), 6)
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.CABLE.get(), 6)
|
||||||
.pattern(" # ")
|
.pattern(" # ")
|
||||||
@@ -244,7 +246,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ingredients.redstone()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ingredients.redstone()), itemPredicate(ingredients.goldIngot())))
|
||||||
.save(add);
|
.save(add);
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||||
.pattern("###")
|
.pattern("###")
|
||||||
.pattern("#C#")
|
.pattern("#C#")
|
||||||
@@ -252,10 +254,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('#', ingredients.goldIngot())
|
.define('#', ingredients.goldIngot())
|
||||||
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||||
.save(
|
.build(ComputerUpgradeRecipe::new)
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")
|
|
||||||
);
|
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
|
||||||
@@ -268,7 +268,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK))
|
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK))
|
||||||
.save(add);
|
.save(add);
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
|
||||||
.pattern("###")
|
.pattern("###")
|
||||||
.pattern("#C#")
|
.pattern("#C#")
|
||||||
@@ -277,9 +277,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||||
.define('I', ingredients.woodenChest())
|
.define('I', ingredients.woodenChest())
|
||||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.NORMAL)));
|
.buildOrThrow(TurtleRecipe::of)
|
||||||
|
.save(add);
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||||
.pattern("###")
|
.pattern("###")
|
||||||
.pattern("#C#")
|
.pattern("#C#")
|
||||||
@@ -288,9 +289,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
|
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||||
.define('I', ingredients.woodenChest())
|
.define('I', ingredients.woodenChest())
|
||||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)));
|
.buildOrThrow(TurtleRecipe::of)
|
||||||
|
.save(add);
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||||
.pattern("###")
|
.pattern("###")
|
||||||
.pattern("#C#")
|
.pattern("#C#")
|
||||||
@@ -299,10 +301,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('C', ModRegistry.Items.TURTLE_NORMAL.get())
|
.define('C', ModRegistry.Items.TURTLE_NORMAL.get())
|
||||||
.define('B', ingredients.goldBlock())
|
.define('B', ingredients.goldBlock())
|
||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||||
.save(
|
.build(ComputerUpgradeRecipe::new)
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")
|
|
||||||
);
|
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
|
||||||
@@ -358,7 +358,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.unlockedBy("has_apple", inventoryChange(Items.GOLDEN_APPLE))
|
.unlockedBy("has_apple", inventoryChange(Items.GOLDEN_APPLE))
|
||||||
.save(add);
|
.save(add);
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
|
||||||
.pattern("###")
|
.pattern("###")
|
||||||
.pattern("#C#")
|
.pattern("#C#")
|
||||||
@@ -366,10 +366,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('#', ingredients.goldIngot())
|
.define('#', ingredients.goldIngot())
|
||||||
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
|
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
|
||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||||
.save(
|
.build(ComputerUpgradeRecipe::new)
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")
|
|
||||||
);
|
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
|
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
|
||||||
@@ -436,57 +434,53 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get()))
|
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get()))
|
||||||
.save(add);
|
.save(add);
|
||||||
|
|
||||||
ShapelessRecipeBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD)
|
.shapeless(RecipeCategory.DECORATIONS, playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c"))
|
||||||
.requires(ingredients.head())
|
.requires(ingredients.head())
|
||||||
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
|
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
|
||||||
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
|
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
|
||||||
.save(
|
.build(CustomShapelessRecipe::new)
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy"));
|
||||||
.withResultTag(playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c")),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy")
|
|
||||||
);
|
|
||||||
|
|
||||||
ShapelessRecipeBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD)
|
.shapeless(RecipeCategory.DECORATIONS, playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb"))
|
||||||
.requires(ingredients.head())
|
.requires(ingredients.head())
|
||||||
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
|
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
||||||
.save(
|
.build(CustomShapelessRecipe::new)
|
||||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
|
||||||
.withResultTag(playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb")),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200")
|
|
||||||
);
|
|
||||||
|
|
||||||
ShapelessRecipeBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get())
|
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get())
|
||||||
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 2)
|
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 2)
|
||||||
.requires(ingredients.string())
|
.requires(ingredients.string())
|
||||||
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
|
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
|
||||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add));
|
.build(ImpostorShapelessRecipe::new)
|
||||||
|
.save(add);
|
||||||
|
|
||||||
ShapelessRecipeBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get())
|
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get())
|
||||||
.requires(ingredients.leather())
|
.requires(ingredients.leather())
|
||||||
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
|
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
|
||||||
.requires(ingredients.string())
|
.requires(ingredients.string())
|
||||||
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
|
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
|
||||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add));
|
.build(ImpostorShapelessRecipe::new)
|
||||||
|
.save(add);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DyeColor ofColour(Colour colour) {
|
private static DyeColor ofColour(Colour colour) {
|
||||||
return DyeColor.byId(15 - colour.ordinal());
|
return DyeColor.byId(15 - colour.ordinal());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InventoryChangeTrigger.TriggerInstance inventoryChange(TagKey<Item> stack) {
|
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(TagKey<Item> stack) {
|
||||||
return InventoryChangeTrigger.TriggerInstance.hasItems(itemPredicate(stack));
|
return InventoryChangeTrigger.TriggerInstance.hasItems(itemPredicate(stack));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InventoryChangeTrigger.TriggerInstance inventoryChange(ItemLike... stack) {
|
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(ItemLike... stack) {
|
||||||
return InventoryChangeTrigger.TriggerInstance.hasItems(stack);
|
return InventoryChangeTrigger.TriggerInstance.hasItems(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InventoryChangeTrigger.TriggerInstance inventoryChange(ItemPredicate... items) {
|
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(ItemPredicate... items) {
|
||||||
return InventoryChangeTrigger.TriggerInstance.hasItems(items);
|
return InventoryChangeTrigger.TriggerInstance.hasItems(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,11 +493,12 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ItemPredicate itemPredicate(Ingredient ingredient) {
|
private static ItemPredicate itemPredicate(Ingredient ingredient) {
|
||||||
var json = ingredient.toJson();
|
var json = Util.getOrThrow(Ingredient.CODEC_NONEMPTY.encodeStart(JsonOps.INSTANCE, ingredient), JsonParseException::new);
|
||||||
if (!(json instanceof JsonObject object)) throw new IllegalStateException("Unknown ingredient " + json);
|
if (!(json instanceof JsonObject object)) throw new IllegalStateException("Unknown ingredient " + json);
|
||||||
|
|
||||||
if (object.has("item")) {
|
if (object.has("item")) {
|
||||||
return itemPredicate(ShapedRecipe.itemFromJson(object));
|
var item = Util.getOrThrow(ItemStack.ITEM_WITH_COUNT_CODEC.parse(JsonOps.INSTANCE, object), JsonParseException::new);
|
||||||
|
return itemPredicate(item.getItem());
|
||||||
} else if (object.has("tag")) {
|
} else if (object.has("tag")) {
|
||||||
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
|
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
|
||||||
} else {
|
} else {
|
||||||
@@ -511,19 +506,14 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompoundTag playerHead(String name, String uuid) {
|
private static ItemStack playerHead(String name, String uuid) {
|
||||||
|
var item = new ItemStack(Items.PLAYER_HEAD);
|
||||||
var owner = NbtUtils.writeGameProfile(new CompoundTag(), new GameProfile(UUID.fromString(uuid), name));
|
var owner = NbtUtils.writeGameProfile(new CompoundTag(), new GameProfile(UUID.fromString(uuid), name));
|
||||||
|
item.getOrCreateTag().put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
|
||||||
var tag = new CompoundTag();
|
return item;
|
||||||
tag.put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Consumer<JsonObject> family(ComputerFamily family) {
|
private static void addSpecial(RecipeOutput add, Recipe<?> recipe) {
|
||||||
return json -> json.addProperty("family", family.getSerializedName());
|
add.accept(RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, recipe.getSerializer()), recipe, null);
|
||||||
}
|
|
||||||
|
|
||||||
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
|
|
||||||
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.data;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import net.minecraft.data.recipes.FinishedRecipe;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.util.GsonHelper;
|
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter for recipes which overrides the serializer and adds custom item NBT.
|
|
||||||
*/
|
|
||||||
final class RecipeWrapper implements Consumer<FinishedRecipe> {
|
|
||||||
private final Consumer<FinishedRecipe> add;
|
|
||||||
private final RecipeSerializer<?> serializer;
|
|
||||||
private final List<Consumer<JsonObject>> extend = new ArrayList<>(0);
|
|
||||||
|
|
||||||
RecipeWrapper(Consumer<FinishedRecipe> add, RecipeSerializer<?> serializer) {
|
|
||||||
this.add = add;
|
|
||||||
this.serializer = serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RecipeWrapper wrap(RecipeSerializer<?> serializer, Consumer<FinishedRecipe> original) {
|
|
||||||
return new RecipeWrapper(original, serializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecipeWrapper withExtraData(Consumer<JsonObject> extra) {
|
|
||||||
extend.add(extra);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecipeWrapper withResultTag(@Nullable CompoundTag resultTag) {
|
|
||||||
if (resultTag == null) return this;
|
|
||||||
|
|
||||||
extend.add(json -> {
|
|
||||||
var object = GsonHelper.getAsJsonObject(json, "result");
|
|
||||||
object.addProperty("nbt", resultTag.toString());
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecipeWrapper withResultTag(Consumer<CompoundTag> resultTag) {
|
|
||||||
var tag = new CompoundTag();
|
|
||||||
resultTag.accept(tag);
|
|
||||||
return withResultTag(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(FinishedRecipe finishedRecipe) {
|
|
||||||
add.accept(new RecipeImpl(finishedRecipe, serializer, extend));
|
|
||||||
}
|
|
||||||
|
|
||||||
private record RecipeImpl(
|
|
||||||
FinishedRecipe recipe, RecipeSerializer<?> serializer, List<Consumer<JsonObject>> extend
|
|
||||||
) implements FinishedRecipe {
|
|
||||||
@Override
|
|
||||||
public void serializeRecipeData(JsonObject jsonObject) {
|
|
||||||
recipe.serializeRecipeData(jsonObject);
|
|
||||||
for (var extender : extend) extender.accept(jsonObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation getId() {
|
|
||||||
return recipe.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RecipeSerializer<?> getType() {
|
|
||||||
return serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public JsonObject serializeAdvancement() {
|
|
||||||
return recipe.serializeAdvancement();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public ResourceLocation getAdvancementId() {
|
|
||||||
return recipe.getAdvancementId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,9 @@
|
|||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftTags;
|
import dan200.computercraft.api.ComputerCraftTags;
|
||||||
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.data.tags.ItemTagsProvider;
|
import net.minecraft.data.tags.ItemTagsProvider;
|
||||||
import net.minecraft.data.tags.TagsProvider;
|
import net.minecraft.data.tags.TagsProvider;
|
||||||
import net.minecraft.tags.BlockTags;
|
import net.minecraft.tags.BlockTags;
|
||||||
@@ -58,7 +59,10 @@ class TagProvider {
|
|||||||
|
|
||||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).addTag(BlockTags.WOOL).add(Blocks.COBWEB);
|
tags.tag(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).addTag(BlockTags.WOOL).add(Blocks.COBWEB);
|
||||||
|
|
||||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE).addTag(BlockTags.CAULDRONS).addTag(BlockTags.BEEHIVES);
|
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE)
|
||||||
|
.addTag(BlockTags.BEEHIVES)
|
||||||
|
.addTag(BlockTags.CAULDRONS)
|
||||||
|
.add(Blocks.COMPOSTER);
|
||||||
|
|
||||||
// Make all blocks aside from command computer mineable.
|
// Make all blocks aside from command computer mineable.
|
||||||
tags.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(
|
tags.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(
|
||||||
@@ -76,6 +80,8 @@ class TagProvider {
|
|||||||
ModRegistry.Blocks.WIRED_MODEM_FULL.get(),
|
ModRegistry.Blocks.WIRED_MODEM_FULL.get(),
|
||||||
ModRegistry.Blocks.CABLE.get()
|
ModRegistry.Blocks.CABLE.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tags.tag(BlockTags.WITHER_IMMUNE).add(ModRegistry.Blocks.COMPUTER_COMMAND.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void itemTags(ItemTagConsumer tags) {
|
public static void itemTags(ItemTagConsumer tags) {
|
||||||
@@ -106,9 +112,9 @@ class TagProvider {
|
|||||||
TagAppender<T> tag(TagKey<T> tag);
|
TagAppender<T> tag(TagKey<T> tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record TagAppender<T>(RegistryWrappers.RegistryWrapper<T> registry, TagBuilder builder) {
|
public record TagAppender<T>(Registry<T> registry, TagBuilder builder) {
|
||||||
public TagAppender<T> add(T object) {
|
public TagAppender<T> add(T object) {
|
||||||
builder.addElement(registry.getKey(object));
|
builder.addElement(RegistryHelper.getKeyOrThrow(registry, object));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ package dan200.computercraft.data;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.ComputerCraftTags.Blocks;
|
import dan200.computercraft.api.ComputerCraftTags.Blocks;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ class TurtleUpgradeProvider extends TurtleUpgradeDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addUpgrades(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade) {
|
protected void addUpgrades(Consumer<Upgrade<UpgradeSerialiser<? extends ITurtleUpgrade>>> addUpgrade) {
|
||||||
simpleWithCustomItem(id("speaker"), TurtleSerialisers.SPEAKER.get(), Items.SPEAKER.get()).add(addUpgrade);
|
simpleWithCustomItem(id("speaker"), TurtleSerialisers.SPEAKER.get(), Items.SPEAKER.get()).add(addUpgrade);
|
||||||
simpleWithCustomItem(vanilla("crafting_table"), TurtleSerialisers.WORKBENCH.get(), net.minecraft.world.item.Items.CRAFTING_TABLE).add(addUpgrade);
|
simpleWithCustomItem(vanilla("crafting_table"), TurtleSerialisers.WORKBENCH.get(), net.minecraft.world.item.Items.CRAFTING_TABLE).add(addUpgrade);
|
||||||
simpleWithCustomItem(id("wireless_modem_normal"), TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade);
|
simpleWithCustomItem(id("wireless_modem_normal"), TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade);
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.data.recipe;
|
||||||
|
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
|
import dan200.computercraft.shared.recipe.RecipeProperties;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.advancements.AdvancementRequirements;
|
||||||
|
import net.minecraft.advancements.AdvancementRewards;
|
||||||
|
import net.minecraft.advancements.Criterion;
|
||||||
|
import net.minecraft.advancements.critereon.RecipeUnlockedTrigger;
|
||||||
|
import net.minecraft.data.recipes.RecipeBuilder;
|
||||||
|
import net.minecraft.data.recipes.RecipeCategory;
|
||||||
|
import net.minecraft.data.recipes.RecipeOutput;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract base class for creating recipes, in the style of {@link RecipeBuilder}.
|
||||||
|
*
|
||||||
|
* @param <S> The type of this class.
|
||||||
|
* @param <O> The output of this builder.
|
||||||
|
* @see ShapelessSpecBuilder
|
||||||
|
*/
|
||||||
|
public abstract class AbstractRecipeBuilder<S extends AbstractRecipeBuilder<S, O>, O> {
|
||||||
|
private final RecipeCategory category;
|
||||||
|
protected final ItemStack result;
|
||||||
|
private String group = "";
|
||||||
|
private final Map<String, Criterion<?>> criteria = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
protected AbstractRecipeBuilder(RecipeCategory category, ItemStack result) {
|
||||||
|
this.category = category;
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the group for this recipe.
|
||||||
|
*
|
||||||
|
* @param group The new group.
|
||||||
|
* @return This object, for chaining.
|
||||||
|
*/
|
||||||
|
public final S group(String group) {
|
||||||
|
this.group = group;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a criterion to this recipe.
|
||||||
|
*
|
||||||
|
* @param name The name of the criterion.
|
||||||
|
* @param criterion The criterion to add.
|
||||||
|
* @return This object, for chaining.
|
||||||
|
*/
|
||||||
|
public final S unlockedBy(String name, Criterion<?> criterion) {
|
||||||
|
criteria.put(name, criterion);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this builder into the output ({@link O}) object.
|
||||||
|
*
|
||||||
|
* @param properties The properties for this recipe.
|
||||||
|
* @return The built object.
|
||||||
|
*/
|
||||||
|
protected abstract O build(RecipeProperties properties);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this builder into a concrete recipe.
|
||||||
|
*
|
||||||
|
* @param factory The recipe's constructor.
|
||||||
|
* @return The "built" recipe.
|
||||||
|
*/
|
||||||
|
public final FinishedRecipe build(Function<O, Recipe<?>> factory) {
|
||||||
|
var properties = new RecipeProperties(group, RecipeBuilder.determineBookCategory(category), true);
|
||||||
|
return new FinishedRecipe(factory.apply(build(properties)), result.getItem(), category, criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this builder into a concrete recipe.
|
||||||
|
*
|
||||||
|
* @param factory The recipe's constructor.
|
||||||
|
* @return The "built" recipe.
|
||||||
|
*/
|
||||||
|
public final FinishedRecipe buildOrThrow(Function<O, DataResult<? extends Recipe<?>>> factory) {
|
||||||
|
return build(s -> Util.getOrThrow(factory.apply(s), IllegalStateException::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private S self() {
|
||||||
|
return (S) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class FinishedRecipe {
|
||||||
|
private final Recipe<?> recipe;
|
||||||
|
private final Item result;
|
||||||
|
private final RecipeCategory category;
|
||||||
|
private final Map<String, Criterion<?>> criteria;
|
||||||
|
|
||||||
|
private FinishedRecipe(Recipe<?> recipe, Item result, RecipeCategory category, Map<String, Criterion<?>> criteria) {
|
||||||
|
this.recipe = recipe;
|
||||||
|
this.result = result;
|
||||||
|
this.category = category;
|
||||||
|
this.criteria = criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(RecipeOutput output, ResourceLocation id) {
|
||||||
|
if (criteria.isEmpty()) throw new IllegalStateException("No way of obtaining recipe " + id);
|
||||||
|
var advancement = output.advancement()
|
||||||
|
.addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(id))
|
||||||
|
.rewards(AdvancementRewards.Builder.recipe(id))
|
||||||
|
.requirements(AdvancementRequirements.Strategy.OR);
|
||||||
|
for (var entry : criteria.entrySet()) advancement.addCriterion(entry.getKey(), entry.getValue());
|
||||||
|
|
||||||
|
output.accept(id, recipe, advancement.build(id.withPrefix("recipes/" + category.getFolderName() + "/")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(RecipeOutput output) {
|
||||||
|
save(output, RecipeBuilder.getDefaultRecipeId(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.data.recipe;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.recipe.RecipeProperties;
|
||||||
|
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
|
||||||
|
import net.minecraft.data.recipes.RecipeCategory;
|
||||||
|
import net.minecraft.data.recipes.ShapedRecipeBuilder;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.world.item.crafting.ShapedRecipePattern;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link ShapedRecipeSpec}s, much like {@link ShapedRecipeBuilder}.
|
||||||
|
*/
|
||||||
|
public final class ShapedSpecBuilder extends AbstractRecipeBuilder<ShapedSpecBuilder, ShapedRecipeSpec> {
|
||||||
|
private final List<String> rows = new ArrayList<>();
|
||||||
|
private final Map<Character, Ingredient> key = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private ShapedSpecBuilder(RecipeCategory category, ItemStack result) {
|
||||||
|
super(category, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShapedSpecBuilder shaped(RecipeCategory category, ItemStack result) {
|
||||||
|
return new ShapedSpecBuilder(category, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShapedSpecBuilder shaped(RecipeCategory category, ItemLike result) {
|
||||||
|
return new ShapedSpecBuilder(category, new ItemStack(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapedSpecBuilder define(char key, Ingredient ingredient) {
|
||||||
|
if (this.key.containsKey(key)) throw new IllegalArgumentException("Symbol '" + key + "' is already defined!");
|
||||||
|
if (key == ' ') throw new IllegalArgumentException("Symbol ' ' (whitespace) is reserved and cannot be defined");
|
||||||
|
|
||||||
|
this.key.put(key, ingredient);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapedSpecBuilder define(char key, TagKey<Item> tag) {
|
||||||
|
return this.define(key, Ingredient.of(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapedSpecBuilder define(char key, ItemLike item) {
|
||||||
|
return this.define(key, Ingredient.of(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapedSpecBuilder pattern(String pattern) {
|
||||||
|
if (!this.rows.isEmpty() && pattern.length() != this.rows.get(0).length()) {
|
||||||
|
throw new IllegalArgumentException("Pattern must be the same width on every line!");
|
||||||
|
} else {
|
||||||
|
this.rows.add(pattern);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ShapedRecipeSpec build(RecipeProperties properties) {
|
||||||
|
return new ShapedRecipeSpec(properties, ShapedRecipePattern.of(key, rows), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.data.recipe;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.recipe.RecipeProperties;
|
||||||
|
import dan200.computercraft.shared.recipe.ShapelessRecipeSpec;
|
||||||
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.data.recipes.RecipeCategory;
|
||||||
|
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link ShapelessRecipeSpec}s, much like {@link ShapelessRecipeBuilder}.
|
||||||
|
*/
|
||||||
|
public final class ShapelessSpecBuilder extends AbstractRecipeBuilder<ShapelessSpecBuilder, ShapelessRecipeSpec> {
|
||||||
|
private final NonNullList<Ingredient> ingredients = NonNullList.create();
|
||||||
|
|
||||||
|
private ShapelessSpecBuilder(RecipeCategory category, ItemStack result) {
|
||||||
|
super(category, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShapelessSpecBuilder shapeless(RecipeCategory category, ItemStack result) {
|
||||||
|
return new ShapelessSpecBuilder(category, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShapelessSpecBuilder shapeless(RecipeCategory category, ItemLike result) {
|
||||||
|
return new ShapelessSpecBuilder(category, new ItemStack(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessSpecBuilder requires(Ingredient ingredient, int count) {
|
||||||
|
for (int i = 0; i < count; i++) ingredients.add(ingredient);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessSpecBuilder requires(Ingredient ingredient) {
|
||||||
|
return requires(ingredient, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessSpecBuilder requires(ItemLike item) {
|
||||||
|
return requires(Ingredient.of(new ItemStack(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessSpecBuilder requires(ItemLike item, int count) {
|
||||||
|
return requires(Ingredient.of(new ItemStack(item)), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessSpecBuilder requires(TagKey<Item> item) {
|
||||||
|
return requires(Ingredient.of(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ShapelessRecipeSpec build(RecipeProperties properties) {
|
||||||
|
return new ShapelessRecipeSpec(properties, ingredients, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,11 @@ import dan200.computercraft.api.media.MediaProvider;
|
|||||||
import dan200.computercraft.api.network.PacketNetwork;
|
import dan200.computercraft.api.network.PacketNetwork;
|
||||||
import dan200.computercraft.api.network.wired.WiredElement;
|
import dan200.computercraft.api.network.wired.WiredElement;
|
||||||
import dan200.computercraft.api.network.wired.WiredNode;
|
import dan200.computercraft.api.network.wired.WiredNode;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.core.filesystem.WritableFileMount;
|
import dan200.computercraft.core.filesystem.WritableFileMount;
|
||||||
import dan200.computercraft.impl.detail.DetailRegistryImpl;
|
import dan200.computercraft.impl.detail.DetailRegistryImpl;
|
||||||
import dan200.computercraft.impl.network.wired.WiredNodeImpl;
|
import dan200.computercraft.impl.network.wired.WiredNodeImpl;
|
||||||
@@ -44,8 +45,8 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic
|
|||||||
private final DetailRegistry<ItemStack> itemStackDetails = new DetailRegistryImpl<>(ItemDetails::fillBasic);
|
private final DetailRegistry<ItemStack> itemStackDetails = new DetailRegistryImpl<>(ItemDetails::fillBasic);
|
||||||
private final DetailRegistry<BlockReference> blockDetails = new DetailRegistryImpl<>(BlockDetails::fillBasic);
|
private final DetailRegistry<BlockReference> blockDetails = new DetailRegistryImpl<>(BlockDetails::fillBasic);
|
||||||
|
|
||||||
protected static final ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
protected static final ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
||||||
protected static final ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
protected static final ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
||||||
|
|
||||||
public static @Nullable InputStream getResourceFile(MinecraftServer server, String domain, String subPath) {
|
public static @Nullable InputStream getResourceFile(MinecraftServer server, String domain, String subPath) {
|
||||||
var manager = server.getResourceManager();
|
var manager = server.getResourceManager();
|
||||||
@@ -116,12 +117,12 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId() {
|
public final ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> turtleUpgradeRegistryId() {
|
||||||
return turtleUpgradeRegistryId;
|
return turtleUpgradeRegistryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId() {
|
public final ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> pocketUpgradeRegistryId() {
|
||||||
return pocketUpgradeRegistryId;
|
return pocketUpgradeRegistryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
|
||||||
|
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registry for peripheral providers.
|
||||||
|
*/
|
||||||
|
public final class Peripherals {
|
||||||
|
private static final GenericPeripheralProvider genericProvider = new GenericPeripheralProvider();
|
||||||
|
|
||||||
|
private Peripherals() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addGenericLookup(ComponentLookup lookup) {
|
||||||
|
genericProvider.registerLookup(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable IPeripheral getGenericPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) {
|
||||||
|
return genericProvider.getPeripheral(level, pos, side, blockEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,19 +6,18 @@ package dan200.computercraft.impl;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public final class PocketUpgrades {
|
public final class PocketUpgrades {
|
||||||
private static final UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> registry = new UpgradeManager<>(
|
private static final UpgradeManager<IPocketUpgrade> registry = new UpgradeManager<>(
|
||||||
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId()
|
"pocket computer upgrade", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey()
|
||||||
);
|
);
|
||||||
|
|
||||||
private PocketUpgrades() {
|
private PocketUpgrades() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> instance() {
|
public static UpgradeManager<IPocketUpgrade> instance() {
|
||||||
return registry;
|
return registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ package dan200.computercraft.impl;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public final class TurtleUpgrades {
|
public final class TurtleUpgrades {
|
||||||
private static final UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> registry = new UpgradeManager<>(
|
private static final UpgradeManager<ITurtleUpgrade> registry = new UpgradeManager<>(
|
||||||
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId()
|
"turtle upgrade", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey()
|
||||||
);
|
);
|
||||||
|
|
||||||
private TurtleUpgrades() {
|
private TurtleUpgrades() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> instance() {
|
public static UpgradeManager<ITurtleUpgrade> instance() {
|
||||||
return registry;
|
return registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user