mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-17 15:07:38 +00:00
Compare commits
4 Commits
v1.20.1-1.
...
v1.20.4-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
240528cce5 | ||
![]() |
83f1f86888 | ||
![]() |
9c202bd1c2 | ||
![]() |
fc834cd97f |
30
.github/workflows/main-ci.yml
vendored
30
.github/workflows/main-ci.yml
vendored
@@ -9,16 +9,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 📥 Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: 📥 Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: 📥 Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
@@ -58,13 +58,13 @@ jobs:
|
||||
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
||||
|
||||
- name: 📤 Upload Jar
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: CC-Tweaked
|
||||
path: ./jars
|
||||
|
||||
- name: 📤 Upload coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
build-core:
|
||||
strategy:
|
||||
@@ -81,28 +81,24 @@ jobs:
|
||||
runs-on: ${{ matrix.uses }}
|
||||
|
||||
steps:
|
||||
- name: 📥 Clone repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: 📥 Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: 📥 Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
- name: ⚒️ Build
|
||||
run: |
|
||||
./gradlew --configure-on-demand :core:assemble
|
||||
|
||||
- name: 🧪 Run tests
|
||||
- name: Run tests
|
||||
run: |
|
||||
./gradlew --configure-on-demand :core:test
|
||||
|
||||
- name: 🧪 Parse test reports
|
||||
- name: Parse test reports
|
||||
run: python3 ./tools/parse-reports.py
|
||||
if: ${{ failure() }}
|
||||
|
9
.github/workflows/make-doc.yml
vendored
9
.github/workflows/make-doc.yml
vendored
@@ -3,7 +3,8 @@ name: Build documentation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- mc-*
|
||||
- mc-1.19.x
|
||||
- mc-1.20.x
|
||||
|
||||
jobs:
|
||||
make_doc:
|
||||
@@ -12,16 +13,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,7 +9,6 @@
|
||||
/projects/*/logs
|
||||
/projects/fabric/fabricloader.log
|
||||
/projects/*/build
|
||||
/projects/*/src/test/generated_tests/
|
||||
/buildSrc/build
|
||||
/out
|
||||
/buildSrc/out
|
||||
|
26
.gitpod.yml
Normal file
26
.gitpod.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
image:
|
||||
file: config/gitpod/Dockerfile
|
||||
|
||||
ports:
|
||||
- port: 25565
|
||||
onOpen: notify
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- eamodio.gitlens
|
||||
- github.vscode-pull-request-github
|
||||
- ms-azuretools.vscode-docker
|
||||
- redhat.java
|
||||
- richardwillis.vscode-gradle
|
||||
- vscjava.vscode-java-debug
|
||||
- vscode.github
|
||||
|
||||
tasks:
|
||||
- name: Setup pre-commit hool
|
||||
init: pre-commit install --allow-missing-config
|
||||
- name: Install npm packages
|
||||
init: npm ci
|
@@ -8,7 +8,8 @@ Files:
|
||||
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
|
||||
projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/*
|
||||
projects/common/src/testMod/resources/data/cctest/structures/*
|
||||
projects/*/src/generated/*
|
||||
projects/fabric/src/generated/*
|
||||
projects/forge/src/generated/*
|
||||
projects/web/src/htmlTransform/export/index.json
|
||||
projects/web/src/htmlTransform/export/items/minecraft/*
|
||||
Comment: Generated/data files are CC0.
|
||||
|
@@ -29,9 +29,9 @@ automatically with GitHub, so please don't submit PRs adding/changing translatio
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
|
||||
- Make sure you've got the following software installed:
|
||||
- Java Development Kit (JDK). This can be downloaded from [Adoptium].
|
||||
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||
- [Git](https://git-scm.com/).
|
||||
- [NodeJS][node].
|
||||
- If you want to work on documentation, [NodeJS][node].
|
||||
|
||||
- Download CC: Tweaked's source code:
|
||||
```
|
||||
|
@@ -51,9 +51,8 @@ dependencies {
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
||||
|
||||
// Forge Gradle
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
|
||||
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion"))
|
||||
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")
|
||||
runtimeOnly("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion")
|
||||
|
||||
// Fabric Loom
|
||||
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
||||
|
@@ -5,8 +5,9 @@
|
||||
import cc.tweaked.gradle.JUnitExt
|
||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||
import org.jetbrains.gradle.ext.*
|
||||
import org.jetbrains.gradle.ext.Application
|
||||
import org.jetbrains.gradle.ext.compiler
|
||||
import org.jetbrains.gradle.ext.runConfigurations
|
||||
import org.jetbrains.gradle.ext.settings
|
||||
|
||||
plugins {
|
||||
publishing
|
||||
@@ -85,19 +86,6 @@ idea.project.settings.runConfigurations {
|
||||
moduleName = "${idea.project.name}.forge.test"
|
||||
packageName = ""
|
||||
}
|
||||
|
||||
register<Application>("Standalone") {
|
||||
moduleName = "${idea.project.name}.standalone.main"
|
||||
mainClass = "cc.tweaked.standalone.Main"
|
||||
programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains"
|
||||
}
|
||||
}
|
||||
|
||||
// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in
|
||||
// "Project Structure".
|
||||
idea.project.settings.delegateActions {
|
||||
delegateBuildRunToGradle = false
|
||||
testRunner = ActionDelegationConfig.TestRunner.PLATFORM
|
||||
}
|
||||
|
||||
idea.project.settings.compiler.javac {
|
||||
|
@@ -14,18 +14,14 @@ repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
maven("https://maven.minecraftforge.net") {
|
||||
name = "Forge"
|
||||
maven("https://maven.neoforged.net/releases") {
|
||||
name = "NeoForge"
|
||||
content {
|
||||
includeGroup("net.minecraftforge")
|
||||
includeGroup("net.minecraftforge.gradle")
|
||||
}
|
||||
}
|
||||
|
||||
maven("https://maven.parchmentmc.org") {
|
||||
name = "Librarian"
|
||||
content {
|
||||
includeGroupByRegex("^org\\.parchmentmc.*")
|
||||
includeGroup("net.neoforged")
|
||||
includeGroup("net.neoforged.gradle")
|
||||
includeModule("codechicken", "DiffPatch")
|
||||
includeModule("net.covers1624", "Quack")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +47,9 @@ dependencies {
|
||||
|
||||
implementation(libs.curseForgeGradle)
|
||||
implementation(libs.fabric.loom)
|
||||
implementation(libs.forgeGradle)
|
||||
implementation(libs.ideaExt)
|
||||
implementation(libs.librarian)
|
||||
implementation(libs.minotaur)
|
||||
implementation(libs.neoGradle.userdev)
|
||||
implementation(libs.vanillaExtract)
|
||||
}
|
||||
|
||||
|
@@ -10,10 +10,8 @@ import cc.tweaked.gradle.IdeaRunConfigurations
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
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("org.parchmentmc.librarian.forgegradle")
|
||||
id("net.neoforged.gradle.userdev")
|
||||
}
|
||||
|
||||
plugins.apply(CCTweakedPlugin::class.java)
|
||||
@@ -21,10 +19,20 @@ plugins.apply(CCTweakedPlugin::class.java)
|
||||
val mcVersion: String by extra
|
||||
|
||||
minecraft {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
|
||||
modIdentifier("computercraft")
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -32,13 +40,3 @@ MinecraftConfigurations.setup(project)
|
||||
extensions.configure(CCTweakedExtension::class.java) {
|
||||
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() }
|
||||
}
|
||||
|
@@ -44,13 +44,6 @@ repositories {
|
||||
|
||||
exclusiveContent {
|
||||
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 {
|
||||
includeGroup("cc.tweaked")
|
||||
// Things we mirror
|
||||
@@ -94,8 +87,9 @@ sourceSets.all {
|
||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||
check("ReferenceEquality", CheckSeverity.OFF)
|
||||
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
|
||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||
|
||||
@@ -108,8 +102,6 @@ sourceSets.all {
|
||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||
option("NullAway:CheckOptionalEmptiness")
|
||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||
|
||||
excludedPaths = ".*/jmh_generated/.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
||||
apiToken = findProperty("curseForgeApiKey") ?: ""
|
||||
enabled = apiToken != ""
|
||||
|
||||
val mainFile = upload("282001", modPublishing.output)
|
||||
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
|
||||
mainFile.changelog =
|
||||
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||
mainFile.changelogType = "markdown"
|
||||
|
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class CCTweakedExtension(
|
||||
@@ -225,12 +226,12 @@ abstract class CCTweakedExtension(
|
||||
* where possible.
|
||||
*/
|
||||
fun downloadFile(label: String, url: String): File {
|
||||
val uri = URI(url)
|
||||
val path = File(uri.path)
|
||||
val url = URL(url)
|
||||
val path = File(url.path)
|
||||
|
||||
project.repositories.ivy {
|
||||
name = label
|
||||
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
|
||||
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
||||
patternLayout {
|
||||
artifact("[artifact].[ext]")
|
||||
}
|
||||
|
@@ -4,23 +4,59 @@
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import net.minecraftforge.gradle.common.util.RunConfig
|
||||
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal
|
||||
import net.neoforged.gradle.common.runs.run.RunImpl
|
||||
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.tasks.JavaExec
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
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
|
||||
|
||||
/**
|
||||
* Set [JavaExec] task to run a given [RunConfig].
|
||||
*
|
||||
* See also [RunExec].
|
||||
*/
|
||||
fun JavaExec.setRunConfig(config: RunConfig) {
|
||||
dependsOn("prepareRuns")
|
||||
setRunConfigInternal(project, this, config)
|
||||
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
|
||||
fun JavaExec.setRunConfig(config: Run) {
|
||||
mainClass.set(config.mainClass)
|
||||
workingDir = config.workingDirectory.get().asFile
|
||||
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(
|
||||
project.extensions.getByType(JavaToolchainService::class.java)
|
||||
.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)
|
||||
}
|
||||
|
@@ -1,120 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import cc.tweaked.vanillaextract.core.util.MoreFiles
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.*
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Merge common files across multiple directories into one destination directory.
|
||||
*
|
||||
* This is intended for merging the generated resources from the Forge and Fabric projects. Files common between the two
|
||||
* are written to the global [output] directory, while distinct files are written to the per-source
|
||||
* [MergeTrees.Source.output] directory.
|
||||
*/
|
||||
abstract class MergeTrees : DefaultTask() {
|
||||
/**
|
||||
* A source directory to read from.
|
||||
*/
|
||||
interface Source {
|
||||
/**
|
||||
* The folder contianing all input files.
|
||||
*/
|
||||
@get:InputFiles
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
val input: ConfigurableFileTree
|
||||
|
||||
fun input(configure: Action<ConfigurableFileTree>) {
|
||||
configure.execute(input)
|
||||
}
|
||||
|
||||
/**
|
||||
* The folder to write files unique to this folder to.
|
||||
*/
|
||||
@get:OutputDirectory
|
||||
val output: DirectoryProperty
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of sources.
|
||||
*/
|
||||
@get:Nested
|
||||
abstract val sources: ListProperty<Source>
|
||||
|
||||
/**
|
||||
* Add and configure a new source.
|
||||
*/
|
||||
fun source(configure: Action<Source>) {
|
||||
val instance = objectFactory.newInstance(Source::class.java)
|
||||
configure.execute(instance)
|
||||
instance.output.disallowChanges()
|
||||
sources.add(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory to write common files to.
|
||||
*/
|
||||
@get:OutputDirectory
|
||||
abstract val output: DirectoryProperty
|
||||
|
||||
@get:Inject
|
||||
protected abstract val objectFactory: ObjectFactory
|
||||
|
||||
@get:Inject
|
||||
protected abstract val fsOperations: FileSystemOperations
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val sources = this.sources.get()
|
||||
if (sources.isEmpty()) throw GradleException("Cannot have an empty list of sources")
|
||||
|
||||
val files = mutableMapOf<String, SharedFile>()
|
||||
for (source in sources) {
|
||||
source.input.visit(
|
||||
object : FileVisitor {
|
||||
override fun visitDir(dirDetails: FileVisitDetails) = Unit
|
||||
override fun visitFile(fileDetails: FileVisitDetails) {
|
||||
val path = fileDetails.file.toRelativeString(source.input.dir)
|
||||
val hash = MoreFiles.computeSha1(fileDetails.file.toPath())
|
||||
|
||||
val existing = files[path]
|
||||
if (existing == null) {
|
||||
files[path] = SharedFile(hash, 1)
|
||||
} else if (existing.hash == hash) {
|
||||
existing.found++
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
|
||||
|
||||
// Copy shared files to the common directory
|
||||
fsOperations.sync {
|
||||
from(sources[0].input)
|
||||
into(output)
|
||||
include(sharedFiles)
|
||||
}
|
||||
|
||||
// And all other files to their per-source directory
|
||||
for (source in sources) {
|
||||
fsOperations.sync {
|
||||
from(source.input)
|
||||
into(source.output)
|
||||
exclude(sharedFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SharedFile(val hash: String, var found: Int)
|
||||
}
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import net.minecraftforge.gradle.common.util.RunConfig
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.invocation.Gradle
|
||||
@@ -65,14 +64,6 @@ abstract class ClientJavaExec : JavaExec() {
|
||||
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.
|
||||
*/
|
||||
|
@@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() {
|
||||
@TaskAction
|
||||
fun install() {
|
||||
project.exec {
|
||||
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
|
||||
commandLine("npm", "ci")
|
||||
workingDir = projectRoot.get().asFile
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,6 @@ abstract class NpmInstall : DefaultTask() {
|
||||
abstract class NpxExecToDir : ExecToDir() {
|
||||
init {
|
||||
dependsOn(NpmInstall.TASK_NAME)
|
||||
executable = ProcessHelpers.getExecutable("npx")
|
||||
executable = "npx"
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ import org.gradle.api.GradleException
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
internal object ProcessHelpers {
|
||||
fun startProcess(vararg command: String): Process {
|
||||
@@ -35,7 +34,7 @@ internal object ProcessHelpers {
|
||||
val process = startProcess(*command)
|
||||
process.outputStream.close()
|
||||
|
||||
val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader ->
|
||||
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
||||
reader.lines().filter { it.isNotEmpty() }.toList()
|
||||
}
|
||||
ProcessGroovyMethods.closeStreams(process)
|
||||
@@ -47,28 +46,6 @@ internal object ProcessHelpers {
|
||||
val path = System.getenv("PATH") ?: return false
|
||||
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an executable on the `PATH` if required.
|
||||
*
|
||||
* [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
|
||||
* Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
|
||||
*/
|
||||
fun getExecutable(name: String): String {
|
||||
if (!System.getProperty("os.name").lowercase().contains("windows")) return name
|
||||
|
||||
val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
|
||||
val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
|
||||
|
||||
for (pathEntry in path) {
|
||||
for (ext in pathExt) {
|
||||
val resolved = File(pathEntry, name + ext)
|
||||
if (resolved.exists()) return resolved.getAbsolutePath()
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Process.waitForOrThrow(message: String) {
|
||||
|
@@ -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,12 +13,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<property name="tabWidth" value="4"/>
|
||||
<property name="charset" value="UTF-8" />
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||
</module>
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
|
@@ -21,5 +21,5 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
||||
|
||||
<!-- Allow underscores in our test classes. -->
|
||||
<suppress checks="MethodName" files=".*(Contract|Test).java" />
|
||||
<suppress checks="MethodName" files=".*Contract.java" />
|
||||
</suppressions>
|
||||
|
12
config/gitpod/Dockerfile
Normal file
12
config/gitpod/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
FROM gitpod/workspace-base
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN sudo apt-get -q update \
|
||||
&& sudo apt-get install -yq openjdk-16-jdk python3-pip npm \
|
||||
&& sudo pip3 install pre-commit \
|
||||
&& sudo update-java-alternatives --set java-1.16.0-openjdk-amd64
|
@@ -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.
|
||||
|
||||
## 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
|
||||
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||
|
@@ -131,7 +131,7 @@ different.
|
||||
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||
|
||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each
|
||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
||||
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
||||
[`fs.ReadHandle.read`] if you prefer.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 254 KiB |
Binary file not shown.
Before Width: | Height: | Size: 304 KiB |
@@ -1,140 +0,0 @@
|
||||
---
|
||||
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"
|
@@ -2,15 +2,15 @@
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.parallel=true
|
||||
|
||||
kotlin.stdlib.default.dependency=false
|
||||
kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.111.0
|
||||
isUnstable=true
|
||||
modVersion=1.109.6
|
||||
|
||||
# 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,26 +7,26 @@
|
||||
# Minecraft
|
||||
# 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
|
||||
fabric-api = "0.86.1+1.20.1"
|
||||
fabric-loader = "0.14.21"
|
||||
forge = "47.1.0"
|
||||
forgeSpi = "7.0.1"
|
||||
fabric-api = "0.93.1+1.20.4"
|
||||
fabric-loader = "0.15.3"
|
||||
neoForge = "20.4.161-beta"
|
||||
neoForgeSpi = "8.0.1"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2023.08.20"
|
||||
parchmentMc = "1.20.1"
|
||||
yarn = "1.20.1+build.10"
|
||||
parchment = "2023.12.31"
|
||||
parchmentMc = "1.20.3"
|
||||
yarn = "1.20.4+build.3"
|
||||
|
||||
# Core dependencies (these versions are tied to the version Minecraft uses)
|
||||
fastutil = "8.5.9"
|
||||
guava = "31.1-jre"
|
||||
netty = "4.1.82.Final"
|
||||
slf4j = "2.0.1"
|
||||
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"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = "0.9.3"
|
||||
cobalt = "0.9.1"
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jsr305 = "3.0.2"
|
||||
@@ -36,14 +36,14 @@ kotlin-coroutines = "1.7.3"
|
||||
nightConfig = "3.6.7"
|
||||
|
||||
# Minecraft mods
|
||||
emi = "1.0.8+1.20.1"
|
||||
emi = "1.0.30+1.20.4"
|
||||
fabricPermissions = "0.3.20230723"
|
||||
iris = "1.6.4+1.20"
|
||||
jei = "15.2.0.22"
|
||||
modmenu = "7.1.0"
|
||||
iris = "1.6.14+1.20.4"
|
||||
jei = "17.3.0.48"
|
||||
modmenu = "9.0.0"
|
||||
moreRed = "4.0.0.4"
|
||||
oculus = "1.2.5"
|
||||
rei = "12.0.626"
|
||||
rei = "14.0.688"
|
||||
rubidium = "0.6.1"
|
||||
sodium = "mc1.20-0.4.10"
|
||||
|
||||
@@ -51,28 +51,26 @@ sodium = "mc1.20-0.4.10"
|
||||
hamcrest = "2.2"
|
||||
jqwik = "1.8.2"
|
||||
junit = "5.10.1"
|
||||
jmh = "1.37"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.2"
|
||||
checkstyle = "10.14.1"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.27.0"
|
||||
checkstyle = "10.12.6"
|
||||
curseForgeGradle = "1.1.18"
|
||||
errorProne-core = "2.23.0"
|
||||
errorProne-plugin = "3.1.0"
|
||||
fabric-loom = "1.6.7"
|
||||
forgeGradle = "6.0.21"
|
||||
fabric-loom = "1.5.7"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.50.0"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-71-g378d86e"
|
||||
librarian = "1.+"
|
||||
illuaminate = "0.1.0-44-g9ee0055"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.+"
|
||||
nullAway = "0.10.25"
|
||||
minotaur = "2.8.7"
|
||||
neoGradle = "7.0.93"
|
||||
nullAway = "0.9.9"
|
||||
spotless = "6.23.3"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.10.0-SQUID.4"
|
||||
vanillaExtract = "0.1.3"
|
||||
teavm = "0.10.0-SQUID.2"
|
||||
vanillaExtract = "0.1.1"
|
||||
versionCatalogUpdate = "0.8.1"
|
||||
|
||||
[libraries]
|
||||
@@ -84,7 +82,7 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
|
||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||
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" }
|
||||
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
||||
@@ -106,9 +104,9 @@ fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fab
|
||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", 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.4-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.20.4-forge", version.ref = "jei" }
|
||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
||||
@@ -127,8 +125,6 @@ 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-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||
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-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
||||
@@ -146,15 +142,13 @@ 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-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
||||
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
||||
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
||||
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
||||
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" }
|
||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
||||
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
|
||||
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
|
||||
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
|
||||
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
|
||||
@@ -166,11 +160,9 @@ vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = "
|
||||
yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
|
||||
|
||||
[plugins]
|
||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||
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" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||
|
||||
@@ -181,7 +173,7 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||
# Minecraft
|
||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
||||
externalMods-forge-runtime = ["jei-forge"]
|
||||
externalMods-forge-runtime = []
|
||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
||||
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
|
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.client;
|
||||
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||
|
||||
/**
|
||||
* The public API for client-only code.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI The main API
|
||||
*/
|
||||
public final class ComputerCraftAPIClient {
|
||||
private ComputerCraftAPIClient() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||
* <p>
|
||||
* This may be called at any point after registry creation, though it is recommended to call it within your client
|
||||
* setup step.
|
||||
*
|
||||
* @param serialiser The turtle upgrade serialiser.
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
* @deprecated This method can lead to confusing load behaviour on Forge. Use
|
||||
* {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} on Fabric, or
|
||||
* {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} on Forge.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
// TODO(1.20.4): Remove this
|
||||
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||
}
|
||||
|
||||
private static ComputerCraftAPIClientService getInstance() {
|
||||
return ComputerCraftAPIClientService.get();
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
|
||||
/**
|
||||
* A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||
@@ -22,5 +22,5 @@ public interface RegisterTurtleUpgradeModeller {
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
*/
|
||||
<T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||
<T extends ITurtleUpgrade> void register(UpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@ import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -31,30 +30,15 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
/**
|
||||
* Obtain the model to be used when rendering a turtle peripheral.
|
||||
* <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 turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
|
||||
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
|
||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models.
|
||||
* @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 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.
|
||||
*/
|
||||
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
|
||||
return getModel(upgrade, (ITurtleAccess) null, side);
|
||||
}
|
||||
|
||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data);
|
||||
|
||||
/**
|
||||
* Get a list of models that this turtle modeller depends on.
|
||||
@@ -85,19 +69,6 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
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.
|
||||
*
|
||||
@@ -109,7 +80,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||
return new TurtleUpgradeModeller<>() {
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -37,16 +36,8 @@ final class TurtleUpgradeModellers {
|
||||
|
||||
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||
@Override
|
||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
||||
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
||||
return getModel(upgrade.getUpgradeItem(data), side);
|
||||
}
|
||||
|
||||
private TransformedModel getModel(ItemStack stack, TurtleSide side) {
|
||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
||||
var stack = upgrade.getUpgradeItem(data);
|
||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
||||
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
||||
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||
|
@@ -5,7 +5,6 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -23,7 +22,6 @@ import java.util.Map;
|
||||
*
|
||||
* @see WiredNode#getNetwork()
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface WiredNetwork {
|
||||
/**
|
||||
* Create a connection between two nodes.
|
||||
@@ -37,9 +35,7 @@ public interface WiredNetwork {
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see WiredNode#connectTo(WiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
* @deprecated Use {@link WiredNode#connectTo(WiredNode)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean connect(WiredNode left, WiredNode right);
|
||||
|
||||
/**
|
||||
@@ -54,9 +50,7 @@ public interface WiredNetwork {
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see WiredNode#disconnectFrom(WiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
* @deprecated Use {@link WiredNode#disconnectFrom(WiredNode)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean disconnect(WiredNode left, WiredNode right);
|
||||
|
||||
/**
|
||||
@@ -70,9 +64,7 @@ public interface WiredNetwork {
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see WiredNode#remove()
|
||||
* @deprecated Use {@link WiredNode#remove()}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean remove(WiredNode node);
|
||||
|
||||
/**
|
||||
@@ -85,8 +77,6 @@ public interface WiredNetwork {
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see WiredNode#updatePeripherals(Map)
|
||||
* @deprecated Use {@link WiredNode#updatePeripherals(Map)}
|
||||
*/
|
||||
@Deprecated
|
||||
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@ package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -23,7 +22,6 @@ import java.util.Map;
|
||||
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
||||
* be used on the main server thread.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface WiredNode extends PacketNetwork {
|
||||
/**
|
||||
* The associated element for this network node.
|
||||
@@ -39,9 +37,7 @@ public interface WiredNode extends PacketNetwork {
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @return This node's network.
|
||||
* @deprecated Use the connect/disconnect/remove methods on {@link WiredNode}.
|
||||
*/
|
||||
@Deprecated
|
||||
WiredNetwork getNetwork();
|
||||
|
||||
/**
|
||||
@@ -51,9 +47,12 @@ public interface WiredNode extends PacketNetwork {
|
||||
*
|
||||
* @param node The other node to connect to.
|
||||
* @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)
|
||||
*/
|
||||
boolean connectTo(WiredNode node);
|
||||
default boolean connectTo(WiredNode node) {
|
||||
return getNetwork().connect(this, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a connection between this node and another.
|
||||
@@ -62,9 +61,13 @@ public interface WiredNode extends PacketNetwork {
|
||||
*
|
||||
* @param node The other node to disconnect from.
|
||||
* @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)
|
||||
*/
|
||||
boolean disconnectFrom(WiredNode node);
|
||||
default boolean disconnectFrom(WiredNode node) {
|
||||
return getNetwork().disconnect(this, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
@@ -75,8 +78,11 @@ 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
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see WiredNetwork#remove(WiredNode)
|
||||
*/
|
||||
boolean remove();
|
||||
default boolean remove() {
|
||||
return getNetwork().remove(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this node's peripherals as having changed.
|
||||
@@ -85,6 +91,9 @@ public interface WiredNode extends PacketNetwork {
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @see WiredNetwork#updatePeripherals(WiredNode, Map)
|
||||
*/
|
||||
void updatePeripherals(Map<String, IPeripheral> peripherals);
|
||||
default void updatePeripherals(Map<String, IPeripheral> peripherals) {
|
||||
getNetwork().updatePeripherals(this, peripherals);
|
||||
}
|
||||
}
|
||||
|
@@ -4,15 +4,12 @@
|
||||
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wrapper class for pocket computers.
|
||||
@@ -90,13 +87,4 @@ public interface IPocketAccess {
|
||||
* entity} changes.
|
||||
*/
|
||||
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.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 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.
|
||||
* <p>
|
||||
* Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding
|
||||
* {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry.
|
||||
* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
|
||||
* {@link UpgradeSerialiser} instance, which are then registered in a registry.
|
||||
* <p>
|
||||
* 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
|
||||
* and where files should be located.
|
||||
* the upgrade automatically registered. It is recommended this is done via {@linkplain PocketUpgradeDataProvider data
|
||||
* 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 {
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
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
|
||||
* 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) {
|
||||
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.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -16,15 +20,54 @@ import javax.annotation.Nullable;
|
||||
* peripheral.
|
||||
* <p>
|
||||
* 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>
|
||||
* 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
|
||||
* and where files should be located.
|
||||
* the upgrade automatically registered. It is recommended this is done via {@linkplain TurtleUpgradeDataProvider data
|
||||
* 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 {
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@@ -7,8 +7,9 @@ package dan200.computercraft.api.turtle;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
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
|
||||
* 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");
|
||||
|
||||
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 {
|
||||
private final ResourceLocation id;
|
||||
private final TurtleUpgradeSerialiser<?> serialiser;
|
||||
private final UpgradeSerialiser<? extends ITurtleUpgrade> serialiser;
|
||||
private final Item toolItem;
|
||||
private @Nullable String adjective;
|
||||
private @Nullable Item craftingItem;
|
||||
@@ -66,7 +67,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
private boolean allowEnchantments = false;
|
||||
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.serialiser = serialiser;
|
||||
this.toolItem = toolItem;
|
||||
@@ -149,12 +150,12 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
*
|
||||
* @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 -> {
|
||||
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 (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 (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||
|
@@ -1,109 +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. The way to do this varies on mod loader, see
|
||||
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
|
||||
* <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.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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
|
||||
* crafting item, but this may be relaxed for your upgrade.
|
||||
* <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
|
||||
* {@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
|
||||
// null one.
|
||||
var shareTag = PlatformHelper.get().getShareTag(stack);
|
||||
var craftingShareTag = PlatformHelper.get().getShareTag(crafting);
|
||||
if (shareTag == craftingShareTag) return true;
|
||||
if (shareTag == null) return Objects.requireNonNull(craftingShareTag).isEmpty();
|
||||
if (craftingShareTag == null) return shareTag.isEmpty();
|
||||
return shareTag.equals(craftingShareTag);
|
||||
var tag = stack.getTag();
|
||||
var craftingTag = crafting.getTag();
|
||||
if (tag == craftingTag) return true;
|
||||
if (tag == null) return Objects.requireNonNull(craftingTag).isEmpty();
|
||||
if (craftingTag == null) return tag.isEmpty();
|
||||
return tag.equals(craftingTag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -6,19 +6,20 @@ package dan200.computercraft.api.upgrades;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||
import net.minecraft.Util;
|
||||
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.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
@@ -31,31 +32,31 @@ import java.util.function.Function;
|
||||
* the other subclasses.
|
||||
*
|
||||
* @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 String name;
|
||||
private final String folder;
|
||||
private final ResourceKey<Registry<R>> registry;
|
||||
private final Registry<UpgradeSerialiser<? extends T>> registry;
|
||||
|
||||
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.name = name;
|
||||
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 serialiser The simple serialiser.
|
||||
* @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)) {
|
||||
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 serialiser The simple serialiser.
|
||||
* @param item The crafting upgrade for this item.
|
||||
* @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)) {
|
||||
throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser.");
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
||||
protected abstract void addUpgrades(Consumer<Upgrade<UpgradeSerialiser<? extends T>>> addUpgrade);
|
||||
|
||||
@Override
|
||||
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());
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public final R existingSerialiser(ResourceLocation id) {
|
||||
var result = PlatformHelper.get().getRegistryObject(registry, id);
|
||||
if (result == null) throw new IllegalArgumentException("No such serialiser " + registry);
|
||||
public final UpgradeSerialiser<? extends T> existingSerialiser(ResourceLocation id) {
|
||||
var result = registry.get(id);
|
||||
if (result == null) throw new IllegalArgumentException("No such serialiser " + id);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@@ -5,21 +5,40 @@
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
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.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
|
||||
* of {@link TurtleUpgradeSerialiser} or {@link PocketUpgradeSerialiser}.
|
||||
* A serialiser for {@link ITurtleUpgrade} or {@link IPocketUpgrade}s.
|
||||
* <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.
|
||||
* @see TurtleUpgradeSerialiser
|
||||
* @see PocketUpgradeSerialiser
|
||||
* @see ITurtleUpgrade
|
||||
* @see IPocketUpgrade
|
||||
*/
|
||||
public interface UpgradeSerialiser<T extends UpgradeBase> {
|
||||
/**
|
||||
@@ -49,4 +68,30 @@ public interface UpgradeSerialiser<T extends UpgradeBase> {
|
||||
*/
|
||||
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.wired.WiredElement;
|
||||
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.turtle.ITurtleUpgrade;
|
||||
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.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
@@ -67,9 +68,9 @@ public interface ComputerCraftAPIService {
|
||||
|
||||
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();
|
||||
|
||||
|
@@ -6,11 +6,6 @@ package dan200.computercraft.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
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 javax.annotation.Nullable;
|
||||
@@ -32,39 +27,6 @@ public interface PlatformHelper {
|
||||
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
|
||||
* {@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.
|
||||
*/
|
||||
public static <T> T load(Class<T> klass) {
|
||||
var services = ServiceLoader.load(klass, klass.getClassLoader()).stream().toList();
|
||||
var services = ServiceLoader.load(klass).stream().toList();
|
||||
return switch (services.size()) {
|
||||
case 1 -> services.get(0).get();
|
||||
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.
|
||||
*/
|
||||
@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;
|
||||
|
||||
protected SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||
public SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T fromJson(ResourceLocation id, JsonObject object) {
|
||||
public T fromJson(ResourceLocation id, JsonObject object) {
|
||||
var item = GsonHelper.getAsItem(object, "item");
|
||||
return factory.apply(id, new ItemStack(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||
var item = buffer.readItem();
|
||||
return factory.apply(id, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||
public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||
buffer.writeItem(upgrade.getCraftingItem());
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ import java.util.function.Function;
|
||||
* @param <T> The upgrade that this class can serialise and deserialise.
|
||||
*/
|
||||
@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;
|
||||
|
||||
public SimpleSerialiser(Function<ResourceLocation, T> constructor) {
|
||||
@@ -29,16 +29,16 @@ public abstract class SimpleSerialiser<T extends UpgradeBase> implements Upgrade
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T fromJson(ResourceLocation id, JsonObject object) {
|
||||
public T fromJson(ResourceLocation id, JsonObject object) {
|
||||
return constructor.apply(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||
return constructor.apply(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||
public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||
}
|
||||
}
|
||||
|
@@ -11,12 +11,6 @@ plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir("src/generated/resources")
|
||||
}
|
||||
}
|
||||
|
||||
minecraft {
|
||||
accessWideners(
|
||||
"src/main/resources/computercraft.accesswidener",
|
||||
@@ -52,9 +46,6 @@ dependencies {
|
||||
testImplementation(libs.bundles.test)
|
||||
testRuntimeOnly(libs.bundles.testRuntime)
|
||||
|
||||
testImplementation(libs.jmh)
|
||||
testAnnotationProcessor(libs.jmh.processor)
|
||||
|
||||
testModCompileOnly(libs.mixin)
|
||||
testModImplementation(testFixtures(project(":core")))
|
||||
testModImplementation(testFixtures(project(":common")))
|
||||
@@ -111,21 +102,3 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||
}
|
||||
|
||||
val runData by tasks.registering(MergeTrees::class) {
|
||||
output = layout.projectDirectory.dir("src/generated/resources")
|
||||
|
||||
for (loader in listOf("forge", "fabric")) {
|
||||
mustRunAfter(":$loader:runData")
|
||||
source {
|
||||
input {
|
||||
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
|
||||
exclude(".cache")
|
||||
}
|
||||
|
||||
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
||||
|
@@ -108,7 +108,7 @@ public final class ClientHooks {
|
||||
*/
|
||||
public static void addBlockDebugInfo(Consumer<String> addText) {
|
||||
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;
|
||||
|
||||
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
|
||||
@@ -138,7 +138,7 @@ public final class ClientHooks {
|
||||
* @param addText A callback which adds a single line of text.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@ import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
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.ViewComputerMenu;
|
||||
@@ -32,6 +31,8 @@ import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.item.ItemColor;
|
||||
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.renderer.ShaderInstance;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
@@ -43,6 +44,8 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||
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.ItemStack;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
@@ -78,34 +81,35 @@ public final class ClientRegistry {
|
||||
|
||||
/**
|
||||
* Register any client-side objects which must be done on the main thread.
|
||||
*
|
||||
* @param itemProperties Callback to register item properties.
|
||||
*/
|
||||
public static void registerMainThread(RegisterItemProperty itemProperties) {
|
||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
||||
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
||||
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
|
||||
|
||||
MenuScreens.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
|
||||
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
||||
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
||||
|
||||
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
||||
|
||||
registerItemProperty(itemProperties, "state",
|
||||
new UnclampedPropertyFunction((stack, world, player, random) -> {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
|
||||
}),
|
||||
public static void registerMainThread() {
|
||||
registerItemProperty("state",
|
||||
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
registerItemProperty(itemProperties, "coloured",
|
||||
registerItemProperty("coloured",
|
||||
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
||||
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"),
|
||||
@@ -121,17 +125,9 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty(RegisterItemProperty itemProperties, String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||
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);
|
||||
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
||||
}
|
||||
|
||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
||||
@@ -169,14 +165,17 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
private static int getPocketColour(ItemStack stack, int layer) {
|
||||
return switch (layer) {
|
||||
default -> 0xFFFFFF;
|
||||
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
|
||||
case 2 -> { // Light colour
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
switch (layer) {
|
||||
case 0:
|
||||
default:
|
||||
return 0xFFFFFF;
|
||||
case 1: // Frame colour
|
||||
return IColouredItem.getColourBasic(stack);
|
||||
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) {
|
||||
|
@@ -9,7 +9,6 @@ import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.client.network.ClientNetworking;
|
||||
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.InputHandler;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
@@ -19,7 +18,6 @@ import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
@@ -98,8 +96,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
getTerminal().update();
|
||||
|
||||
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
|
||||
new ItemToast(minecraft(), displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
||||
.showOrReplace(minecraft().getToasts());
|
||||
new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
||||
.showOrReplace(minecraft.getToasts());
|
||||
uploadNagDeadline = Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +125,6 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(graphics, mouseX, mouseY);
|
||||
}
|
||||
@@ -209,7 +206,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toUpload.isEmpty()) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||
}
|
||||
|
||||
public void uploadResult(UploadResult result, @Nullable Component message) {
|
||||
@@ -225,13 +222,9 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
}
|
||||
|
||||
private void alert(Component title, Component message) {
|
||||
OptionScreen.show(minecraft(), title, message,
|
||||
List.of(OptionScreen.newButton(OK, b -> minecraft().setScreen(this))),
|
||||
() -> minecraft().setScreen(this)
|
||||
OptionScreen.show(minecraft, title, message,
|
||||
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||
() -> minecraft.setScreen(this)
|
||||
);
|
||||
}
|
||||
|
||||
private Minecraft minecraft() {
|
||||
return Nullability.assertNonNull(minecraft);
|
||||
}
|
||||
}
|
||||
|
@@ -49,31 +49,31 @@ public final class ClientInputHandler implements InputHandler {
|
||||
|
||||
@Override
|
||||
public void keyDown(int key, boolean repeat) {
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key));
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyUp(int key) {
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key));
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClick(int button, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseUp(int button, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.UP, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDrag(int button, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.DRAG, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseScroll(int direction, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.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
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
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.ToastComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
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}.
|
||||
*/
|
||||
public class ItemToast implements Toast {
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation("toast/recipe");
|
||||
public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object();
|
||||
|
||||
private static final long DISPLAY_TIME = 7000L;
|
||||
@@ -79,7 +81,7 @@ public class ItemToast implements Toast {
|
||||
}
|
||||
|
||||
if (width == 160 && message.size() <= 1) {
|
||||
graphics.blit(TEXTURE, 0, 0, 0, 64, width, height());
|
||||
graphics.blitSprite(TEXTURE, 0, 0, width, height());
|
||||
} else {
|
||||
|
||||
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) {
|
||||
var leftOffset = 5;
|
||||
var leftOffset = u == 0 ? 20 : 5;
|
||||
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) {
|
||||
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,10 +6,8 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||
@@ -18,7 +16,6 @@ import net.minecraft.world.entity.player.Inventory;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
|
||||
@@ -47,8 +44,8 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
protected void init() {
|
||||
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
||||
// grabbing unsets.
|
||||
minecraft().mouseHandler.grabMouse();
|
||||
minecraft().screen = this;
|
||||
minecraft.mouseHandler.grabMouse();
|
||||
minecraft.screen = this;
|
||||
KeyMapping.releaseAll();
|
||||
|
||||
super.init();
|
||||
@@ -66,14 +63,14 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
|
||||
Objects.requireNonNull(minecraft().player).getInventory().swapPaint(pDelta);
|
||||
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
|
||||
minecraft.player.getInventory().swapPaint(scrollX);
|
||||
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
Objects.requireNonNull(minecraft().player).closeContainer();
|
||||
minecraft.player.closeContainer();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@@ -96,16 +93,12 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float 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 y = 10;
|
||||
for (var line : lines) {
|
||||
graphics.drawString(font, line, (width / 2) - (font.width(line) / 2), y, 0xFFFFFF, true);
|
||||
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
|
||||
y += 9;
|
||||
}
|
||||
}
|
||||
|
||||
private Minecraft minecraft() {
|
||||
return Nullability.assertNonNull(minecraft);
|
||||
}
|
||||
}
|
||||
|
@@ -86,8 +86,6 @@ public final class OptionScreen extends Screen {
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
|
||||
// Render the actual texture.
|
||||
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
|
||||
graphics.blit(BACKGROUND,
|
||||
|
@@ -30,7 +30,6 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(graphics, mouseX, mouseY);
|
||||
}
|
||||
|
@@ -64,15 +64,15 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double x, double y, double delta) {
|
||||
if (super.mouseScrolled(x, y, delta)) return true;
|
||||
if (delta < 0) {
|
||||
public boolean mouseScrolled(double x, double y, double deltaX, double deltaY) {
|
||||
if (super.mouseScrolled(x, y, deltaX, deltaY)) return true;
|
||||
if (deltaX < 0) {
|
||||
// Scroll up goes to the next page
|
||||
if (page < pages - 1) page++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (delta > 0) {
|
||||
if (deltaX > 0) {
|
||||
// Scroll down goes to the previous page
|
||||
if (page > 0) page--;
|
||||
return true;
|
||||
@@ -91,14 +91,12 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
}
|
||||
|
||||
@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.
|
||||
graphics.pose().pushPose();
|
||||
graphics.pose().translate(0, 0, -1);
|
||||
renderBackground(graphics);
|
||||
super.renderBackground(graphics, mouseX, mouseY, partialTicks);
|
||||
graphics.pose().popPose();
|
||||
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -43,6 +43,10 @@ public class DynamicImageButton extends Button {
|
||||
|
||||
@Override
|
||||
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());
|
||||
|
||||
RenderSystem.disableDepthTest();
|
||||
@@ -50,14 +54,6 @@ public class DynamicImageButton extends Button {
|
||||
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 HintedMessage(Component message, @Nullable Component hint) {
|
||||
this(
|
||||
|
@@ -195,7 +195,7 @@ public class TerminalWidget extends AbstractWidget {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double delta, double deltaY) {
|
||||
if (!inTermRegion(mouseX, mouseY)) return false;
|
||||
if (!hasMouseSupport() || delta == 0) return false;
|
||||
|
||||
|
@@ -17,7 +17,6 @@ import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -28,6 +27,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -49,7 +49,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMonitorData(BlockPos pos, @Nullable TerminalState terminal) {
|
||||
public void handleMonitorData(BlockPos pos, TerminalState terminal) {
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (player == null) return;
|
||||
|
||||
@@ -67,17 +67,19 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, @Nullable TerminalState terminal) {
|
||||
ClientPocketComputers.setState(instanceId, state, lightState, terminal);
|
||||
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
||||
computer.setState(state, lightState);
|
||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerDeleted(UUID instanceId) {
|
||||
public void handlePocketComputerDeleted(int instanceId) {
|
||||
ClientPocketComputers.remove(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio buffer) {
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
|
||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,7 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ServerGamePacketListener;
|
||||
import net.minecraft.network.protocol.common.ServerCommonPacketListener;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -27,7 +27,7 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
||||
* @param message The messsge to convert.
|
||||
* @return The converted message.
|
||||
*/
|
||||
Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
|
||||
Packet<ServerCommonPacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
|
||||
|
||||
/**
|
||||
* Render a {@link BakedModel}, using any loader-specific hooks.
|
||||
|
@@ -4,25 +4,21 @@
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
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.pocket.items.PocketComputerItem;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
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#getInstanceUUID()} to locals {@link PocketComputerData}.
|
||||
* Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}.
|
||||
* <p>
|
||||
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
||||
*/
|
||||
public final class ClientPocketComputers {
|
||||
private static final Map<UUID, PocketComputerData> instances = new HashMap<>();
|
||||
private static final Int2ObjectMap<PocketComputerData> instances = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private ClientPocketComputers() {
|
||||
}
|
||||
@@ -31,29 +27,25 @@ public final class ClientPocketComputers {
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
public static void remove(UUID id) {
|
||||
public static void remove(int id) {
|
||||
instances.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of a pocket computer.
|
||||
* Get or create a pocket computer.
|
||||
*
|
||||
* @param instanceId The instance ID of the pocket computer.
|
||||
* @param state The computer state of the pocket computer.
|
||||
* @param lightColour The current colour of the modem light.
|
||||
* @param terminalData The current terminal contents.
|
||||
* @param instanceId The instance ID of the pocket computer.
|
||||
* @param advanced Whether this computer has an advanced terminal.
|
||||
* @return The pocket computer data.
|
||||
*/
|
||||
public static void setState(UUID instanceId, ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
|
||||
public static PocketComputerData get(int instanceId, boolean advanced) {
|
||||
var computer = instances.get(instanceId);
|
||||
if (computer == null) {
|
||||
instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData));
|
||||
} else {
|
||||
computer.setState(state, lightColour, terminalData);
|
||||
}
|
||||
if (computer == null) instances.put(instanceId, computer = new PocketComputerData(advanced));
|
||||
return computer;
|
||||
}
|
||||
|
||||
public static @Nullable PocketComputerData get(ItemStack stack) {
|
||||
var id = PocketComputerItem.getInstanceID(stack);
|
||||
return id == null ? null : instances.get(id);
|
||||
public static PocketComputerData get(ItemStack stack) {
|
||||
var family = stack.getItem() instanceof PocketComputerItem computer ? computer.getFamily() : ComputerFamily.NORMAL;
|
||||
return get(PocketComputerItem.getInstanceID(stack), family != ComputerFamily.NORMAL);
|
||||
}
|
||||
}
|
||||
|
@@ -4,13 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Clientside data about a pocket computer.
|
||||
* <p>
|
||||
@@ -21,22 +21,20 @@ import javax.annotation.Nullable;
|
||||
* @see ClientPocketComputers The registry which holds pocket computers.
|
||||
* @see PocketServerComputer The server-side pocket computer.
|
||||
*/
|
||||
public final class PocketComputerData {
|
||||
private @Nullable NetworkedTerminal terminal;
|
||||
private ComputerState state;
|
||||
private int lightColour;
|
||||
public class PocketComputerData {
|
||||
private final NetworkedTerminal terminal;
|
||||
private ComputerState state = ComputerState.OFF;
|
||||
private int lightColour = -1;
|
||||
|
||||
PocketComputerData(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
|
||||
this.state = state;
|
||||
this.lightColour = lightColour;
|
||||
if (terminalData != null) terminal = terminalData.create();
|
||||
public PocketComputerData(boolean colour) {
|
||||
terminal = new NetworkedTerminal(Config.pocketTermWidth, Config.pocketTermHeight, colour);
|
||||
}
|
||||
|
||||
public int getLightState() {
|
||||
return state != ComputerState.OFF ? lightColour : -1;
|
||||
}
|
||||
|
||||
public @Nullable NetworkedTerminal getTerminal() {
|
||||
public Terminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
@@ -44,16 +42,12 @@ public final class PocketComputerData {
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
|
||||
public void setState(ComputerState state, int lightColour) {
|
||||
this.state = state;
|
||||
this.lightColour = lightColour;
|
||||
}
|
||||
|
||||
if (terminalData != null) {
|
||||
if (terminal == null) {
|
||||
terminal = terminalData.create();
|
||||
} else {
|
||||
terminalData.apply(terminal);
|
||||
}
|
||||
}
|
||||
public void setTerminal(TerminalState state) {
|
||||
state.apply(terminal);
|
||||
}
|
||||
}
|
||||
|
@@ -17,8 +17,6 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
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.
|
||||
*
|
||||
@@ -37,7 +35,7 @@ public abstract class ItemMapLikeRenderer {
|
||||
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) {
|
||||
Player player = Objects.requireNonNull(Minecraft.getInstance().player);
|
||||
Player player = Minecraft.getInstance().player;
|
||||
|
||||
transform.pushPose();
|
||||
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {
|
||||
|
@@ -11,7 +11,6 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -33,16 +32,10 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
@Override
|
||||
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
var terminal = computer == null ? null : computer.getTerminal();
|
||||
var terminal = computer.getTerminal();
|
||||
|
||||
int termWidth, termHeight;
|
||||
if (terminal == null) {
|
||||
termWidth = Config.pocketTermWidth;
|
||||
termHeight = Config.pocketTermHeight;
|
||||
} else {
|
||||
termWidth = terminal.getWidth();
|
||||
termHeight = terminal.getHeight();
|
||||
}
|
||||
var termWidth = terminal.getWidth();
|
||||
var termHeight = terminal.getHeight();
|
||||
|
||||
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
||||
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||
@@ -67,15 +60,14 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
||||
|
||||
// Render the light
|
||||
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
var lightColour = ClientPocketComputers.get(stack).getLightState();
|
||||
if (lightColour == -1) lightColour = Colour.BLACK.getHex();
|
||||
renderLight(transform, bufferSource, lightColour, width, height);
|
||||
|
||||
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||
if (terminal == null) {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
|
||||
} else {
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||
}
|
||||
FixedWidthFontRenderer.drawTerminal(
|
||||
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL)),
|
||||
MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN
|
||||
);
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
@@ -125,10 +125,10 @@ public class SpriteRenderer {
|
||||
}
|
||||
|
||||
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) {
|
||||
return sprite.getV((double) y / height * 16);
|
||||
return sprite.getV((float) y / height);
|
||||
}
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
// Render the label
|
||||
var label = turtle.getLabel();
|
||||
var hit = renderer.cameraHitResult;
|
||||
if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
|
||||
if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
|
||||
var mc = Minecraft.getInstance();
|
||||
var font = this.font;
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import com.mojang.blaze3d.platform.MemoryTracker;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.client.FrameInfo;
|
||||
import dan200.computercraft.client.integration.ShaderMod;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
@@ -25,6 +26,7 @@ import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -58,9 +60,9 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
||||
@Override
|
||||
public void render(MonitorBlockEntity monitor, float partialTicks, PoseStack transform, MultiBufferSource bufferSource, int lightmapCoord, int overlayLight) {
|
||||
// Render from the origin monitor
|
||||
var originTerminal = monitor.getOriginClientMonitor();
|
||||
if (originTerminal == null) return;
|
||||
var originTerminal = monitor.getClientMonitor();
|
||||
|
||||
if (originTerminal == null) return;
|
||||
var origin = originTerminal.getOrigin();
|
||||
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
|
||||
var monitorPos = monitor.getBlockPos();
|
||||
@@ -255,6 +257,11 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
||||
return Config.monitorDistance;
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public AABB getRenderBoundingBox(MonitorBlockEntity monitor) {
|
||||
return monitor.getRenderBoundingBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any monitors were rendered this frame.
|
||||
*
|
||||
|
@@ -5,7 +5,6 @@
|
||||
package dan200.computercraft.client.sound;
|
||||
|
||||
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.SpeakerPosition;
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
@@ -37,7 +36,7 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The {@link Channel} which this sound is playing on.
|
||||
*
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
*/
|
||||
@Nullable
|
||||
Channel channel;
|
||||
@@ -45,23 +44,21 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The underlying {@link SoundEngine} executor.
|
||||
*
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
* @see SoundEngine#executor
|
||||
*/
|
||||
@Nullable
|
||||
Executor executor;
|
||||
|
||||
private int charge = 0; // q
|
||||
private int strength = 0; // s
|
||||
private int lowPassCharge;
|
||||
private boolean previousBit = false;
|
||||
|
||||
DfpwmStream() {
|
||||
}
|
||||
|
||||
void push(EncodedAudio audio) {
|
||||
var charge = audio.charge();
|
||||
var strength = audio.strength();
|
||||
var previousBit = audio.previousBit();
|
||||
var input = audio.audio();
|
||||
|
||||
void push(ByteBuffer input) {
|
||||
var readable = input.remaining();
|
||||
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
||||
|
||||
|
@@ -6,12 +6,12 @@ package dan200.computercraft.client.sound;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
||||
@@ -25,7 +25,7 @@ public class SpeakerInstance {
|
||||
SpeakerInstance() {
|
||||
}
|
||||
|
||||
private void pushAudio(EncodedAudio buffer) {
|
||||
private void pushAudio(ByteBuffer buffer) {
|
||||
var sound = this.sound;
|
||||
|
||||
var stream = currentStream;
|
||||
@@ -43,7 +43,7 @@ public class SpeakerInstance {
|
||||
}
|
||||
}
|
||||
|
||||
public void playAudio(SpeakerPosition position, float volume, EncodedAudio buffer) {
|
||||
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
|
||||
pushAudio(buffer);
|
||||
|
||||
var soundManager = Minecraft.getInstance().getSoundManager();
|
||||
|
@@ -10,6 +10,7 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,7 +41,7 @@ public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
|
||||
}
|
||||
|
||||
@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;
|
||||
if (turtle != null) {
|
||||
var turtleNBT = turtle.getUpgradeNBTData(side);
|
||||
|
@@ -10,15 +10,13 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
@@ -29,12 +27,10 @@ import java.util.stream.Stream;
|
||||
* A registry of {@link TurtleUpgradeModeller}s.
|
||||
*/
|
||||
public final class TurtleUpgradeModellers {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TurtleUpgradeModellers.class);
|
||||
|
||||
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());
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -48,15 +44,12 @@ public final class 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) {
|
||||
if (fetchedModels) {
|
||||
// TODO(1.20.4): Replace with an error.
|
||||
LOG.warn(
|
||||
"Turtle upgrade serialiser {} was registered too late, its models may not be loaded correctly. If you are " +
|
||||
"the mod author, you may be using a deprecated API - see https://github.com/cc-tweaked/CC-Tweaked/pull/1684 " +
|
||||
"for further information.",
|
||||
PlatformHelper.get().getRegistryKey(TurtleUpgradeSerialiser.registryId(), serialiser)
|
||||
);
|
||||
throw new IllegalStateException(String.format(
|
||||
"Turtle upgrade serialiser %s must be registered before models are baked.",
|
||||
RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.serialiserRegistryKey()), serialiser)
|
||||
));
|
||||
}
|
||||
|
||||
if (turtleModels.putIfAbsent(serialiser, modeller) != null) {
|
||||
@@ -67,13 +60,13 @@ public final class TurtleUpgradeModellers {
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
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) {
|
||||
@SuppressWarnings("unchecked")
|
||||
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) {
|
||||
|
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"parent": "computercraft:block/turtle_base",
|
||||
"textures": {
|
||||
"back": "computercraft:block/turtle_advanced_back",
|
||||
"backpack": "computercraft:block/turtle_advanced_backpack",
|
||||
"bottom": "computercraft:block/turtle_advanced_bottom",
|
||||
"front": "computercraft:block/turtle_advanced_front",
|
||||
"left": "computercraft:block/turtle_advanced_left",
|
||||
"right": "computercraft:block/turtle_advanced_right",
|
||||
"top": "computercraft:block/turtle_advanced_top"
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"parent": "computercraft:block/turtle_base",
|
||||
"textures": {
|
||||
"back": "computercraft:block/turtle_normal_back",
|
||||
"backpack": "computercraft:block/turtle_normal_backpack",
|
||||
"bottom": "computercraft:block/turtle_normal_bottom",
|
||||
"front": "computercraft:block/turtle_normal_front",
|
||||
"left": "computercraft:block/turtle_normal_left",
|
||||
"right": "computercraft:block/turtle_normal_right",
|
||||
"top": "computercraft:block/turtle_normal_top"
|
||||
}
|
||||
}
|
@@ -38,9 +38,6 @@ import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
|
||||
|
||||
class BlockModelProvider {
|
||||
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
|
||||
private static final TextureSlot LEFT = TextureSlot.create("left");
|
||||
private static final TextureSlot RIGHT = TextureSlot.create("right");
|
||||
private static final TextureSlot BACKPACK = TextureSlot.create("backpack");
|
||||
|
||||
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
|
||||
@@ -61,7 +58,7 @@ class BlockModelProvider {
|
||||
private static final ModelTemplate TURTLE = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_base")),
|
||||
Optional.empty(),
|
||||
TextureSlot.FRONT, TextureSlot.BACK, TextureSlot.TOP, TextureSlot.BOTTOM, LEFT, RIGHT, BACKPACK
|
||||
TextureSlot.TEXTURE
|
||||
);
|
||||
private static final ModelTemplate TURTLE_UPGRADE_LEFT = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_upgrade_base_left")),
|
||||
@@ -170,16 +167,7 @@ class BlockModelProvider {
|
||||
}
|
||||
|
||||
private static void registerTurtle(BlockModelGenerators generators, TurtleBlock block) {
|
||||
var model = TURTLE.create(block, new TextureMapping()
|
||||
.put(TextureSlot.FRONT, getBlockTexture(block, "_front"))
|
||||
.put(TextureSlot.BACK, getBlockTexture(block, "_back"))
|
||||
.put(TextureSlot.TOP, getBlockTexture(block, "_top"))
|
||||
.put(TextureSlot.BOTTOM, getBlockTexture(block, "_bottom"))
|
||||
.put(LEFT, getBlockTexture(block, "_left"))
|
||||
.put(RIGHT, getBlockTexture(block, "_right"))
|
||||
.put(BACKPACK, getBlockTexture(block, "_backpack")),
|
||||
generators.modelOutput
|
||||
);
|
||||
var model = TURTLE.create(block, TextureMapping.defaultTexture(block), generators.modelOutput);
|
||||
generators.blockStateOutput.accept(
|
||||
MultiVariantGenerator.multiVariant(block, Variant.variant().with(VariantProperties.MODEL, model))
|
||||
.with(createHorizontalFacingDispatch())
|
||||
|
@@ -7,13 +7,16 @@ package dan200.computercraft.data;
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||
import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -33,9 +36,9 @@ public final class DataProviders {
|
||||
var blockTags = generator.blockTags(TagProvider::blockTags);
|
||||
generator.itemTags(TagProvider::itemTags, blockTags);
|
||||
|
||||
generator.add(out -> new net.minecraft.data.loot.LootTableProvider(out, Set.of(), LootTableProvider.getTables()));
|
||||
generator.lootTable(LootTableProvider.getTables());
|
||||
|
||||
generator.add(out -> new ModelProvider(out, BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels));
|
||||
generator.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels);
|
||||
|
||||
generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades));
|
||||
|
||||
@@ -54,8 +57,12 @@ public final class DataProviders {
|
||||
|
||||
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
|
||||
|
||||
void lootTable(List<SubProviderEntry> tables);
|
||||
|
||||
TagsProvider<Block> blockTags(Consumer<TagProvider.TagConsumer<Block>> tags);
|
||||
|
||||
TagsProvider<Item> itemTags(Consumer<TagProvider.ItemTagConsumer> tags, TagsProvider<Block> blocks);
|
||||
|
||||
void models(Consumer<BlockModelGenerators> blocks, Consumer<ItemModelGenerators> items);
|
||||
}
|
||||
}
|
||||
|
@@ -13,18 +13,16 @@ import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.core.metrics.Metric;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
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.AggregatedMetric;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
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.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -167,19 +165,10 @@ public final class LanguageProvider implements DataProvider {
|
||||
add("commands.computercraft.generic.exception", "Unhandled exception (%s)");
|
||||
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.many_matching", "Multiple computers matching '%s' (instances %s)");
|
||||
add("argument.computercraft.tracking_field.no_field", "Unknown field '%s'");
|
||||
add("argument.computercraft.argument_expected", "Argument expected");
|
||||
add("argument.computercraft.unknown_computer_family", "Unknown computer family '%s'");
|
||||
|
||||
// Metrics
|
||||
add(Metrics.COMPUTER_TASKS, "Tasks");
|
||||
@@ -282,18 +271,17 @@ public final class LanguageProvider implements DataProvider {
|
||||
|
||||
private Stream<String> getExpectedKeys() {
|
||||
return Stream.of(
|
||||
RegistryWrappers.BLOCKS.stream()
|
||||
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(Block::getDescriptionId),
|
||||
RegistryWrappers.ITEMS.stream()
|
||||
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(Item::getDescriptionId),
|
||||
BuiltInRegistries.BLOCK.holders()
|
||||
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(x -> x.value().getDescriptionId()),
|
||||
BuiltInRegistries.ITEM.holders()
|
||||
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(x -> x.value().getDescriptionId()),
|
||||
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
||||
ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||
ComputerSelector.options().values().stream().map(ComputerSelector.Option::translationKey)
|
||||
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey)
|
||||
).flatMap(x -> x);
|
||||
}
|
||||
|
||||
|
@@ -5,8 +5,9 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
@@ -67,7 +68,7 @@ public class ModelProvider implements DataProvider {
|
||||
blocks.accept(new BlockModelGenerators(addBlockState, addModel, explicitItems::add));
|
||||
items.accept(new ItemModelGenerators(addModel));
|
||||
|
||||
for (var block : RegistryWrappers.BLOCKS) {
|
||||
for (var block : BuiltInRegistries.BLOCK) {
|
||||
if (!blockStates.containsKey(block)) continue;
|
||||
|
||||
var item = Item.BY_BLOCK.get(block);
|
||||
@@ -80,7 +81,7 @@ public class ModelProvider implements DataProvider {
|
||||
}
|
||||
|
||||
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);
|
||||
return Util.sequenceFailFast(futures);
|
||||
}
|
||||
|
@@ -5,8 +5,9 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
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.resources.ResourceLocation;
|
||||
|
||||
@@ -21,7 +22,7 @@ class PocketUpgradeProvider extends PocketUpgradeDataProvider {
|
||||
}
|
||||
|
||||
@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()));
|
||||
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);
|
||||
|
@@ -5,34 +5,56 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
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.common.IColouredItem;
|
||||
import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||
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.RecipeIngredients;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
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.recipes.TurtleOverlayRecipe;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||
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.ItemPredicate;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
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.NbtUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
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.ShapedRecipe;
|
||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
@@ -43,7 +65,7 @@ import java.util.function.Consumer;
|
||||
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
||||
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 TurtleUpgradeDataProvider turtleUpgrades;
|
||||
private final PocketUpgradeDataProvider pocketUpgrades;
|
||||
@@ -55,40 +77,37 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildRecipes(Consumer<FinishedRecipe> add) {
|
||||
public void buildRecipes(RecipeOutput add) {
|
||||
basicRecipes(add);
|
||||
diskColours(add);
|
||||
pocketUpgrades(add);
|
||||
turtleUpgrades(add);
|
||||
turtleOverlays(add);
|
||||
|
||||
addSpecial(add, ModRegistry.RecipeSerializers.PRINTOUT.get());
|
||||
addSpecial(add, ModRegistry.RecipeSerializers.DISK.get());
|
||||
addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM.get());
|
||||
addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM_CLEAR.get());
|
||||
addSpecial(add, ModRegistry.RecipeSerializers.TURTLE_UPGRADE.get());
|
||||
addSpecial(add, ModRegistry.RecipeSerializers.POCKET_COMPUTER_UPGRADE.get());
|
||||
addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new ClearColourRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new TurtleUpgradeRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new PocketComputerUpgradeRecipe(CraftingBookCategory.MISC));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
ShapelessRecipeBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.DISK.get())
|
||||
ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, DiskItem.createFromIDAndColour(-1, null, colour.getHex()))
|
||||
.requires(ingredients.redstone())
|
||||
.requires(Items.PAPER)
|
||||
.requires(DyeItem.byColor(ofColour(colour)))
|
||||
.group("computercraft:disk")
|
||||
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add)
|
||||
.withResultTag(x -> x.putInt(IColouredItem.NBT_COLOUR, colour.getHex())),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1))
|
||||
);
|
||||
.build(ImpostorShapelessRecipe::new)
|
||||
.save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,23 +120,22 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
*
|
||||
* @param add The callback to add recipes.
|
||||
*/
|
||||
private void turtleUpgrades(Consumer<FinishedRecipe> add) {
|
||||
private void turtleUpgrades(RecipeOutput add) {
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
|
||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||
|
||||
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
||||
var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null);
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null))
|
||||
.group(name.toString())
|
||||
.pattern("#T")
|
||||
.define('T', base.getItem())
|
||||
.define('#', upgrade.getCraftingItem().getItem())
|
||||
.unlockedBy("has_items",
|
||||
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||
.unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||
.build(ImpostorShapedRecipe::new)
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
|
||||
add,
|
||||
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
|
||||
);
|
||||
}
|
||||
@@ -133,31 +151,30 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
*
|
||||
* @param add The callback to add recipes.
|
||||
*/
|
||||
private void pocketUpgrades(Consumer<FinishedRecipe> add) {
|
||||
private void pocketUpgrades(RecipeOutput add) {
|
||||
for (var pocket : pocketComputerItems()) {
|
||||
var base = pocket.create(-1, null, -1, null);
|
||||
var name = RegistryWrappers.ITEMS.getKey(pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
|
||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
|
||||
|
||||
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
|
||||
var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade));
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade)))
|
||||
.group(name.toString())
|
||||
.pattern("#")
|
||||
.pattern("P")
|
||||
.define('P', base.getItem())
|
||||
.define('#', upgrade.getCraftingItem().getItem())
|
||||
.unlockedBy("has_items",
|
||||
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||
.unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||
.build(ImpostorShapedRecipe::new)
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
|
||||
add,
|
||||
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void turtleOverlays(Consumer<FinishedRecipe> add) {
|
||||
private void turtleOverlays(RecipeOutput add) {
|
||||
turtleOverlay(add, "turtle_trans_overlay", x -> x
|
||||
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
|
||||
.requires(ColourUtils.getDyeTag(DyeColor.LIGHT_BLUE))
|
||||
@@ -178,28 +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()) {
|
||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
|
||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||
|
||||
var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem())
|
||||
var builder = ShapelessSpecBuilder.shapeless(RecipeCategory.REDSTONE, base)
|
||||
.group(name.withSuffix("_overlay").toString())
|
||||
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
|
||||
build.accept(builder);
|
||||
builder
|
||||
.requires(base.getItem())
|
||||
.save(
|
||||
RecipeWrapper
|
||||
.wrap(ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get(), add)
|
||||
.withExtraData(x -> x.addProperty("overlay", new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay).toString())),
|
||||
name.withSuffix("_overlays/" + overlay)
|
||||
);
|
||||
.build(s -> new TurtleOverlayRecipe(s, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay)))
|
||||
.save(add, name.withSuffix("_overlays/" + overlay));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void basicRecipes(Consumer<FinishedRecipe> add) {
|
||||
private void basicRecipes(RecipeOutput add) {
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.CABLE.get(), 6)
|
||||
.pattern(" # ")
|
||||
@@ -233,7 +246,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ingredients.redstone()), itemPredicate(ingredients.goldIngot())))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
@@ -241,10 +254,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.define('#', ingredients.goldIngot())
|
||||
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")
|
||||
);
|
||||
.build(ComputerUpgradeRecipe::new)
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
|
||||
@@ -257,7 +268,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
@@ -266,9 +277,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||
.define('I', ingredients.woodenChest())
|
||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
|
||||
.buildOrThrow(TurtleRecipe::of)
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
@@ -277,9 +289,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||
.define('I', ingredients.woodenChest())
|
||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
|
||||
.buildOrThrow(TurtleRecipe::of)
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
@@ -288,10 +301,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.define('C', ModRegistry.Items.TURTLE_NORMAL.get())
|
||||
.define('B', ingredients.goldBlock())
|
||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")
|
||||
);
|
||||
.build(ComputerUpgradeRecipe::new)
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
|
||||
@@ -347,7 +358,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.unlockedBy("has_apple", inventoryChange(Items.GOLDEN_APPLE))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
@@ -355,10 +366,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.define('#', ingredients.goldIngot())
|
||||
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
|
||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")
|
||||
);
|
||||
.build(ComputerUpgradeRecipe::new)
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
|
||||
@@ -425,57 +434,53 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get()))
|
||||
.save(add);
|
||||
|
||||
ShapelessRecipeBuilder
|
||||
.shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD)
|
||||
ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.DECORATIONS, playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c"))
|
||||
.requires(ingredients.head())
|
||||
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
|
||||
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
|
||||
.withResultTag(playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c")),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy")
|
||||
);
|
||||
.build(CustomShapelessRecipe::new)
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy"));
|
||||
|
||||
ShapelessRecipeBuilder
|
||||
.shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD)
|
||||
ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.DECORATIONS, playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb"))
|
||||
.requires(ingredients.head())
|
||||
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
|
||||
.withResultTag(playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb")),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200")
|
||||
);
|
||||
.build(CustomShapelessRecipe::new)
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
|
||||
|
||||
ShapelessRecipeBuilder
|
||||
ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get())
|
||||
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 2)
|
||||
.requires(ingredients.string())
|
||||
.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())
|
||||
.requires(ingredients.leather())
|
||||
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
|
||||
.requires(ingredients.string())
|
||||
.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) {
|
||||
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));
|
||||
}
|
||||
|
||||
private static InventoryChangeTrigger.TriggerInstance inventoryChange(ItemLike... stack) {
|
||||
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(ItemLike... 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);
|
||||
}
|
||||
|
||||
@@ -488,11 +493,12 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
|
||||
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 (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")) {
|
||||
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
|
||||
} else {
|
||||
@@ -500,15 +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 tag = new CompoundTag();
|
||||
tag.put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
|
||||
return tag;
|
||||
item.getOrCreateTag().put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
|
||||
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
|
||||
private static void addSpecial(RecipeOutput add, Recipe<?> recipe) {
|
||||
add.accept(RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, recipe.getSerializer()), recipe, null);
|
||||
}
|
||||
}
|
||||
|
@@ -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,9 +5,9 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.integration.ExternalModTags;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.tags.ItemTagsProvider;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
@@ -82,12 +82,6 @@ class TagProvider {
|
||||
);
|
||||
|
||||
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) {
|
||||
@@ -118,9 +112,9 @@ class TagProvider {
|
||||
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) {
|
||||
builder.addElement(registry.getKey(object));
|
||||
builder.addElement(RegistryHelper.getKeyOrThrow(registry, object));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -6,8 +6,9 @@ package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.ComputerCraftTags.Blocks;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
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.resources.ResourceLocation;
|
||||
|
||||
@@ -22,7 +23,7 @@ class TurtleUpgradeProvider extends TurtleUpgradeDataProvider {
|
||||
}
|
||||
|
||||
@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(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);
|
||||
|
@@ -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.wired.WiredElement;
|
||||
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.turtle.ITurtleUpgrade;
|
||||
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.impl.detail.DetailRegistryImpl;
|
||||
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<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<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_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<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) {
|
||||
var manager = server.getResourceManager();
|
||||
@@ -116,12 +117,12 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId() {
|
||||
public final ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> turtleUpgradeRegistryId() {
|
||||
return turtleUpgradeRegistryId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId() {
|
||||
public final ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> pocketUpgradeRegistryId() {
|
||||
return pocketUpgradeRegistryId;
|
||||
}
|
||||
|
||||
|
@@ -16,20 +16,18 @@ import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
private static final GenericPeripheralProvider<Runnable> genericProvider = new GenericPeripheralProvider<>();
|
||||
private static final GenericPeripheralProvider genericProvider = new GenericPeripheralProvider();
|
||||
|
||||
private Peripherals() {
|
||||
}
|
||||
|
||||
public static void addGenericLookup(ComponentLookup<? super Runnable> lookup) {
|
||||
public static void addGenericLookup(ComponentLookup lookup) {
|
||||
genericProvider.registerLookup(lookup);
|
||||
}
|
||||
|
||||
public static @Nullable IPeripheral getGenericPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, Runnable invalidate) {
|
||||
return genericProvider.getPeripheral(level, pos, side, blockEntity, invalidate);
|
||||
public static @Nullable IPeripheral getGenericPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) {
|
||||
return genericProvider.getPeripheral(level, pos, side, blockEntity);
|
||||
}
|
||||
}
|
@@ -6,19 +6,18 @@ package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PocketUpgrades {
|
||||
private static final UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> registry = new UpgradeManager<>(
|
||||
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId()
|
||||
private static final UpgradeManager<IPocketUpgrade> registry = new UpgradeManager<>(
|
||||
"pocket computer upgrade", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey()
|
||||
);
|
||||
|
||||
private PocketUpgrades() {
|
||||
}
|
||||
|
||||
public static UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> instance() {
|
||||
public static UpgradeManager<IPocketUpgrade> instance() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
|
@@ -6,19 +6,18 @@ package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class TurtleUpgrades {
|
||||
private static final UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> registry = new UpgradeManager<>(
|
||||
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId()
|
||||
private static final UpgradeManager<ITurtleUpgrade> registry = new UpgradeManager<>(
|
||||
"turtle upgrade", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey()
|
||||
);
|
||||
|
||||
private TurtleUpgrades() {
|
||||
}
|
||||
|
||||
public static UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> instance() {
|
||||
public static UpgradeManager<ITurtleUpgrade> instance() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
|
@@ -31,27 +31,26 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* Manages turtle and pocket computer upgrades.
|
||||
*
|
||||
* @param <R> The type of upgrade serialisers.
|
||||
* @param <T> The type of upgrade.
|
||||
* @see TurtleUpgrades
|
||||
* @see PocketUpgrades
|
||||
*/
|
||||
public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends UpgradeBase> extends SimpleJsonResourceReloadListener {
|
||||
public class UpgradeManager<T extends UpgradeBase> extends SimpleJsonResourceReloadListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpgradeManager.class);
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
|
||||
public record UpgradeWrapper<R extends UpgradeSerialiser<? extends T>, T extends UpgradeBase>(
|
||||
String id, T upgrade, R serialiser, String modId
|
||||
public record UpgradeWrapper<T extends UpgradeBase>(
|
||||
String id, T upgrade, UpgradeSerialiser<? extends T> serialiser, String modId
|
||||
) {
|
||||
}
|
||||
|
||||
private final String kind;
|
||||
private final ResourceKey<Registry<R>> registry;
|
||||
private final ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry;
|
||||
|
||||
private Map<String, UpgradeWrapper<R, T>> current = Map.of();
|
||||
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Map.of();
|
||||
private Map<String, UpgradeWrapper<T>> current = Map.of();
|
||||
private Map<T, UpgradeWrapper<T>> currentWrappers = Map.of();
|
||||
|
||||
public UpgradeManager(String kind, String path, ResourceKey<Registry<R>> registry) {
|
||||
public UpgradeManager(String kind, String path, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry) {
|
||||
super(GSON, path);
|
||||
this.kind = kind;
|
||||
this.registry = registry;
|
||||
@@ -64,7 +63,7 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public UpgradeWrapper<R, T> getWrapper(T upgrade) {
|
||||
public UpgradeWrapper<T> getWrapper(T upgrade) {
|
||||
return currentWrappers.get(upgrade);
|
||||
}
|
||||
|
||||
@@ -92,16 +91,17 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
return currentWrappers.keySet();
|
||||
}
|
||||
|
||||
public Map<String, UpgradeWrapper<R, T>> getUpgradeWrappers() {
|
||||
public Map<String, UpgradeWrapper<T>> getUpgradeWrappers() {
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(Map<ResourceLocation, JsonElement> upgrades, ResourceManager manager, ProfilerFiller profiler) {
|
||||
Map<String, UpgradeWrapper<R, T>> newUpgrades = new HashMap<>();
|
||||
var registry = RegistryHelper.getRegistry(this.registry);
|
||||
Map<String, UpgradeWrapper<T>> newUpgrades = new HashMap<>();
|
||||
for (var element : upgrades.entrySet()) {
|
||||
try {
|
||||
loadUpgrade(newUpgrades, element.getKey(), element.getValue());
|
||||
loadUpgrade(registry, newUpgrades, element.getKey(), element.getValue());
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
LOG.error("Error loading {} {} from JSON file", kind, element.getKey(), e);
|
||||
}
|
||||
@@ -112,12 +112,12 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
LOG.info("Loaded {} {}s", current.size(), kind);
|
||||
}
|
||||
|
||||
private void loadUpgrade(Map<String, UpgradeWrapper<R, T>> current, ResourceLocation id, JsonElement json) {
|
||||
private void loadUpgrade(Registry<UpgradeSerialiser<? extends T>> registry, Map<String, UpgradeWrapper<T>> current, ResourceLocation id, JsonElement json) {
|
||||
var root = GsonHelper.convertToJsonObject(json, "top element");
|
||||
if (!PlatformHelper.get().shouldLoadResource(root)) return;
|
||||
|
||||
var serialiserId = new ResourceLocation(GsonHelper.getAsString(root, "type"));
|
||||
var serialiser = PlatformHelper.get().tryGetRegistryObject(registry, serialiserId);
|
||||
var serialiser = registry.get(serialiserId);
|
||||
if (serialiser == null) throw new JsonSyntaxException("Unknown upgrade type '" + serialiserId + "'");
|
||||
|
||||
// TODO: Can we track which mod this resource came from and use that instead? It's theoretically possible,
|
||||
@@ -130,11 +130,11 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
throw new IllegalArgumentException("Upgrade " + id + " from " + serialiser + " was incorrectly given id " + upgrade.getUpgradeID());
|
||||
}
|
||||
|
||||
var result = new UpgradeWrapper<R, T>(id.toString(), upgrade, serialiser, modId);
|
||||
var result = new UpgradeWrapper<T>(id.toString(), upgrade, serialiser, modId);
|
||||
current.put(result.id(), result);
|
||||
}
|
||||
|
||||
public void loadFromNetwork(Map<String, UpgradeWrapper<R, T>> newUpgrades) {
|
||||
public void loadFromNetwork(Map<String, UpgradeWrapper<T>> newUpgrades) {
|
||||
current = Collections.unmodifiableMap(newUpgrades);
|
||||
currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x));
|
||||
}
|
||||
|
@@ -4,66 +4,45 @@
|
||||
|
||||
package dan200.computercraft.impl.network.wired;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Verifies certain elements of a network are well-formed.
|
||||
* Verifies certain elements of a network are "well formed".
|
||||
* <p>
|
||||
* This adds substantial overhead to network modification, and so is only enabled when assertions are enabled.
|
||||
* This adds substantial overhead to network modification, and so should only be enabled
|
||||
* in a development environment.
|
||||
*/
|
||||
final class InvariantChecker {
|
||||
public final class InvariantChecker {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InvariantChecker.class);
|
||||
private static final boolean ENABLED = false;
|
||||
|
||||
private InvariantChecker() {
|
||||
}
|
||||
|
||||
static void checkNode(WiredNodeImpl node) {
|
||||
assert checkNodeImpl(node) : "Node invariants failed. See logs.";
|
||||
}
|
||||
public static void checkNode(WiredNodeImpl node) {
|
||||
if (!ENABLED) return;
|
||||
|
||||
private static boolean checkNodeImpl(WiredNodeImpl node) {
|
||||
var okay = true;
|
||||
|
||||
if (node.currentSet != null) {
|
||||
okay = false;
|
||||
LOG.error("{}: currentSet was not cleared.", node);
|
||||
var network = node.network;
|
||||
if (network == null) {
|
||||
LOG.error("Node's network is null", new Exception());
|
||||
return;
|
||||
}
|
||||
|
||||
var network = makeNullable(node.network);
|
||||
if (network == null) {
|
||||
okay = false;
|
||||
LOG.error("{}: Node's network is null.", node);
|
||||
} else if (makeNullable(network.nodes) == null || !network.nodes.contains(node)) {
|
||||
okay = false;
|
||||
LOG.error("{}: Node's network does not contain node.", node);
|
||||
if (network.nodes == null || !network.nodes.contains(node)) {
|
||||
LOG.error("Node's network does not contain node", new Exception());
|
||||
}
|
||||
|
||||
for (var neighbour : node.neighbours) {
|
||||
if (!neighbour.neighbours.contains(node)) {
|
||||
okay = false;
|
||||
LOG.error("{}: Neighbour {}'s neighbour set does not contain origianl node.", node, neighbour);
|
||||
LOG.error("Neighbour is missing node", new Exception());
|
||||
}
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
static void checkNetwork(WiredNetworkImpl network) {
|
||||
assert checkNetworkImpl(network) : "Network invariants failed. See logs.";
|
||||
}
|
||||
public static void checkNetwork(WiredNetworkImpl network) {
|
||||
if (!ENABLED) return;
|
||||
|
||||
private static boolean checkNetworkImpl(WiredNetworkImpl network) {
|
||||
var okay = true;
|
||||
for (var node : network.nodes) okay &= checkNodeImpl(node);
|
||||
return okay;
|
||||
}
|
||||
|
||||
@Contract("")
|
||||
private static <T> @Nullable T makeNullable(T object) {
|
||||
return object;
|
||||
for (var node : network.nodes) checkNode(node);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user