mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-04 23:53:01 +00:00
Compare commits
114 Commits
v1.20.1-1.
...
v1.20.4-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22bd5309ba | ||
|
|
ad49325376 | ||
|
|
825d45eb26 | ||
|
|
8b2516abb5 | ||
|
|
bce099ef32 | ||
|
|
6d14ce625f | ||
|
|
c8eadf4011 | ||
|
|
0c1ab780bb | ||
|
|
0f623c2cca | ||
|
|
b9ba2534a4 | ||
|
|
c764981a40 | ||
|
|
6363164f2b | ||
|
|
63580b4acb | ||
|
|
9af1aa1ecf | ||
|
|
ad0f551204 | ||
|
|
0d3e00cc41 | ||
|
|
836d6b939e | ||
|
|
0e5248e5e6 | ||
|
|
e154b0db2a | ||
|
|
ae767eb5be | ||
|
|
c50d56d9fa | ||
|
|
777aa34bb0 | ||
|
|
286f969f94 | ||
|
|
7b9a156abc | ||
|
|
0a9e5c78f3 | ||
|
|
da5885ef35 | ||
|
|
57c72711bb | ||
|
|
cbafbca86b | ||
|
|
c9caffb10f | ||
|
|
4675583e1c | ||
|
|
afe16cc593 | ||
|
|
0abd107348 | ||
|
|
cef4b4906b | ||
|
|
04900dc82f | ||
|
|
9b63cc81b1 | ||
|
|
9eead7a0ec | ||
|
|
ad97b2922b | ||
|
|
52986f8d73 | ||
|
|
ab00580389 | ||
|
|
128ac2f109 | ||
|
|
5d8c46c7e6 | ||
|
|
1a5dc92bd4 | ||
|
|
98b2d3f310 | ||
|
|
e92c2d02f8 | ||
|
|
f8ef40d378 | ||
|
|
61f9b1d0c6 | ||
|
|
ffb62dfa02 | ||
|
|
6fb291112d | ||
|
|
7ee821e9c9 | ||
|
|
b7df91349a | ||
|
|
cb8e06af2a | ||
|
|
6478fca7a2 | ||
|
|
3493159a05 | ||
|
|
eead67e314 | ||
|
|
3b8813cf8f | ||
|
|
a9191a4d4e | ||
|
|
451a2593ce | ||
|
|
d38b1da974 | ||
|
|
6e374579a4 | ||
|
|
4daa2a2b6a | ||
|
|
84b6edab82 | ||
|
|
31aaf46d09 | ||
|
|
2d11b51c62 | ||
|
|
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 |
12
.github/workflows/main-ci.yml
vendored
12
.github/workflows/main-ci.yml
vendored
@@ -9,16 +9,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 📥 Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: 📥 Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
@@ -82,16 +82,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Java
|
- name: Set up Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/make-doc.yml
vendored
6
.github/workflows/make-doc.yml
vendored
@@ -13,16 +13,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Java
|
- name: Set up Java
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,7 +7,9 @@
|
|||||||
/logs
|
/logs
|
||||||
/build
|
/build
|
||||||
/projects/*/logs
|
/projects/*/logs
|
||||||
|
/projects/fabric/fabricloader.log
|
||||||
/projects/*/build
|
/projects/*/build
|
||||||
|
/projects/*/src/test/generated_tests/
|
||||||
/buildSrc/build
|
/buildSrc/build
|
||||||
/out
|
/out
|
||||||
/buildSrc/out
|
/buildSrc/out
|
||||||
|
|||||||
@@ -51,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")
|
||||||
@@ -75,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/).
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ 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")
|
||||||
}
|
}
|
||||||
@@ -102,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,12 +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.ideaExt)
|
||||||
implementation(libs.librarian)
|
|
||||||
implementation(libs.minotaur)
|
implementation(libs.minotaur)
|
||||||
implementation(libs.vanillaGradle)
|
implementation(libs.neoGradle.userdev)
|
||||||
implementation(libs.vineflower)
|
implementation(libs.vanillaExtract)
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
@@ -75,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,21 +40,10 @@ 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")
|
||||||
// Things we mirror
|
// Things we mirror
|
||||||
@@ -76,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())
|
||||||
}
|
}
|
||||||
@@ -107,6 +102,8 @@ sourceSets.all {
|
|||||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||||
option("NullAway:CheckOptionalEmptiness")
|
option("NullAway:CheckOptionalEmptiness")
|
||||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||||
|
|
||||||
|
excludedPaths = ".*/jmh_generated/.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,6 +198,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",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
|||||||
apiToken = findProperty("curseForgeApiKey") ?: ""
|
apiToken = findProperty("curseForgeApiKey") ?: ""
|
||||||
enabled = apiToken != ""
|
enabled = apiToken != ""
|
||||||
|
|
||||||
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
|
val mainFile = upload("282001", modPublishing.output)
|
||||||
mainFile.changelog =
|
mainFile.changelog =
|
||||||
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||||
mainFile.changelogType = "markdown"
|
mainFile.changelogType = "markdown"
|
||||||
|
|||||||
@@ -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() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -65,14 +64,6 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
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))
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,12 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<property name="tabWidth" value="4"/>
|
<property name="tabWidth" value="4"/>
|
||||||
<property name="charset" value="UTF-8" />
|
<property name="charset" value="UTF-8" />
|
||||||
|
|
||||||
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
|
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
<module name="SuppressionFilter">
|
<module name="SuppressionFilter">
|
||||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
<module name="BeforeExecutionExclusionFileFilter">
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
||||||
|
|
||||||
<!-- Allow underscores in our test classes. -->
|
<!-- Allow underscores in our test classes. -->
|
||||||
<suppress checks="MethodName" files=".*Contract.java" />
|
<suppress checks="MethodName" files=".*(Contract|Test).java" />
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ In order to give the best results, a GPS constellation needs at least four compu
|
|||||||
constellation is redundant, but it does not cause problems.
|
constellation is redundant, but it does not cause problems.
|
||||||
|
|
||||||
## Building a GPS constellation
|
## Building a GPS constellation
|
||||||
<img alt="An example GPS constellation." src="/images/gps-constellation-example.png" class="big-image" />
|
<img alt="An example GPS constellation." src="../images/gps-constellation-example.png" class="big-image" />
|
||||||
|
|
||||||
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
||||||
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||||
|
|||||||
BIN
doc/images/computercraft-dump.png
Normal file
BIN
doc/images/computercraft-dump.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 254 KiB |
BIN
doc/images/computercraft-track.png
Normal file
BIN
doc/images/computercraft-track.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 304 KiB |
@@ -21,7 +21,7 @@ of the mod should run fine on later versions.
|
|||||||
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
|
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.
|
as documentation for breaking changes and "gotchas" one should look out for between versions.
|
||||||
|
|
||||||
## CC: Tweaked 1.109.0 {#cct-1.109}
|
## CC: Tweaked 1.109.0 to 1.109.3 {#cct-1.109}
|
||||||
|
|
||||||
- Update to Lua 5.2:
|
- Update to Lua 5.2:
|
||||||
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
|
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
|
||||||
@@ -31,6 +31,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
|
|||||||
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
|
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
|
||||||
environment.
|
environment.
|
||||||
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
|
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
|
||||||
|
- `math.random` now uses Lua 5.4's random number generator.
|
||||||
|
|
||||||
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
|
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
|
||||||
|
|
||||||
@@ -75,6 +76,6 @@ as documentation for breaking changes and "gotchas" one should look out for betw
|
|||||||
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
||||||
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
||||||
|
|
||||||
[flattening]: https://minecraft.wiki.com/w/Java_Edition_1.13/Flattening
|
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
|
||||||
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
||||||
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
||||||
|
|||||||
140
doc/reference/command.md
Normal file
140
doc/reference/command.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
---
|
||||||
|
module: [kind=reference] computercraft_command
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# The `/computercraft` command
|
||||||
|
CC: Tweaked provides a `/computercraft` command for server owners to manage running computers on a server.
|
||||||
|
|
||||||
|
## Permissions {#permissions}
|
||||||
|
As the `/computercraft` command is mostly intended for debugging and administrative purposes, its sub-commands typically
|
||||||
|
require you to have op (or similar).
|
||||||
|
|
||||||
|
- All players have access to the [`queue`] sub-command.
|
||||||
|
- On a multi-player server, all other commands require op.
|
||||||
|
- On a single-player world, the player can run the [`dump`], [`turn-on`]/[`shutdown`], and [`track`] sub-commands, even
|
||||||
|
when cheats are not enabled. The [`tp`] and [`view`] commands require cheats.
|
||||||
|
|
||||||
|
If a permission mod such as [LuckPerms] is installed[^permission], you can configure access to the individual
|
||||||
|
sub-commands. Each sub-command creates a `computercraft.command.NAME` permission node to control which players can
|
||||||
|
execute it.
|
||||||
|
|
||||||
|
[LuckPerms]: https://github.com/LuckPerms/LuckPerms/ "A permissions plugin for Minecraft servers."
|
||||||
|
[fabric-permission-api]: https://github.com/lucko/fabric-permissions-api "A simple permissions API for Fabric"
|
||||||
|
|
||||||
|
[^permission]: This supports any mod which uses Forge's permission API or [fabric-permission-api].
|
||||||
|
|
||||||
|
## Computer selectors {#computer-selectors}
|
||||||
|
Some commands (such as [`tp`] or [`turn-on`]) target a specific computer, or a list of computers. To specify which
|
||||||
|
computers to operate on, you must use "computer selectors".
|
||||||
|
|
||||||
|
Computer selectors are similar to Minecraft's [entity target selectors], but targeting computers instead. They allow
|
||||||
|
you to select one or more computers, based on a set of predicates.
|
||||||
|
|
||||||
|
The following predicates are supported:
|
||||||
|
- `id=<id>`: Select computer(s) with a specific id.
|
||||||
|
- `instance=<id>`: Select the computer with the given instance id.
|
||||||
|
- `family=<normal|advanced|command>`: Select computers based on their type.
|
||||||
|
- `label=<label>`: Select computers with the given label.
|
||||||
|
- `distance=<distance>`: Select computers within a specific distance of the player executing the command. This uses
|
||||||
|
Minecraft's [float range] syntax.
|
||||||
|
|
||||||
|
`#<id>` may also be used as a shorthand for `@c[id=<id>]`, to select computer(s) with a specific id.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
- `/computercraft turn-on #12`: Turn on the computer(s) with an id of 12.
|
||||||
|
- `/computercraft shutdown @c[distance=..100]`: Shut down all computers with 100 blocks of the player.
|
||||||
|
|
||||||
|
[entity target selectors]: https://minecraft.wiki/w/Target_selectors "Target Selectors on the Minecraft wiki"
|
||||||
|
[Float range]: https://minecraft.wiki/w/Argument_types#minecraft:float_range
|
||||||
|
|
||||||
|
## Commands {#commands}
|
||||||
|
### `/computercraft dump` {#dump}
|
||||||
|
`/computercraft dump` prints a table of currently loaded computers, including their id, position, and whether they're
|
||||||
|
running. It can also be run with a single computer argument to dump more detailed information about a computer.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Next to the computer id, there are several buttons to either [teleport][`tp`] to the computer, or [open its terminal
|
||||||
|
][`view`].
|
||||||
|
|
||||||
|
Computers are sorted by distance to the player, so nearby computers will appear earlier.
|
||||||
|
|
||||||
|
### `/computercraft turn-on [computers...]` {#turn-on}
|
||||||
|
Turn on one or more computers or, if no run with no arguments, all loaded computers.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
- `/computercraft turn-on #0 #2`: Turn on computers with id 0 and 2.
|
||||||
|
- `/computercraft turn-on @c[family=command]`: Turn on all command computers.
|
||||||
|
|
||||||
|
### `/computercraft shutdown [computers...]` {#shutdown}
|
||||||
|
Shutdown one or more computers or, if no run with no arguments, all loaded computers.
|
||||||
|
|
||||||
|
This is sometimes useful when dealing with lag, as a way to ensure that ComputerCraft is not causing problems.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
- `/computercraft shutdown`: Shut down all loaded computers.
|
||||||
|
- `/computercraft shutdown @c[distance=..10]`: Shut down all computers in a block radius.
|
||||||
|
|
||||||
|
### `/computercraft tp [computer]` {#tp}
|
||||||
|
Teleport to the given computer.
|
||||||
|
|
||||||
|
This is normally used from via the [`dump`] command interface rather than being invoked directly.
|
||||||
|
|
||||||
|
### `/computercraft view [computer]` {#view}
|
||||||
|
Open a terminal for the specified computer. This allows remotely viewing computers without having to interact with the
|
||||||
|
block.
|
||||||
|
|
||||||
|
This is normally used from via the [`dump`] command interface rather than being invoked directly.
|
||||||
|
|
||||||
|
### `/computercraft track` {#track}
|
||||||
|
The `/computercraft track` command allows you to enable profiling of computers. When a computer runs code, or interacts
|
||||||
|
with the Minecraft world, we time how long that takes. This timing information may then be queried, and used to find
|
||||||
|
computers which may be causing lag.
|
||||||
|
|
||||||
|
To enable the profiler, run `/computercraft track start`. Computers will then start recording metrics. Once enough data
|
||||||
|
has been gathered, run `/computercraft track stop` to stop profiling and display the recorded data.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The table by default shows the number of times each computer has run, and how long it ran for (in total, and on
|
||||||
|
average). In the above screenshot, we can see one computer was particularly badly behaved, and ran for 7 seconds. The
|
||||||
|
buttons may be used to [teleport][`tp`] to the computer, or [open its terminal ][`view`], and inspect it further.
|
||||||
|
|
||||||
|
`/computercraft track dump` can be used to display this table at any point (including while profiling is still running).
|
||||||
|
|
||||||
|
Computers also record other information, such as how much server-thread time they consume, or their HTTP bandwidth
|
||||||
|
usage. The `dump` subcommand accepts a list of other fields to display, instead of the default timings.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
- `/computercraft track dump server_tasks_count server_tasks`: Print the number of server-thread tasks each computer
|
||||||
|
executed, and how long they took in total.
|
||||||
|
- `/computercraft track dump http_upload http_download`: Print the number of bytes uploaded and downloaded by each
|
||||||
|
computer.
|
||||||
|
|
||||||
|
|
||||||
|
### `/computercraft queue` {#queue}
|
||||||
|
The queue subcommand allows non-operator players to queue a `computer_command` event on *command* computers.
|
||||||
|
|
||||||
|
This has a similar purpose to vanilla's [`/trigger`] command. Command computers may choose to listen to this event, and
|
||||||
|
then perform some action.
|
||||||
|
|
||||||
|
[`/trigger`]: https://minecraft.wiki/w/Commands/trigger "/trigger on the Minecraft wiki"
|
||||||
|
|
||||||
|
|
||||||
|
[`dump`]: #dump "/computercraft dump"
|
||||||
|
[`queue`]: #queue "/computercraft queue"
|
||||||
|
[`shutdown`]: #shutdown "/computercraft shutdown"
|
||||||
|
[`tp`]: #tp "/computercraft tp"
|
||||||
|
[`track`]: #track "/computercraft track"
|
||||||
|
[`turn-on`]: #turn-on "/computercraft turn-on"
|
||||||
|
[`view`]: #view "/computercraft view"
|
||||||
|
[computer selectors]: #computer-selectors "Computer selectors"
|
||||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
|||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=true
|
isUnstable=true
|
||||||
modVersion=1.109.1
|
modVersion=1.110.2
|
||||||
|
|
||||||
# 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,70 +7,72 @@
|
|||||||
# 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.210"
|
||||||
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.8.0"
|
cobalt = "0.9.2"
|
||||||
cobalt-next = "0.8.1" # Not a real version, used to constrain the version we accept.
|
commonsCli = "1.6.0"
|
||||||
commonsCli = "1.3.1"
|
jetbrainsAnnotations = "24.1.0"
|
||||||
fastutil = "8.5.9"
|
|
||||||
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
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
jqwik = "1.7.4"
|
jqwik = "1.8.2"
|
||||||
junit = "5.10.0"
|
junit = "5.10.1"
|
||||||
|
jmh = "1.37"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.8.2"
|
cctJavadoc = "1.8.2"
|
||||||
checkstyle = "10.12.3"
|
checkstyle = "10.14.1"
|
||||||
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-71-g378d86e"
|
||||||
librarian = "1.+"
|
lwjgl = "3.3.3"
|
||||||
lwjgl = "3.3.1"
|
minotaur = "2.8.7"
|
||||||
minotaur = "2.+"
|
neoGradle = "7.0.100"
|
||||||
mixinGradle = "0.7.+"
|
nullAway = "0.10.25"
|
||||||
nullAway = "0.9.9"
|
spotless = "6.23.3"
|
||||||
spotless = "6.21.0"
|
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
teavm = "0.10.0-SQUID.2"
|
teavm = "0.10.0-SQUID.3"
|
||||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
vanillaExtract = "0.1.2"
|
||||||
vineflower = "1.11.0"
|
versionCatalogUpdate = "0.8.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Normal dependencies
|
# Normal dependencies
|
||||||
@@ -81,7 +83,7 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
|
|||||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
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" }
|
||||||
@@ -103,9 +105,9 @@ fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fab
|
|||||||
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" }
|
||||||
@@ -124,6 +126,8 @@ junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.re
|
|||||||
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||||
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" }
|
||||||
|
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
|
||||||
|
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
|
||||||
|
|
||||||
# LWJGL
|
# LWJGL
|
||||||
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
||||||
@@ -141,11 +145,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" }
|
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" }
|
||||||
@@ -156,16 +159,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" }
|
||||||
|
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"]
|
||||||
@@ -174,8 +176,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"]
|
||||||
|
|
||||||
@@ -184,5 +185,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.6-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" \
|
||||||
|
|||||||
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|||||||
@@ -105,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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2018 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.network.wired;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wired network is composed of one of more {@link WiredNode}s, a set of connections between them, and a series
|
|
||||||
* of peripherals.
|
|
||||||
* <p>
|
|
||||||
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
|
|
||||||
* there is some path between two nodes then they must be on the same network. {@link WiredNetwork} will automatically
|
|
||||||
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
|
|
||||||
* change.
|
|
||||||
* <p>
|
|
||||||
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
|
|
||||||
* it is generally preferred to use the methods provided by {@link WiredNode}.
|
|
||||||
*
|
|
||||||
* @see WiredNode#getNetwork()
|
|
||||||
*/
|
|
||||||
public interface WiredNetwork {
|
|
||||||
/**
|
|
||||||
* Create a connection between two nodes.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread.
|
|
||||||
*
|
|
||||||
* @param left The first node to connect
|
|
||||||
* @param right The second node to connect
|
|
||||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
|
||||||
* @throws IllegalStateException If neither node is on the network.
|
|
||||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
|
||||||
* @see WiredNode#connectTo(WiredNode)
|
|
||||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
|
||||||
*/
|
|
||||||
boolean connect(WiredNode left, WiredNode right);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a connection between this node and another.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread.
|
|
||||||
*
|
|
||||||
* @param left The first node in the connection.
|
|
||||||
* @param right The second node in the connection.
|
|
||||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
|
||||||
* @throws IllegalArgumentException If either node is not on the network.
|
|
||||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
|
||||||
* @see WiredNode#disconnectFrom(WiredNode)
|
|
||||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
|
||||||
*/
|
|
||||||
boolean disconnect(WiredNode left, WiredNode right);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sever all connections this node has, removing it from this network.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread. You should only call this on nodes
|
|
||||||
* that your network element owns.
|
|
||||||
*
|
|
||||||
* @param node The node to remove
|
|
||||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
|
||||||
* only element.
|
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
|
||||||
* @see WiredNode#remove()
|
|
||||||
*/
|
|
||||||
boolean remove(WiredNode node);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the peripherals a node provides.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread. You should only call this on nodes
|
|
||||||
* that your network element owns.
|
|
||||||
*
|
|
||||||
* @param node The node to attach peripherals for.
|
|
||||||
* @param peripherals The new peripherals for this node.
|
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
|
||||||
* @see WiredNode#updatePeripherals(Map)
|
|
||||||
*/
|
|
||||||
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,12 @@ package dan200.computercraft.api.network.wired;
|
|||||||
|
|
||||||
import dan200.computercraft.api.network.PacketNetwork;
|
import dan200.computercraft.api.network.PacketNetwork;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wired nodes act as a layer between {@link WiredElement}s and {@link WiredNetwork}s.
|
* A single node on a wired network.
|
||||||
* <p>
|
* <p>
|
||||||
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
||||||
* methods may be safely used on any thread.
|
* methods may be safely used on any thread.
|
||||||
@@ -22,6 +23,7 @@ import java.util.Map;
|
|||||||
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
||||||
* be used on the main server thread.
|
* be used on the main server thread.
|
||||||
*/
|
*/
|
||||||
|
@ApiStatus.NonExtendable
|
||||||
public interface WiredNode extends PacketNetwork {
|
public interface WiredNode extends PacketNetwork {
|
||||||
/**
|
/**
|
||||||
* The associated element for this network node.
|
* The associated element for this network node.
|
||||||
@@ -30,16 +32,6 @@ public interface WiredNode extends PacketNetwork {
|
|||||||
*/
|
*/
|
||||||
WiredElement getElement();
|
WiredElement getElement();
|
||||||
|
|
||||||
/**
|
|
||||||
* The network this node is currently connected to. Note that this may change
|
|
||||||
* after any network operation, so it should not be cached.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread.
|
|
||||||
*
|
|
||||||
* @return This node's network.
|
|
||||||
*/
|
|
||||||
WiredNetwork getNetwork();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a connection from this node to another.
|
* Create a connection from this node to another.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -47,12 +39,9 @@ public interface WiredNode extends PacketNetwork {
|
|||||||
*
|
*
|
||||||
* @param node The other node to connect to.
|
* @param node The other node to connect to.
|
||||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
|
||||||
* @see WiredNode#disconnectFrom(WiredNode)
|
* @see WiredNode#disconnectFrom(WiredNode)
|
||||||
*/
|
*/
|
||||||
default boolean connectTo(WiredNode node) {
|
boolean connectTo(WiredNode node);
|
||||||
return getNetwork().connect(this, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a connection between this node and another.
|
* Destroy a connection between this node and another.
|
||||||
@@ -61,13 +50,9 @@ public interface WiredNode extends PacketNetwork {
|
|||||||
*
|
*
|
||||||
* @param node The other node to disconnect from.
|
* @param node The other node to disconnect from.
|
||||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
||||||
* @throws IllegalArgumentException If {@code node} is not on the same network.
|
|
||||||
* @see WiredNetwork#disconnect(WiredNode, WiredNode)
|
|
||||||
* @see WiredNode#connectTo(WiredNode)
|
* @see WiredNode#connectTo(WiredNode)
|
||||||
*/
|
*/
|
||||||
default boolean disconnectFrom(WiredNode node) {
|
boolean disconnectFrom(WiredNode node);
|
||||||
return getNetwork().disconnect(this, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sever all connections this node has, removing it from this network.
|
* Sever all connections this node has, removing it from this network.
|
||||||
@@ -78,11 +63,8 @@ public interface WiredNode extends PacketNetwork {
|
|||||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||||
* only element.
|
* only element.
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
* @throws IllegalArgumentException If the node is not in the network.
|
||||||
* @see WiredNetwork#remove(WiredNode)
|
|
||||||
*/
|
*/
|
||||||
default boolean remove() {
|
boolean remove();
|
||||||
return getNetwork().remove(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark this node's peripherals as having changed.
|
* Mark this node's peripherals as having changed.
|
||||||
@@ -91,9 +73,6 @@ public interface WiredNode extends PacketNetwork {
|
|||||||
* that your network element owns.
|
* that your network element owns.
|
||||||
*
|
*
|
||||||
* @param peripherals The new peripherals for this node.
|
* @param peripherals The new peripherals for this node.
|
||||||
* @see WiredNetwork#updatePeripherals(WiredNode, Map)
|
|
||||||
*/
|
*/
|
||||||
default void updatePeripherals(Map<String, IPeripheral> peripherals) {
|
void updatePeripherals(Map<String, IPeripheral> peripherals);
|
||||||
getNetwork().updatePeripherals(this, peripherals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import dan200.computercraft.api.network.PacketSender;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object on a {@link WiredNetwork} capable of sending packets.
|
* An object on a wired network capable of sending packets.
|
||||||
* <p>
|
* <p>
|
||||||
* Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
|
* Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
|
||||||
* to send the packet from.
|
* to send the packet from.
|
||||||
|
*
|
||||||
|
* @see WiredElement
|
||||||
*/
|
*/
|
||||||
public interface WiredSender extends PacketSender {
|
public interface WiredSender extends PacketSender {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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,19 +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.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -31,31 +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 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.");
|
||||||
}
|
}
|
||||||
@@ -65,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())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,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) {
|
||||||
@@ -107,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")));
|
||||||
@@ -129,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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ public final class Services {
|
|||||||
* @throws IllegalStateException When the service cannot be loaded.
|
* @throws IllegalStateException When the service cannot be loaded.
|
||||||
*/
|
*/
|
||||||
public static <T> T load(Class<T> klass) {
|
public static <T> T load(Class<T> klass) {
|
||||||
var services = ServiceLoader.load(klass).stream().toList();
|
var services = ServiceLoader.load(klass, klass.getClassLoader()).stream().toList();
|
||||||
return switch (services.size()) {
|
return switch (services.size()) {
|
||||||
case 1 -> services.get(0).get();
|
case 1 -> services.get(0).get();
|
||||||
case 0 -> throw new IllegalStateException("Cannot find service for " + klass.getName());
|
case 0 -> throw new IllegalStateException("Cannot find service for " + klass.getName());
|
||||||
|
|||||||
@@ -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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,24 @@ configurations {
|
|||||||
register("cctJavadoc")
|
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"))
|
||||||
implementation(commonClasses(project(":common-api")))
|
implementation(commonClasses(project(":common-api")))
|
||||||
clientImplementation(clientClasses(project(":common-api")))
|
clientImplementation(clientClasses(project(":common-api")))
|
||||||
|
|
||||||
|
compileOnly(libs.mixin)
|
||||||
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)
|
||||||
|
|
||||||
@@ -39,6 +47,10 @@ dependencies {
|
|||||||
testImplementation(libs.bundles.test)
|
testImplementation(libs.bundles.test)
|
||||||
testRuntimeOnly(libs.bundles.testRuntime)
|
testRuntimeOnly(libs.bundles.testRuntime)
|
||||||
|
|
||||||
|
testImplementation(libs.jmh)
|
||||||
|
testAnnotationProcessor(libs.jmh.processor)
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -72,7 +84,7 @@ val luaJavadoc by tasks.registering(Javadoc::class) {
|
|||||||
|
|
||||||
javadocTool.set(
|
javadocTool.set(
|
||||||
javaToolchains.javadocToolFor {
|
javaToolchains.javadocToolFor {
|
||||||
languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
|
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,39 @@ 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.ComputerState;
|
||||||
|
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 +74,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);
|
||||||
@@ -80,33 +82,66 @@ 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.
|
||||||
|
*
|
||||||
|
* @param itemProperties Callback to register item properties.
|
||||||
*/
|
*/
|
||||||
public static void registerMainThread() {
|
public static void registerMainThread(RegisterItemProperty itemProperties) {
|
||||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
registerItemProperty(itemProperties, "state",
|
||||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
new UnclampedPropertyFunction((stack, world, player, random) -> {
|
||||||
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
var computer = ClientPocketComputers.get(stack);
|
||||||
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
|
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
|
||||||
|
}),
|
||||||
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",
|
|
||||||
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
|
||||||
);
|
);
|
||||||
registerItemProperty("coloured",
|
registerItemProperty(itemProperties, "coloured",
|
||||||
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
||||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(RegisterItemProperty itemProperties, String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||||
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
for (var item : items) itemProperties.register(item.get(), id, getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an item property via {@link ItemProperties#register}. Forge and Fabric expose different methods, so we
|
||||||
|
* supply this via mod-loader-specific code.
|
||||||
|
*/
|
||||||
|
public interface RegisterItemProperty {
|
||||||
|
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
||||||
@@ -144,17 +179,14 @@ public final class ClientRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getPocketColour(ItemStack stack, int layer) {
|
private static int getPocketColour(ItemStack stack, int layer) {
|
||||||
switch (layer) {
|
return switch (layer) {
|
||||||
case 0:
|
default -> 0xFFFFFF;
|
||||||
default:
|
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
|
||||||
return 0xFFFFFF;
|
case 2 -> { // Light colour
|
||||||
case 1: // Frame colour
|
var computer = ClientPocketComputers.get(stack);
|
||||||
return IColouredItem.getColourBasic(stack);
|
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||||
case 2: { // Light colour
|
|
||||||
var light = ClientPocketComputers.get(stack).getLightState();
|
|
||||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTurtleColour(ItemStack stack, int layer) {
|
private static int getTurtleColour(ItemStack stack, int layer) {
|
||||||
@@ -179,4 +211,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,8 +7,9 @@ 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.core.util.Nullability;
|
||||||
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;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
@@ -18,6 +19,7 @@ import dan200.computercraft.shared.config.Config;
|
|||||||
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
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.Minecraft;
|
||||||
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.components.events.GuiEventListener;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
@@ -96,8 +98,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
getTerminal().update();
|
getTerminal().update();
|
||||||
|
|
||||||
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
|
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
|
||||||
new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
new ItemToast(minecraft(), displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
||||||
.showOrReplace(minecraft.getToasts());
|
.showOrReplace(minecraft().getToasts());
|
||||||
uploadNagDeadline = Long.MAX_VALUE;
|
uploadNagDeadline = Long.MAX_VALUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +127,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);
|
||||||
}
|
}
|
||||||
@@ -207,7 +208,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientPlatformHelper.get()::sendToServer);
|
if (!toUpload.isEmpty()) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void uploadResult(UploadResult result, @Nullable Component message) {
|
public void uploadResult(UploadResult result, @Nullable Component message) {
|
||||||
@@ -223,9 +224,13 @@ 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,
|
||||||
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
List.of(OptionScreen.newButton(OK, b -> minecraft().setScreen(this))),
|
||||||
() -> minecraft.setScreen(this)
|
() -> minecraft().setScreen(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Minecraft minecraft() {
|
||||||
|
return Nullability.assertNonNull(minecraft);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ package dan200.computercraft.client.gui;
|
|||||||
|
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
import dan200.computercraft.core.util.Nullability;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||||
@@ -16,6 +18,7 @@ import net.minecraft.world.entity.player.Inventory;
|
|||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||||
|
|
||||||
@@ -44,8 +47,8 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
|||||||
protected void init() {
|
protected void init() {
|
||||||
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
||||||
// grabbing unsets.
|
// grabbing unsets.
|
||||||
minecraft.mouseHandler.grabMouse();
|
minecraft().mouseHandler.grabMouse();
|
||||||
minecraft.screen = this;
|
minecraft().screen = this;
|
||||||
KeyMapping.releaseAll();
|
KeyMapping.releaseAll();
|
||||||
|
|
||||||
super.init();
|
super.init();
|
||||||
@@ -63,14 +66,14 @@ 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);
|
Objects.requireNonNull(minecraft().player).getInventory().swapPaint(scrollY);
|
||||||
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
|
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose() {
|
public void onClose() {
|
||||||
minecraft.player.closeContainer();
|
Objects.requireNonNull(minecraft().player).closeContainer();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,12 +96,16 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
|||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
|
|
||||||
var font = minecraft.font;
|
var font = minecraft().font;
|
||||||
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
|
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
|
||||||
var y = 10;
|
var y = 10;
|
||||||
for (var line : lines) {
|
for (var line : lines) {
|
||||||
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
|
graphics.drawString(font, line, (width / 2) - (font.width(line) / 2), y, 0xFFFFFF, true);
|
||||||
y += 9;
|
y += 9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Minecraft minecraft() {
|
||||||
|
return Nullability.assertNonNull(minecraft);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 (deltaY < 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 (deltaY > 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(
|
||||||
|
|||||||
@@ -195,16 +195,16 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
|
public boolean mouseScrolled(double mouseX, double mouseY, double deltaX, double deltaY) {
|
||||||
if (!inTermRegion(mouseX, mouseY)) return false;
|
if (!inTermRegion(mouseX, mouseY)) return false;
|
||||||
if (!hasMouseSupport() || delta == 0) return false;
|
if (!hasMouseSupport() || deltaY == 0) return false;
|
||||||
|
|
||||||
var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
|
var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
|
||||||
var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
|
var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
|
||||||
charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
|
charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
|
||||||
charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
|
charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
|
||||||
|
|
||||||
computer.mouseScroll(delta < 0 ? 1 : -1, charX + 1, charY + 1);
|
computer.mouseScroll(deltaY < 0 ? 1 : -1, charX + 1, charY + 1);
|
||||||
|
|
||||||
lastMouseX = charX;
|
lastMouseX = charX;
|
||||||
lastMouseY = charY;
|
lastMouseY = charY;
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -16,12 +17,13 @@ import dan200.computercraft.shared.computer.terminal.TerminalState;
|
|||||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
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.EncodedAudio;
|
||||||
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;
|
||||||
|
|
||||||
@@ -29,18 +31,17 @@ import javax.annotation.Nullable;
|
|||||||
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,44 @@ 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 computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
var mc = Minecraft.getInstance();
|
||||||
computer.setState(state, lightState);
|
ClientPlatformHelper.get().playStreamingMusic(pos, sound);
|
||||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
if (name != null) mc.gui.setNowPlaying(Component.literal(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handlePocketComputerDeleted(int instanceId) {
|
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||||
|
ClientPocketComputers.setState(instanceId, state, lightState, terminal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePocketComputerDeleted(UUID 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, EncodedAudio 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,21 +4,25 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.pocket;
|
package dan200.computercraft.client.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}.
|
* Maps {@link ServerComputer#getInstanceUUID()} to locals {@link PocketComputerData}.
|
||||||
* <p>
|
* <p>
|
||||||
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
||||||
*/
|
*/
|
||||||
public final class ClientPocketComputers {
|
public final class ClientPocketComputers {
|
||||||
private static final Int2ObjectMap<PocketComputerData> instances = new Int2ObjectOpenHashMap<>();
|
private static final Map<UUID, PocketComputerData> instances = new HashMap<>();
|
||||||
|
|
||||||
private ClientPocketComputers() {
|
private ClientPocketComputers() {
|
||||||
}
|
}
|
||||||
@@ -27,25 +31,29 @@ public final class ClientPocketComputers {
|
|||||||
instances.clear();
|
instances.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void remove(int id) {
|
public static void remove(UUID id) {
|
||||||
instances.remove(id);
|
instances.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or create a pocket computer.
|
* Set the state of a pocket computer.
|
||||||
*
|
*
|
||||||
* @param instanceId The instance ID of the pocket computer.
|
* @param instanceId The instance ID of the pocket computer.
|
||||||
* @param advanced Whether this computer has an advanced terminal.
|
* @param state The computer state of the pocket computer.
|
||||||
* @return The pocket computer data.
|
* @param lightColour The current colour of the modem light.
|
||||||
|
* @param terminalData The current terminal contents.
|
||||||
*/
|
*/
|
||||||
public static PocketComputerData get(int instanceId, boolean advanced) {
|
public static void setState(UUID instanceId, ComputerState state, int lightColour, TerminalState terminalData) {
|
||||||
var computer = instances.get(instanceId);
|
var computer = instances.get(instanceId);
|
||||||
if (computer == null) instances.put(instanceId, computer = new PocketComputerData(advanced));
|
if (computer == null) {
|
||||||
return computer;
|
instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData));
|
||||||
|
} else {
|
||||||
|
computer.setState(state, lightColour, terminalData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PocketComputerData get(ItemStack stack) {
|
public static @Nullable PocketComputerData get(ItemStack stack) {
|
||||||
var family = stack.getItem() instanceof PocketComputerItem computer ? computer.getFamily() : ComputerFamily.NORMAL;
|
var id = PocketComputerItem.getInstanceID(stack);
|
||||||
return get(PocketComputerItem.getInstanceID(stack), family != ComputerFamily.NORMAL);
|
return id == null ? null : instances.get(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.pocket;
|
package dan200.computercraft.client.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.config.Config;
|
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clientside data about a pocket computer.
|
* Clientside data about a pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -21,20 +21,22 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
|||||||
* @see ClientPocketComputers The registry which holds pocket computers.
|
* @see ClientPocketComputers The registry which holds pocket computers.
|
||||||
* @see PocketServerComputer The server-side pocket computer.
|
* @see PocketServerComputer The server-side pocket computer.
|
||||||
*/
|
*/
|
||||||
public class PocketComputerData {
|
public final class PocketComputerData {
|
||||||
private final NetworkedTerminal terminal;
|
private @Nullable NetworkedTerminal terminal;
|
||||||
private ComputerState state = ComputerState.OFF;
|
private ComputerState state;
|
||||||
private int lightColour = -1;
|
private int lightColour;
|
||||||
|
|
||||||
public PocketComputerData(boolean colour) {
|
PocketComputerData(ComputerState state, int lightColour, TerminalState terminalData) {
|
||||||
terminal = new NetworkedTerminal(Config.pocketTermWidth, Config.pocketTermHeight, colour);
|
this.state = state;
|
||||||
|
this.lightColour = lightColour;
|
||||||
|
if (terminalData.hasTerminal()) terminal = terminalData.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLightState() {
|
public int getLightState() {
|
||||||
return state != ComputerState.OFF ? lightColour : -1;
|
return state != ComputerState.OFF ? lightColour : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Terminal getTerminal() {
|
public @Nullable NetworkedTerminal getTerminal() {
|
||||||
return terminal;
|
return terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +44,16 @@ public class PocketComputerData {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setState(ComputerState state, int lightColour) {
|
void setState(ComputerState state, int lightColour, TerminalState terminalData) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.lightColour = lightColour;
|
this.lightColour = lightColour;
|
||||||
}
|
|
||||||
|
|
||||||
public void setTerminal(TerminalState state) {
|
if (terminalData.hasTerminal()) {
|
||||||
state.apply(terminal);
|
if (terminal == null) {
|
||||||
|
terminal = terminalData.create();
|
||||||
|
} else {
|
||||||
|
terminalData.apply(terminal);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import net.minecraft.world.entity.player.Player;
|
|||||||
import net.minecraft.world.item.ItemDisplayContext;
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for items which have map-like rendering when held in the hand.
|
* A base class for items which have map-like rendering when held in the hand.
|
||||||
*
|
*
|
||||||
@@ -35,7 +37,7 @@ public abstract class ItemMapLikeRenderer {
|
|||||||
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
|
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
|
||||||
|
|
||||||
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
|
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
|
||||||
Player player = Minecraft.getInstance().player;
|
Player player = Objects.requireNonNull(Minecraft.getInstance().player);
|
||||||
|
|
||||||
transform.pushPose();
|
transform.pushPose();
|
||||||
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {
|
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
|
|||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@@ -32,10 +33,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
@Override
|
@Override
|
||||||
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
||||||
var computer = ClientPocketComputers.get(stack);
|
var computer = ClientPocketComputers.get(stack);
|
||||||
var terminal = computer.getTerminal();
|
var terminal = computer == null ? null : computer.getTerminal();
|
||||||
|
|
||||||
var termWidth = terminal.getWidth();
|
int termWidth, termHeight;
|
||||||
var termHeight = terminal.getHeight();
|
if (terminal == null) {
|
||||||
|
termWidth = Config.pocketTermWidth;
|
||||||
|
termHeight = Config.pocketTermHeight;
|
||||||
|
} else {
|
||||||
|
termWidth = terminal.getWidth();
|
||||||
|
termHeight = terminal.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
||||||
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||||
@@ -60,14 +67,15 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
||||||
|
|
||||||
// Render the light
|
// Render the light
|
||||||
var lightColour = ClientPocketComputers.get(stack).getLightState();
|
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||||
if (lightColour == -1) lightColour = Colour.BLACK.getHex();
|
|
||||||
renderLight(transform, bufferSource, lightColour, width, height);
|
renderLight(transform, bufferSource, lightColour, width, height);
|
||||||
|
|
||||||
FixedWidthFontRenderer.drawTerminal(
|
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||||
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL)),
|
if (terminal == null) {
|
||||||
MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN
|
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
|
||||||
);
|
} else {
|
||||||
|
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
transform.popPose();
|
transform.popPose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -66,7 +55,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
|||||||
// Render the label
|
// Render the label
|
||||||
var label = turtle.getLabel();
|
var label = turtle.getLabel();
|
||||||
var hit = renderer.cameraHitResult;
|
var hit = renderer.cameraHitResult;
|
||||||
if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
|
if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
|
||||||
var mc = Minecraft.getInstance();
|
var mc = Minecraft.getInstance();
|
||||||
var font = this.font;
|
var font = this.font;
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -58,9 +60,9 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
|||||||
@Override
|
@Override
|
||||||
public void render(MonitorBlockEntity monitor, float partialTicks, PoseStack transform, MultiBufferSource bufferSource, int lightmapCoord, int overlayLight) {
|
public void render(MonitorBlockEntity monitor, float partialTicks, PoseStack transform, MultiBufferSource bufferSource, int lightmapCoord, int overlayLight) {
|
||||||
// Render from the origin monitor
|
// Render from the origin monitor
|
||||||
var originTerminal = monitor.getClientMonitor();
|
var originTerminal = monitor.getOriginClientMonitor();
|
||||||
|
|
||||||
if (originTerminal == null) return;
|
if (originTerminal == null) return;
|
||||||
|
|
||||||
var origin = originTerminal.getOrigin();
|
var origin = originTerminal.getOrigin();
|
||||||
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
|
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
|
||||||
var monitorPos = monitor.getBlockPos();
|
var monitorPos = monitor.getBlockPos();
|
||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
package dan200.computercraft.client.sound;
|
package dan200.computercraft.client.sound;
|
||||||
|
|
||||||
import com.mojang.blaze3d.audio.Channel;
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
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 +37,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, EncodedAudio)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Channel channel;
|
Channel channel;
|
||||||
@@ -44,26 +45,28 @@ class DfpwmStream implements AudioStream {
|
|||||||
/**
|
/**
|
||||||
* The underlying {@link SoundEngine} executor.
|
* The underlying {@link SoundEngine} executor.
|
||||||
*
|
*
|
||||||
* @see SpeakerInstance#pushAudio(ByteBuf)
|
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||||
* @see SoundEngine#executor
|
* @see SoundEngine#executor
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Executor executor;
|
Executor executor;
|
||||||
|
|
||||||
private int charge = 0; // q
|
|
||||||
private int strength = 0; // s
|
|
||||||
private int lowPassCharge;
|
private int lowPassCharge;
|
||||||
private boolean previousBit = false;
|
|
||||||
|
|
||||||
DfpwmStream() {
|
DfpwmStream() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(ByteBuf input) {
|
void push(EncodedAudio audio) {
|
||||||
var readable = input.readableBytes();
|
var charge = audio.charge();
|
||||||
|
var strength = audio.strength();
|
||||||
|
var previousBit = audio.previousBit();
|
||||||
|
var input = audio.audio();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ 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.EncodedAudio;
|
||||||
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;
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public class SpeakerInstance {
|
|||||||
SpeakerInstance() {
|
SpeakerInstance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void pushAudio(ByteBuf buffer) {
|
private void pushAudio(EncodedAudio 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, EncodedAudio 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"
|
|
||||||
}
|
|
||||||
@@ -13,17 +13,17 @@ import dan200.computercraft.api.upgrades.UpgradeBase;
|
|||||||
import dan200.computercraft.core.metrics.Metric;
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
import dan200.computercraft.core.metrics.Metrics;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.command.arguments.ComputerSelector;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
import dan200.computercraft.shared.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;
|
||||||
@@ -166,10 +166,19 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
add("commands.computercraft.generic.exception", "Unhandled exception (%s)");
|
add("commands.computercraft.generic.exception", "Unhandled exception (%s)");
|
||||||
add("commands.computercraft.generic.additional_rows", "%d additional rows…");
|
add("commands.computercraft.generic.additional_rows", "%d additional rows…");
|
||||||
|
|
||||||
|
// Argument types
|
||||||
|
add("argument.computercraft.computer.instance", "Unique instance ID");
|
||||||
|
add("argument.computercraft.computer.id", "Computer ID");
|
||||||
|
add("argument.computercraft.computer.label", "Computer label");
|
||||||
|
add("argument.computercraft.computer.distance", "Distance to entity");
|
||||||
|
add("argument.computercraft.computer.family", "Computer family");
|
||||||
|
|
||||||
|
// Exceptions
|
||||||
add("argument.computercraft.computer.no_matching", "No computers matching '%s'");
|
add("argument.computercraft.computer.no_matching", "No computers matching '%s'");
|
||||||
add("argument.computercraft.computer.many_matching", "Multiple computers matching '%s' (instances %s)");
|
add("argument.computercraft.computer.many_matching", "Multiple computers matching '%s' (instances %s)");
|
||||||
add("argument.computercraft.tracking_field.no_field", "Unknown field '%s'");
|
add("argument.computercraft.tracking_field.no_field", "Unknown field '%s'");
|
||||||
add("argument.computercraft.argument_expected", "Argument expected");
|
add("argument.computercraft.argument_expected", "Argument expected");
|
||||||
|
add("argument.computercraft.unknown_computer_family", "Unknown computer family '%s'");
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
add(Metrics.COMPUTER_TASKS, "Tasks");
|
add(Metrics.COMPUTER_TASKS, "Tasks");
|
||||||
@@ -272,17 +281,18 @@ 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"),
|
||||||
ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey),
|
ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||||
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey)
|
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||||
|
ComputerSelector.options().values().stream().map(ComputerSelector.Option::translationKey)
|
||||||
).flatMap(x -> x);
|
).flatMap(x -> x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 @@
|
|||||||
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 dan200.computercraft.shared.integration.ExternalModTags;
|
||||||
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;
|
||||||
@@ -79,6 +81,14 @@ 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());
|
||||||
|
|
||||||
|
tags.tag(ExternalModTags.Blocks.CREATE_BRITTLE).add(
|
||||||
|
ModRegistry.Blocks.CABLE.get(),
|
||||||
|
ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get(),
|
||||||
|
ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void itemTags(ItemTagConsumer tags) {
|
public static void itemTags(ItemTagConsumer tags) {
|
||||||
@@ -109,9 +119,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,27 +9,25 @@ import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
|
|||||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The registry for peripheral providers.
|
* The registry for peripheral providers.
|
||||||
* <p>
|
|
||||||
* This lives in the {@code impl} package despite it not being part of the public API, in order to mirror Forge's class.
|
|
||||||
*/
|
*/
|
||||||
public final class Peripherals {
|
public final class Peripherals {
|
||||||
private static final GenericPeripheralProvider<Runnable> genericProvider = new GenericPeripheralProvider<>();
|
private static final GenericPeripheralProvider genericProvider = new GenericPeripheralProvider();
|
||||||
|
|
||||||
private Peripherals() {
|
private Peripherals() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addGenericLookup(ComponentLookup<? super Runnable> lookup) {
|
public static void addGenericLookup(ComponentLookup lookup) {
|
||||||
genericProvider.registerLookup(lookup);
|
genericProvider.registerLookup(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable IPeripheral getGenericPeripheral(Level level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, Runnable invalidate) {
|
public static @Nullable IPeripheral getGenericPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) {
|
||||||
return genericProvider.getPeripheral(level, pos, side, blockEntity, invalidate);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user