mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-22 17:37:38 +00:00
Compare commits
94 Commits
v1.20.1-1.
...
v1.20.1-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b27526bd21 | ||
![]() |
cb25f6c08a | ||
![]() |
d38b1d04e7 | ||
![]() |
9ccee75a99 | ||
![]() |
359c8d6652 | ||
![]() |
1788afacfc | ||
![]() |
f695f22d8a | ||
![]() |
bc03090ca4 | ||
![]() |
a617d0d566 | ||
![]() |
36599b321e | ||
![]() |
1d6e3f4fc0 | ||
![]() |
30dc4cb38c | ||
![]() |
be4512d1c3 | ||
![]() |
e6ee292850 | ||
![]() |
9d36f72bad | ||
![]() |
b5923c4462 | ||
![]() |
4d1e689719 | ||
![]() |
9d4af07568 | ||
![]() |
89294f4a22 | ||
![]() |
133b51b092 | ||
![]() |
272010e945 | ||
![]() |
e0889c613a | ||
![]() |
f115d43d07 | ||
![]() |
8be6b1b772 | ||
![]() |
104d5e70de | ||
![]() |
e3bda2f763 | ||
![]() |
234f69e8e5 | ||
![]() |
ed3a17f9b9 | ||
![]() |
0349c2b1f9 | ||
![]() |
03f9e6bd6d | ||
![]() |
9d8c933a14 | ||
![]() |
78bb3da58c | ||
![]() |
39a5e40c92 | ||
![]() |
763ba51919 | ||
![]() |
cf6ec8c28f | ||
![]() |
95d3b646b2 | ||
![]() |
488f66eead | ||
![]() |
1f7d245876 | ||
![]() |
af12b3a0ea | ||
![]() |
eb3e8ba677 | ||
![]() |
2043939531 | ||
![]() |
84a799d27a | ||
![]() |
fe826f5c9c | ||
![]() |
f8b7422294 | ||
![]() |
b343c01216 | ||
![]() |
76968f2f28 | ||
![]() |
1d365f5a0b | ||
![]() |
7b240cbf7e | ||
![]() |
d272a327c7 | ||
![]() |
0c0556a5bc | ||
![]() |
87345c6b2e | ||
![]() |
784e623776 | ||
![]() |
bcb3e9bd53 | ||
![]() |
c30bffbd0f | ||
![]() |
91c41856c5 | ||
![]() |
18c9723308 | ||
![]() |
aee382ed70 | ||
![]() |
6656da5877 | ||
![]() |
09e521727f | ||
![]() |
cab66a2d6e | ||
![]() |
8eabd4f303 | ||
![]() |
e3ced84885 | ||
![]() |
0929ab577d | ||
![]() |
2228733abc | ||
![]() |
e67c94d1bd | ||
![]() |
ae5a661a47 | ||
![]() |
0ff58cdc3e | ||
![]() |
1747c74770 | ||
![]() |
71669cf49c | ||
![]() |
bd327e37eb | ||
![]() |
bdce9a8170 | ||
![]() |
7e5598d084 | ||
![]() |
440fca6535 | ||
![]() |
6635edd35c | ||
![]() |
93ad40efbb | ||
![]() |
27dc8b5b2c | ||
![]() |
3ebdf7ef5e | ||
![]() |
905d4cb091 | ||
![]() |
e7ab05d064 | ||
![]() |
6ec34b42e5 | ||
![]() |
ab785a0906 | ||
![]() |
4541decd40 | ||
![]() |
747a5a53b4 | ||
![]() |
c0643fadca | ||
![]() |
0a31de43c2 | ||
![]() |
96b6947ef2 | ||
![]() |
e7a1065bfc | ||
![]() |
663eecff0c | ||
![]() |
e6125bcf60 | ||
![]() |
0d6c6e7ae7 | ||
![]() |
ae71eb3cae | ||
![]() |
3188197447 | ||
![]() |
6c8b391dab | ||
![]() |
b1248e4901 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
/logs
|
||||
/build
|
||||
/projects/*/logs
|
||||
/projects/fabric/fabricloader.log
|
||||
/projects/*/build
|
||||
/buildSrc/build
|
||||
/out
|
||||
|
@@ -44,7 +44,7 @@ repos:
|
||||
name: Check Java codestyle
|
||||
files: ".*\\.java$"
|
||||
language: system
|
||||
entry: ./gradlew checkstyleMain checkstyleTest
|
||||
entry: ./gradlew checkstyle
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
- id: illuaminate
|
||||
|
29
.reuse/dep5
29
.reuse/dep5
@@ -10,8 +10,8 @@ Files:
|
||||
projects/common/src/testMod/resources/data/cctest/structures/*
|
||||
projects/fabric/src/generated/*
|
||||
projects/forge/src/generated/*
|
||||
projects/web/src/export/index.json
|
||||
projects/web/src/export/items/minecraft/*
|
||||
projects/web/src/htmlTransform/export/index.json
|
||||
projects/web/src/htmlTransform/export/items/minecraft/*
|
||||
Comment: Generated/data files are CC0.
|
||||
Copyright: The CC: Tweaked Developers
|
||||
License: CC0-1.0
|
||||
@@ -37,10 +37,10 @@ Files:
|
||||
projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
|
||||
projects/fabric/src/testMod/resources/fabric.mod.json
|
||||
projects/forge/src/client/resources/computercraft-client.forge.mixins.json
|
||||
projects/web/src/mount/.settings
|
||||
projects/web/src/mount/example.nfp
|
||||
projects/web/src/mount/example.nft
|
||||
projects/web/src/mount/expr_template.lua
|
||||
projects/web/src/frontend/mount/.settings
|
||||
projects/web/src/frontend/mount/example.nfp
|
||||
projects/web/src/frontend/mount/example.nft
|
||||
projects/web/src/frontend/mount/expr_template.lua
|
||||
projects/web/tsconfig.json
|
||||
Comment: Several assets where it's inconvenient to create a .license file.
|
||||
Copyright: The CC: Tweaked Developers
|
||||
@@ -53,19 +53,32 @@ Files:
|
||||
projects/common/src/main/resources/assets/computercraft/textures/*
|
||||
projects/common/src/main/resources/pack.mcmeta
|
||||
projects/common/src/main/resources/pack.png
|
||||
projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
||||
projects/web/src/export/items/computercraft/*
|
||||
projects/web/src/htmlTransform/export/items/computercraft/*
|
||||
Comment: Bulk-license original assets as CCPL.
|
||||
Copyright: 2011 Daniel Ratcliffe
|
||||
License: LicenseRef-CCPL
|
||||
|
||||
Files:
|
||||
projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
|
||||
projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
|
||||
projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
|
||||
projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
|
||||
projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
|
||||
projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
|
||||
projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
|
||||
Comment: Community-contributed license files
|
||||
Copyright: 2017 The CC: Tweaked Developers
|
||||
License: LicenseRef-CCPL
|
||||
|
||||
Files:
|
||||
projects/common/src/main/resources/assets/computercraft/lang/*
|
||||
Comment: Community-contributed license files
|
||||
Copyright: 2017 The CC: Tweaked Developers
|
||||
License: LicenseRef-CCPL
|
||||
License: MPL-2.0
|
||||
|
||||
Files:
|
||||
.github/*
|
||||
|
@@ -100,7 +100,6 @@ about how you can build on that until you've covered everything!
|
||||
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
||||
[community]: README.md#community "Get in touch with the community."
|
||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
||||
[checkstyle]: https://checkstyle.org/
|
||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
||||
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
|
||||
|
@@ -42,7 +42,6 @@ repositories {
|
||||
url "https://squiddev.cc/maven/"
|
||||
content {
|
||||
includeGroup("cc.tweaked")
|
||||
includeModule("org.squiddev", "Cobalt")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,8 +75,8 @@ minecraft {
|
||||
```
|
||||
|
||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||
subject to change at any point. If you depend on functionality outside the API, file an issue, and we can look into
|
||||
exposing more features.
|
||||
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
||||
an issue to let me know!
|
||||
|
||||
We bundle the API sources with the jar, so documentation should be easily viewable within your editor. Alternatively,
|
||||
the generated documentation [can be browsed online](https://tweaked.cc/javadoc/).
|
||||
|
@@ -2,13 +2,19 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import cc.tweaked.gradle.JUnitExt
|
||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||
import org.jetbrains.gradle.ext.compiler
|
||||
import org.jetbrains.gradle.ext.runConfigurations
|
||||
import org.jetbrains.gradle.ext.settings
|
||||
|
||||
plugins {
|
||||
publishing
|
||||
alias(libs.plugins.taskTree)
|
||||
alias(libs.plugins.githubRelease)
|
||||
alias(libs.plugins.gradleVersions)
|
||||
alias(libs.plugins.versionCatalogUpdate)
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
id("cc-tweaked")
|
||||
}
|
||||
@@ -38,6 +44,50 @@ githubRelease {
|
||||
|
||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||
|
||||
idea.project.settings.runConfigurations {
|
||||
register<JUnitExt>("Core Tests") {
|
||||
vmParameters = "-ea"
|
||||
moduleName = "${idea.project.name}.core.test"
|
||||
packageName = ""
|
||||
}
|
||||
|
||||
register<JUnitExt>("CraftOS Tests") {
|
||||
vmParameters = "-ea"
|
||||
moduleName = "${idea.project.name}.core.test"
|
||||
className = "dan200.computercraft.core.ComputerTestDelegate"
|
||||
}
|
||||
|
||||
register<JUnitExt>("CraftOS Tests (Fast)") {
|
||||
vmParameters = "-ea -Dcc.skip_keywords=slow"
|
||||
moduleName = "${idea.project.name}.core.test"
|
||||
className = "dan200.computercraft.core.ComputerTestDelegate"
|
||||
}
|
||||
|
||||
register<JUnitExt>("Common Tests") {
|
||||
vmParameters = "-ea"
|
||||
moduleName = "${idea.project.name}.common.test"
|
||||
packageName = ""
|
||||
}
|
||||
|
||||
register<JUnitExt>("Fabric Tests") {
|
||||
val fabricProject = evaluationDependsOn(":fabric")
|
||||
val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods
|
||||
.joinToString(File.pathSeparator + File.pathSeparator) { modSettings ->
|
||||
SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath }
|
||||
}
|
||||
|
||||
vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup"
|
||||
moduleName = "${idea.project.name}.fabric.test"
|
||||
packageName = ""
|
||||
}
|
||||
|
||||
register<JUnitExt>("Forge Tests") {
|
||||
vmParameters = "-ea"
|
||||
moduleName = "${idea.project.name}.forge.test"
|
||||
packageName = ""
|
||||
}
|
||||
}
|
||||
|
||||
idea.project.settings.compiler.javac {
|
||||
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
||||
// and errors. Loop through our source sets and find the appropriate flags.
|
||||
@@ -54,3 +104,9 @@ idea.project.settings.compiler.javac {
|
||||
}
|
||||
.toMap()
|
||||
}
|
||||
|
||||
versionCatalogUpdate {
|
||||
sortByKey.set(false)
|
||||
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
|
||||
keep { keepUnusedLibraries.set(true) }
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@
|
||||
plugins {
|
||||
`java-gradle-plugin`
|
||||
`kotlin-dsl`
|
||||
alias(libs.plugins.gradleVersions)
|
||||
alias(libs.plugins.versionCatalogUpdate)
|
||||
}
|
||||
|
||||
// Duplicated in settings.gradle.kts
|
||||
@@ -50,9 +52,9 @@ dependencies {
|
||||
implementation(libs.curseForgeGradle)
|
||||
implementation(libs.fabric.loom)
|
||||
implementation(libs.forgeGradle)
|
||||
implementation(libs.ideaExt)
|
||||
implementation(libs.librarian)
|
||||
implementation(libs.minotaur)
|
||||
implementation(libs.vineflower)
|
||||
implementation(libs.vanillaGradle)
|
||||
}
|
||||
|
||||
@@ -74,3 +76,9 @@ gradlePlugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
versionCatalogUpdate {
|
||||
sortByKey.set(false)
|
||||
keep { keepUnusedLibraries.set(true) }
|
||||
catalogFile.set(file("../gradle/libs.versions.toml"))
|
||||
}
|
||||
|
@@ -12,7 +12,6 @@ import cc.tweaked.gradle.MinecraftConfigurations
|
||||
plugins {
|
||||
`java-library`
|
||||
id("fabric-loom")
|
||||
id("io.github.juuxel.loom-vineflower")
|
||||
id("cc-tweaked.java-convention")
|
||||
}
|
||||
|
||||
|
@@ -57,7 +57,6 @@ repositories {
|
||||
|
||||
filter {
|
||||
includeGroup("cc.tweaked")
|
||||
includeModule("org.squiddev", "Cobalt")
|
||||
// Things we mirror
|
||||
includeGroup("commoble.morered")
|
||||
includeGroup("dev.architectury")
|
||||
@@ -66,6 +65,7 @@ repositories {
|
||||
includeGroup("me.shedaniel.cloth")
|
||||
includeGroup("me.shedaniel")
|
||||
includeGroup("mezz.jei")
|
||||
includeGroup("org.teavm")
|
||||
includeModule("com.terraformersmc", "modmenu")
|
||||
includeModule("me.lucko", "fabric-permissions-api")
|
||||
}
|
||||
@@ -76,6 +76,12 @@ dependencies {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
checkstyle(libs.findLibrary("checkstyle").get())
|
||||
|
||||
constraints {
|
||||
checkstyle("org.codehaus.plexus:plexus-container-default:2.1.1") {
|
||||
because("2.1.0 depends on deprecated Google collections module")
|
||||
}
|
||||
}
|
||||
|
||||
errorprone(libs.findLibrary("errorProne-core").get())
|
||||
errorprone(libs.findLibrary("nullAway").get())
|
||||
}
|
||||
@@ -99,7 +105,10 @@ sourceSets.all {
|
||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||
|
||||
check("NullAway", CheckSeverity.ERROR)
|
||||
option("NullAway:AnnotatedPackages", listOf("dan200.computercraft", "net.fabricmc.fabric.api").joinToString(","))
|
||||
option(
|
||||
"NullAway:AnnotatedPackages",
|
||||
listOf("dan200.computercraft", "cc.tweaked", "net.fabricmc.fabric.api").joinToString(","),
|
||||
)
|
||||
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
|
||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||
option("NullAway:CheckOptionalEmptiness")
|
||||
@@ -174,6 +183,12 @@ project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("checkstyle") {
|
||||
description = "Run Checkstyle on all sources"
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
dependsOn(tasks.withType(Checkstyle::class.java))
|
||||
}
|
||||
|
||||
spotless {
|
||||
encoding = StandardCharsets.UTF_8
|
||||
lineEndings = LineEnding.UNIX
|
||||
@@ -192,6 +207,7 @@ spotless {
|
||||
val ktlintConfig = mapOf(
|
||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
||||
"ktlint_standard_class-naming" to "disabled",
|
||||
"ktlint_standard_function-naming" to "disabled",
|
||||
"ij_kotlin_allow_trailing_comma" to "true",
|
||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
||||
)
|
||||
|
@@ -10,9 +10,11 @@ import org.gradle.api.GradleException
|
||||
import org.gradle.api.NamedDomainObjectProvider
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.attributes.TestSuiteType
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.reporting.ReportingExtension
|
||||
@@ -73,11 +75,17 @@ abstract class CCTweakedExtension(
|
||||
*/
|
||||
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
||||
|
||||
/**
|
||||
* Dependencies excluded from published artifacts.
|
||||
*/
|
||||
private val excludedDeps: ListProperty<Dependency> = project.objects.listProperty(Dependency::class.java)
|
||||
|
||||
/** All source sets referenced by this project. */
|
||||
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
||||
|
||||
init {
|
||||
sourceDirectories.finalizeValueOnRead()
|
||||
excludedDeps.finalizeValueOnRead()
|
||||
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
||||
}
|
||||
|
||||
@@ -173,7 +181,7 @@ abstract class CCTweakedExtension(
|
||||
}
|
||||
|
||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
||||
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
|
||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||
|
||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||
@@ -185,7 +193,7 @@ abstract class CCTweakedExtension(
|
||||
jacoco.applyTo(this)
|
||||
extensions.configure(JacocoTaskExtension::class.java) {
|
||||
includes = listOf("dan200.computercraft.*")
|
||||
classDumpDir = classDump
|
||||
classDumpDir = classDump.get().asFile
|
||||
|
||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
||||
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
||||
@@ -246,6 +254,20 @@ abstract class CCTweakedExtension(
|
||||
).resolve().single()
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude a dependency from being published in Maven.
|
||||
*/
|
||||
fun exclude(dep: Dependency) {
|
||||
excludedDeps.add(dep)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a [MavenDependencySpec].
|
||||
*/
|
||||
fun configureExcludes(spec: MavenDependencySpec) {
|
||||
for (dep in excludedDeps.get()) spec.exclude(dep)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
||||
private val IGNORED_USERS = setOf(
|
||||
|
@@ -9,6 +9,10 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.gradle.ext.IdeaExtPlugin
|
||||
import org.jetbrains.gradle.ext.runConfigurations
|
||||
import org.jetbrains.gradle.ext.settings
|
||||
|
||||
/**
|
||||
* Configures projects to match a shared configuration.
|
||||
@@ -21,6 +25,20 @@ class CCTweakedPlugin : Plugin<Project> {
|
||||
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
||||
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
||||
}
|
||||
|
||||
project.plugins.withType(IdeaExtPlugin::class.java) { extendIdea(project) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the [IdeaExtPlugin] plugin's `runConfiguration` container to also support [JUnitExt].
|
||||
*/
|
||||
private fun extendIdea(project: Project) {
|
||||
val ideaModel = project.extensions.findByName("idea") as IdeaModel? ?: return
|
||||
val ideaProject = ideaModel.project ?: return
|
||||
|
||||
ideaProject.settings.runConfigurations {
|
||||
registerFactory(JUnitExt::class.java) { name -> project.objects.newInstance(JUnitExt::class.java, name) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.component.ModuleComponentSelector
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.artifacts.result.DependencyResult
|
||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
|
||||
abstract class DependencyCheck : DefaultTask() {
|
||||
@get:Input
|
||||
abstract val configuration: ListProperty<Configuration>
|
||||
|
||||
/**
|
||||
* A mapping of module coordinates (`group:module`) to versions, overriding the requested version.
|
||||
*/
|
||||
@get:Input
|
||||
abstract val overrides: MapProperty<String, String>
|
||||
|
||||
init {
|
||||
description = "Check :core's dependencies are consistent with Minecraft's."
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
|
||||
configuration.finalizeValueOnRead()
|
||||
overrides.finalizeValueOnRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* Override a module with a different version.
|
||||
*/
|
||||
fun override(module: Provider<MinimalExternalModuleDependency>, version: String) {
|
||||
overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
var ok = true
|
||||
for (configuration in configuration.get()) {
|
||||
configuration.incoming.resolutionResult.allDependencies {
|
||||
if (!check(this@allDependencies)) ok = false
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
throw GradleException("Mismatched versions in Minecraft dependencies. gradle/libs.versions.toml may need updating.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun check(dependency: DependencyResult): Boolean {
|
||||
if (dependency !is ResolvedDependencyResult) {
|
||||
logger.warn("Found unexpected dependency result {}", dependency)
|
||||
return false
|
||||
}
|
||||
|
||||
// Skip dependencies on non-modules.
|
||||
val requested = dependency.requested
|
||||
if (requested !is ModuleComponentSelector) return true
|
||||
|
||||
// If this dependency is specified within some project (so is non-transitive), or is pulled in via Minecraft,
|
||||
// then check for consistency.
|
||||
// It would be nice to be smarter about transitive dependencies, but avoiding false positives is hard.
|
||||
val from = dependency.from.id
|
||||
if (
|
||||
from is ProjectComponentIdentifier ||
|
||||
from is ModuleComponentIdentifier && (from.group == "net.minecraft" || from.group == "io.netty")
|
||||
) {
|
||||
// If the version is different between the requested and selected version, report an error.
|
||||
val selected = dependency.selected.moduleVersion!!.version
|
||||
val requestedVersion = overrides.get()["${requested.group}:${requested.module}"] ?: requested.version
|
||||
if (requestedVersion != selected) {
|
||||
logger.error("Requested dependency {} (via {}) but got version {}", requested, from, selected)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.AbstractExecTask
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
@@ -11,5 +12,5 @@ import java.io.File
|
||||
|
||||
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||
@get:OutputDirectory
|
||||
abstract val output: Property<File>
|
||||
abstract val output: DirectoryProperty
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.file.FileSystemLocation
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.JavaExec
|
||||
@@ -124,3 +125,33 @@ class CloseScope : AutoCloseable {
|
||||
|
||||
/** Proxy method to avoid overload ambiguity. */
|
||||
fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
|
||||
|
||||
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
|
||||
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath
|
||||
|
||||
/**
|
||||
* Get the version immediately after the provided version.
|
||||
*
|
||||
* For example, given "1.2.3", this will return "1.2.4".
|
||||
*/
|
||||
fun getNextVersion(version: String): String {
|
||||
// Split a version like x.y.z-SNAPSHOT into x.y.z and -SNAPSHOT
|
||||
val dashIndex = version.indexOf('-')
|
||||
val mainVersion = if (dashIndex < 0) version else version.substring(0, dashIndex)
|
||||
|
||||
// Find the last component in x.y.z and increment it.
|
||||
val lastIndex = mainVersion.lastIndexOf('.')
|
||||
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
|
||||
val lastVersion = try {
|
||||
version.substring(lastIndex + 1).toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
|
||||
}
|
||||
|
||||
// Then append all components together.
|
||||
val out = StringBuilder()
|
||||
out.append(version, 0, lastIndex + 1)
|
||||
out.append(lastVersion + 1)
|
||||
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
|
||||
return out.toString()
|
||||
}
|
||||
|
23
buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
Normal file
23
buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.jetbrains.gradle.ext.JUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A version of [JUnit] with a functional [className].
|
||||
*
|
||||
* See [#92](https://github.com/JetBrains/gradle-idea-ext-plugin/issues/92).
|
||||
*/
|
||||
open class JUnitExt @Inject constructor(nameParam: String) : JUnit(nameParam) {
|
||||
override fun toMap(): MutableMap<String, *> {
|
||||
val map = HashMap(super.toMap())
|
||||
// Should be "class" instead of "className".
|
||||
// See https://github.com/JetBrains/intellij-community/blob/9ba394021dc73a3926f13d6d6cdf434f9ee7046d/plugins/junit/src/com/intellij/execution/junit/JUnitRunConfigurationImporter.kt#L39
|
||||
map["class"] = className
|
||||
return map
|
||||
}
|
||||
}
|
@@ -6,6 +6,8 @@ package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.plugins.BasePluginExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.specs.Spec
|
||||
|
||||
@@ -26,8 +28,13 @@ class MavenDependencySpec {
|
||||
|
||||
fun exclude(dep: Dependency) {
|
||||
exclude {
|
||||
// We have to cheat a little for project dependencies, as the project name doesn't match the artifact group.
|
||||
val name = when (dep) {
|
||||
is ProjectDependency -> dep.dependencyProject.extensions.getByType(BasePluginExtension::class.java).archivesName.get()
|
||||
else -> dep.name
|
||||
}
|
||||
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
||||
(dep.name.isNullOrEmpty() || dep.name == it.artifactId) &&
|
||||
(name.isNullOrEmpty() || name == it.artifactId) &&
|
||||
(dep.version.isNullOrEmpty() || dep.version == it.version)
|
||||
}
|
||||
}
|
||||
|
@@ -109,6 +109,15 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
||||
project.extensions.configure(CCTweakedExtension::class.java) {
|
||||
sourceDirectories.add(SourceSetReference.internal(client))
|
||||
}
|
||||
|
||||
// Register a task to check there are no conflicts with the core project.
|
||||
val checkDependencyConsistency =
|
||||
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
|
||||
// We need to check both the main and client classpath *configurations*, as the actual configuration
|
||||
configuration.add(configurations.named(main.runtimeClasspathConfigurationName))
|
||||
configuration.add(configurations.named(client.runtimeClasspathConfigurationName))
|
||||
}
|
||||
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
||||
}
|
||||
|
||||
private fun setupOutgoing(sourceSet: SourceSet, suffix: String = "") {
|
||||
|
@@ -58,7 +58,7 @@ abstract class ClientJavaExec : JavaExec() {
|
||||
if (!clientDebug) systemProperty("cctest.client", "")
|
||||
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
|
||||
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
||||
workingDir(project.layout.buildDirectory.dir("gametest/$name"))
|
||||
}
|
||||
|
||||
init {
|
||||
|
@@ -112,7 +112,9 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<module name="LambdaParameterName" />
|
||||
<module name="LocalFinalVariableName" />
|
||||
<module name="LocalVariableName" />
|
||||
<module name="MemberName" />
|
||||
<module name="MemberName">
|
||||
<property name="format" value="^\$?[a-z][a-zA-Z0-9]*$" />
|
||||
</module>
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
||||
</module>
|
||||
@@ -122,7 +124,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</module>
|
||||
<module name="ParameterName" />
|
||||
<module name="StaticVariableName">
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$" />
|
||||
</module>
|
||||
<module name="TypeName" />
|
||||
|
||||
|
@@ -16,4 +16,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
<!-- The commands API is documented in Lua. -->
|
||||
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
|
||||
|
||||
<!-- Allow putting files in other packages if they look like our TeaVM stubs. -->
|
||||
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
||||
|
||||
<!-- Allow underscores in our test classes. -->
|
||||
<suppress checks="MethodName" files=".*Contract.java" />
|
||||
</suppressions>
|
||||
|
@@ -6,7 +6,7 @@ see: key To listen to any key press.
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
The [`char`] event is fired when a character is typed on the keyboard.
|
||||
|
@@ -12,7 +12,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
|
||||
|
||||
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
|
||||
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.BinaryReadHandle`] with an
|
||||
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.ReadHandle`] with an
|
||||
additional [getName][`TransferredFile.getName`] method.
|
||||
|
||||
## Return values
|
||||
@@ -29,7 +29,7 @@ for _, file in ipairs(files.getFiles()) do
|
||||
local size = file.seek("end")
|
||||
file.seek("set", 0)
|
||||
|
||||
print(file.getName() .. " " .. file.getSize())
|
||||
print(file.getName() .. " " .. size)
|
||||
end
|
||||
```
|
||||
|
||||
|
@@ -5,7 +5,7 @@ module: [kind=event] key
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
This event is fired when any key is pressed while the terminal is focused.
|
||||
|
@@ -6,7 +6,7 @@ see: keys For a lookup table of the given keys.
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
||||
|
@@ -5,7 +5,7 @@ module: [kind=event] mouse_click
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
This event is fired when the terminal is clicked with a mouse. This event is only fired on advanced computers (including
|
||||
|
@@ -6,7 +6,7 @@ see: mouse_click For when a mouse button is initially pressed.
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
This event is fired every time the mouse is moved while a mouse button is being held.
|
||||
|
@@ -5,7 +5,7 @@ module: [kind=event] mouse_scroll
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
This event is fired when a mouse wheel is scrolled in the terminal.
|
||||
|
@@ -5,7 +5,7 @@ module: [kind=event] mouse_up
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-CCPL
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
||||
|
@@ -134,7 +134,7 @@ accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, whic
|
||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
||||
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.BinaryReadHandle.read`] if you prefer.
|
||||
[`fs.ReadHandle.read`] if you prefer.
|
||||
|
||||
## Processing audio
|
||||
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
||||
|
81
doc/reference/breaking_changes.md
Normal file
81
doc/reference/breaking_changes.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
module: [kind=reference] breaking_changes
|
||||
---
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# Incompatibilities between versions
|
||||
|
||||
CC: Tweaked tries to remain as compatible between versions as possible, meaning most programs written for older version
|
||||
of the mod should run fine on later versions.
|
||||
|
||||
> [External peripherals][!WARNING]
|
||||
>
|
||||
> While CC: Tweaked is relatively stable across versions, this may not be true for other mods which add their own
|
||||
> peripherals. Older programs which interact with external blocks may not work on newer versions of the game.
|
||||
|
||||
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
|
||||
as documentation for breaking changes and "gotchas" one should look out for between versions.
|
||||
|
||||
## CC: Tweaked 1.109.0 to 1.109.3 {#cct-1.109}
|
||||
|
||||
- Update to Lua 5.2:
|
||||
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
|
||||
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
|
||||
now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
|
||||
with other functions, and `setfenv` will have no effect.
|
||||
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
|
||||
environment.
|
||||
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
|
||||
- `math.random` now uses Lua 5.4's random number generator.
|
||||
|
||||
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
|
||||
|
||||
## Minecraft 1.13 {#mc-1.13}
|
||||
- The "key code" for [`key`] and [`key_up`] events has changed, due to Minecraft updating to LWJGL 3. Make sure you're
|
||||
using the constants provided by the [`keys`] API, rather than hard-coding numerical values.
|
||||
|
||||
Related to this change, the numpad enter key now has a different key code to the enter key. You may need to adjust
|
||||
your programs to handle both. (Note, the `keys.numpadEnter` constant was defined in pre-1.13 versions of CC, but the
|
||||
`keys.enter` constant was queued when the key was pressed)
|
||||
|
||||
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
|
||||
result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
|
||||
|
||||
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
|
||||
more understandable format.
|
||||
|
||||
- Item and block names now represent a unique item type. For instance, wool is split into 16 separate items
|
||||
(`minecraft:white_wool`, etc...) rather than a single `minecraft:wool` with each meta/damage value specifying the
|
||||
colour.
|
||||
|
||||
- Custom ROMs are now provided using data packs rather than resource packs. This should mostly be a matter of renaming
|
||||
the "assets" folder to "data", and placing it in "datapacks", but there are a couple of other gotchas to look out
|
||||
for:
|
||||
|
||||
- Data packs [impose some restrictions on file names][legal_data_pack]. As a result, your programs and directories
|
||||
must all be lower case.
|
||||
- Due to how data packs are read by CC: Tweaked, you may need to use the `/reload` command to see changes to your
|
||||
pack show up on the computer.
|
||||
|
||||
See [the example datapack][datapack-example] for how to get started.
|
||||
|
||||
- Turtles can now be waterlogged and move "through" water sources rather than breaking them.
|
||||
|
||||
## CC: Tweaked 1.88.0 {#cc-1.88}
|
||||
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
|
||||
|
||||
## ComputerCraft 1.80pr1 {#cc-1.80}
|
||||
- Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
|
||||
will not be accessible outside of this program.
|
||||
|
||||
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
||||
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
||||
|
||||
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
|
||||
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
||||
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
@@ -9,17 +9,19 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# Lua 5.2/5.3 features in CC: Tweaked
|
||||
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However, Cobalt and CC:T implement additional features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 features) that are not available in base 5.1. This page lists all of the compatibility for these newer versions.
|
||||
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.2. However, Cobalt and CC:T implement additional
|
||||
features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 and 5.1 features). This page lists all of the
|
||||
compatibility for these newer versions.
|
||||
|
||||
## Lua 5.2
|
||||
| Feature | Supported? | Notes |
|
||||
|---------------------------------------------------------------|------------|-------------------------------------------------------------------|
|
||||
| `goto`/labels | ❌ | |
|
||||
| `_ENV` | 🔶 | The `_ENV` global points to `getfenv()`, but it cannot be set. |
|
||||
| `goto`/labels | ✔ | |
|
||||
| `_ENV` | ✔ | |
|
||||
| `\z` escape | ✔ | |
|
||||
| `\xNN` escape | ✔ | |
|
||||
| Hex literal fractional/exponent parts | ✔ | |
|
||||
| Empty statements | ❌ | |
|
||||
| Empty statements | ✔ | |
|
||||
| `__len` metamethod | ✔ | |
|
||||
| `__ipairs` metamethod | ❌ | Deprecated in Lua 5.3. `ipairs` uses `__len`/`__index` instead. |
|
||||
| `__pairs` metamethod | ✔ | |
|
||||
@@ -27,12 +29,12 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
|
||||
| `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. |
|
||||
| New `load` syntax | ✔ | |
|
||||
| `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. |
|
||||
| Removed `loadstring` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||
| Removed `getfenv`, `setfenv` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||
| Removed `loadstring` | ❌ | |
|
||||
| Removed `getfenv`, `setfenv` | 🔶 | Only supports closures with an `_ENV` upvalue. |
|
||||
| `rawlen` function | ✔ | |
|
||||
| Negative index to `select` | ✔ | |
|
||||
| Removed `unpack` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||
| Arguments to `xpcall` | ✔ | |
|
||||
| Removed `unpack` | ❌ | |
|
||||
| Arguments to `xpcall` | ✔ | |
|
||||
| Second return value from `coroutine.running` | ✔ | |
|
||||
| Removed `module` | ✔ | |
|
||||
| `package.loaders` -> `package.searchers` | ❌ | |
|
||||
@@ -40,14 +42,14 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
|
||||
| `package.config` | ✔ | |
|
||||
| `package.searchpath` | ✔ | |
|
||||
| Removed `package.seeall` | ✔ | |
|
||||
| `string.dump` on functions with upvalues (blanks them out) | ✔ | |
|
||||
| `string.rep` separator | ✔ | |
|
||||
| `string.dump` on functions with upvalues (blanks them out) | ❌ | `string.dump` is not supported |
|
||||
| `string.rep` separator | ✔ | |
|
||||
| `%g` match group | ❌ | |
|
||||
| Removal of `%z` match group | ❌ | |
|
||||
| Removed `table.maxn` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||
| Removed `table.maxn` | ❌ | |
|
||||
| `table.pack`/`table.unpack` | ✔ | |
|
||||
| `math.log` base argument | ✔ | |
|
||||
| Removed `math.log10` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||
| Removed `math.log10` | ❌ | |
|
||||
| `*L` mode to `file:read` | ✔ | |
|
||||
| `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. |
|
||||
| `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. |
|
||||
@@ -61,7 +63,7 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
|
||||
| Tail call hooks | ❌ | |
|
||||
| `=` prefix for chunks | ✔ | |
|
||||
| Yield across C boundary | ✔ | |
|
||||
| Removal of ambiguity error | ❌ | |
|
||||
| Removal of ambiguity error | ✔ | |
|
||||
| Identifiers may no longer use locale-dependent letters | ✔ | |
|
||||
| Ephemeron tables | ❌ | |
|
||||
| Identical functions may be reused | ❌ | Removed in Lua 5.4 |
|
||||
|
@@ -95,10 +95,10 @@ function pullEventRaw(filter) end
|
||||
-- nearest multiple of 0.05.
|
||||
function sleep(time) end
|
||||
|
||||
--- Get the current CraftOS version (for example, `CraftOS 1.8`).
|
||||
--- Get the current CraftOS version (for example, `CraftOS 1.9`).
|
||||
--
|
||||
-- This is defined by `bios.lua`. For the current version of CC:Tweaked, this
|
||||
-- should return `CraftOS 1.8`.
|
||||
-- should return `CraftOS 1.9`.
|
||||
--
|
||||
-- @treturn string The current CraftOS version.
|
||||
-- @usage os.version()
|
||||
|
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.108.1
|
||||
modVersion=1.109.4
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.1
|
||||
|
@@ -10,27 +10,29 @@
|
||||
fabric-api = "0.86.1+1.20.1"
|
||||
fabric-loader = "0.14.21"
|
||||
forge = "47.1.0"
|
||||
forgeSpi = "6.0.0"
|
||||
forgeSpi = "7.0.1"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2023.08.20"
|
||||
parchmentMc = "1.20.1"
|
||||
|
||||
# Normal dependencies
|
||||
asm = "9.5"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.32.0"
|
||||
cobalt = "0.7.3"
|
||||
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
|
||||
# Core dependencies (these versions are tied to the version Minecraft uses)
|
||||
fastutil = "8.5.9"
|
||||
guava = "31.1-jre"
|
||||
jetbrainsAnnotations = "24.0.1"
|
||||
netty = "4.1.82.Final"
|
||||
slf4j = "2.0.1"
|
||||
|
||||
# Core dependencies (independent of Minecraft)
|
||||
asm = "9.6"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = "0.9.0"
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jsr305 = "3.0.2"
|
||||
jzlib = "1.1.3"
|
||||
kotlin = "1.8.10"
|
||||
kotlin-coroutines = "1.6.4"
|
||||
netty = "4.1.82.Final"
|
||||
kotlin = "1.9.21"
|
||||
kotlin-coroutines = "1.7.3"
|
||||
nightConfig = "3.6.7"
|
||||
slf4j = "1.7.36"
|
||||
|
||||
# Minecraft mods
|
||||
emi = "1.0.8+1.20.1"
|
||||
@@ -45,37 +47,41 @@ rubidium = "0.6.1"
|
||||
sodium = "mc1.20-0.4.10"
|
||||
|
||||
# Testing
|
||||
byteBuddy = "1.14.7"
|
||||
hamcrest = "2.2"
|
||||
jqwik = "1.7.4"
|
||||
junit = "5.10.0"
|
||||
jqwik = "1.8.2"
|
||||
junit = "5.10.1"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.0"
|
||||
checkstyle = "10.12.3"
|
||||
cctJavadoc = "1.8.2"
|
||||
checkstyle = "10.12.6"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.21.1"
|
||||
errorProne-core = "2.23.0"
|
||||
errorProne-plugin = "3.1.0"
|
||||
fabric-loom = "1.3.7"
|
||||
forgeGradle = "6.0.8"
|
||||
githubRelease = "2.4.1"
|
||||
fabric-loom = "1.5.7"
|
||||
forgeGradle = "6.0.20"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.50.0"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-40-g975cbc3"
|
||||
illuaminate = "0.1.0-44-g9ee0055"
|
||||
librarian = "1.+"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.+"
|
||||
mixinGradle = "0.7.+"
|
||||
mixinGradle = "0.7.38"
|
||||
nullAway = "0.9.9"
|
||||
spotless = "6.21.0"
|
||||
spotless = "6.23.3"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.10.0-SQUID.2"
|
||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||
vineflower = "1.11.0"
|
||||
versionCatalogUpdate = "0.8.1"
|
||||
|
||||
[libraries]
|
||||
# Normal dependencies
|
||||
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
||||
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
|
||||
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
||||
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
@@ -95,6 +101,7 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
# Minecraft mods
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||
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" }
|
||||
@@ -112,8 +119,6 @@ rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
|
||||
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
||||
|
||||
# Testing
|
||||
byteBuddyAgent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
|
||||
byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
|
||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
||||
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
||||
@@ -122,6 +127,12 @@ junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", vers
|
||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||
|
||||
# LWJGL
|
||||
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
||||
lwjgl-core = { module = "org.lwjgl:lwjgl" }
|
||||
lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl" }
|
||||
lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw" }
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||
@@ -133,34 +144,47 @@ errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", versi
|
||||
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" }
|
||||
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-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" }
|
||||
teavm-metaprogramming-api = { module = "org.teavm:teavm-metaprogramming-api", version.ref = "teavm" }
|
||||
teavm-metaprogramming-impl = { module = "org.teavm:teavm-metaprogramming-impl", version.ref = "teavm" }
|
||||
teavm-platform = { module = "org.teavm:teavm-platform", version.ref = "teavm" }
|
||||
teavm-tooling = { module = "org.teavm:teavm-tooling", version.ref = "teavm" }
|
||||
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
||||
vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vineflower" }
|
||||
|
||||
[plugins]
|
||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
|
||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||
|
||||
[bundles]
|
||||
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
|
||||
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-fabric = ["nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
||||
|
||||
# Testing
|
||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
||||
|
||||
# Build tools
|
||||
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
||||
teavm-tooling = ["teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl"]
|
||||
|
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.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -2,15 +2,15 @@
|
||||
|
||||
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
;
|
||||
; SPDX-License-Identifier: LicenseRef-CCPL
|
||||
; SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
(sources
|
||||
/doc/
|
||||
/projects/forge/build/docs/luaJavadoc/
|
||||
/projects/common/build/docs/luaJavadoc/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
||||
/projects/core/src/test/resources/test-rom
|
||||
/projects/web/src/mount)
|
||||
/projects/web/src/frontend/mount)
|
||||
|
||||
(doc
|
||||
; Also defined in projects/web/build.gradle.kts
|
||||
@@ -23,7 +23,7 @@
|
||||
(url https://tweaked.cc/)
|
||||
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||
|
||||
(styles /projects/web/src/styles.css)
|
||||
(styles /projects/web/build/rollup/index.css)
|
||||
(scripts /projects/web/build/rollup/index.js)
|
||||
(head doc/head.html))
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
(library-path
|
||||
/doc/stub/
|
||||
/projects/forge/build/docs/luaJavadoc/
|
||||
/projects/common/build/docs/luaJavadoc/
|
||||
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
|
||||
@@ -77,7 +77,6 @@
|
||||
(globals
|
||||
:max
|
||||
_CC_DEFAULT_SETTINGS
|
||||
_CC_DISABLE_LUA51_FEATURES
|
||||
_HOST
|
||||
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
|
||||
;; isn't smart enough.
|
||||
@@ -89,7 +88,7 @@
|
||||
(/doc/stub/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||
/projects/forge/build/docs/luaJavadoc/)
|
||||
/projects/common/build/docs/luaJavadoc/)
|
||||
(linters -var:unused-global)
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
@@ -115,4 +114,4 @@
|
||||
:max sleep write
|
||||
cct_test describe expect howlci fail it pending stub before_each)))
|
||||
|
||||
(at /projects/web/src/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||
(at /projects/web/src/frontend/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||
|
4721
package-lock.json
generated
4721
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -6,24 +6,24 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@squid-dev/cc-web-term": "^2.0.0",
|
||||
"preact": "^10.5.5",
|
||||
"setimmediate": "^1.0.5",
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-terser": "^0.4.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@rollup/plugin-url": "^8.0.1",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"glob": "^10.3.4",
|
||||
"react-dom": "^18.1.0",
|
||||
"react": "^18.1.0",
|
||||
"rehype-highlight": "^6.0.0",
|
||||
"rehype-react": "^7.1.1",
|
||||
"rehype": "^12.0.0",
|
||||
"requirejs": "^2.3.6",
|
||||
"rollup": "^3.19.1",
|
||||
"ts-node": "^10.8.0",
|
||||
"@swc/core": "^1.3.92",
|
||||
"@types/node": "^20.8.3",
|
||||
"lightningcss": "^1.22.0",
|
||||
"preact-render-to-string": "^6.2.1",
|
||||
"rehype": "^13.0.0",
|
||||
"rehype-highlight": "^7.0.0",
|
||||
"rehype-react": "^8.0.0",
|
||||
"rollup": "^4.0.0",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
@@ -62,6 +62,9 @@ mentioning:
|
||||
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
||||
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
||||
|
||||
- `standalone`: This contains a standalone UI for computers, allowing debugging and development of CraftOS without
|
||||
launching Minecraft.
|
||||
|
||||
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
||||
rendering recipes
|
||||
|
||||
|
@@ -27,8 +27,13 @@ public final class ComputerCraftAPIClient {
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
|
||||
/**
|
||||
* A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||
* <p>
|
||||
* This interface is largely intended to be used from multi-loader code, to allow sharing registration code between
|
||||
* multiple loaders.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RegisterTurtleUpgradeModeller {
|
||||
/**
|
||||
* Register a {@link TurtleUpgradeModeller}.
|
||||
*
|
||||
* @param serialiser The turtle upgrade serialiser.
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
*/
|
||||
<T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||
}
|
@@ -4,12 +4,10 @@
|
||||
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||
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 dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -21,9 +19,13 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides models for a {@link ITurtleUpgrade}.
|
||||
* <p>
|
||||
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
||||
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
||||
* on Forge
|
||||
*
|
||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
|
||||
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
||||
*/
|
||||
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
/**
|
||||
|
@@ -46,6 +46,14 @@ public class ComputerCraftTags {
|
||||
public static final TagKey<Block> WIRED_MODEM = make("wired_modem");
|
||||
public static final TagKey<Block> MONITOR = make("monitor");
|
||||
|
||||
/**
|
||||
* Blocks which should be ignored by a {@code peripheral_hub} peripheral.
|
||||
* <p>
|
||||
* This should include blocks which themselves expose a peripheral hub (such as {@linkplain #WIRED_MODEM wired
|
||||
* modems}).
|
||||
*/
|
||||
public static final TagKey<Block> PERIPHERAL_HUB_IGNORE = make("peripheral_hub_ignore");
|
||||
|
||||
/**
|
||||
* Blocks which can be broken by any turtle tool.
|
||||
*/
|
||||
|
@@ -48,13 +48,8 @@ import java.util.function.Function;
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Finally, we need to register a model for our upgrade. This is done with
|
||||
* {@link dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller}:
|
||||
*
|
||||
* <pre>{@code
|
||||
* // Register our model inside FMLClientSetupEvent
|
||||
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
|
||||
* }</pre>
|
||||
* 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.
|
||||
*
|
||||
|
@@ -19,8 +19,6 @@ import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
@@ -36,8 +34,6 @@ import java.util.function.Function;
|
||||
* @param <R> The upgrade serialiser to register for.
|
||||
*/
|
||||
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
private final PackOutput output;
|
||||
private final String name;
|
||||
private final String folder;
|
||||
|
@@ -2,14 +2,13 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import cc.tweaked.gradle.annotationProcessorEverywhere
|
||||
import cc.tweaked.gradle.clientClasses
|
||||
import cc.tweaked.gradle.commonClasses
|
||||
import cc.tweaked.gradle.*
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
id("cc-tweaked.vanilla")
|
||||
id("cc-tweaked.gametest")
|
||||
id("cc-tweaked.illuaminate")
|
||||
id("cc-tweaked.publishing")
|
||||
}
|
||||
|
||||
minecraft {
|
||||
@@ -19,6 +18,10 @@ minecraft {
|
||||
)
|
||||
}
|
||||
|
||||
configurations {
|
||||
register("cctJavadoc")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
||||
implementation(project(":core"))
|
||||
@@ -39,4 +42,55 @@ dependencies {
|
||||
testModImplementation(testFixtures(project(":core")))
|
||||
testModImplementation(testFixtures(project(":common")))
|
||||
testModImplementation(libs.bundles.kotlin)
|
||||
|
||||
testFixturesImplementation(testFixtures(project(":core")))
|
||||
|
||||
"cctJavadoc"(libs.cctJavadoc)
|
||||
}
|
||||
|
||||
illuaminate {
|
||||
version.set(libs.versions.illuaminate)
|
||||
}
|
||||
|
||||
val luaJavadoc by tasks.registering(Javadoc::class) {
|
||||
description = "Generates documentation for Java-side Lua functions."
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
|
||||
val sourceSets = listOf(sourceSets.main.get(), project(":core").sourceSets.main.get())
|
||||
for (sourceSet in sourceSets) {
|
||||
source(sourceSet.java)
|
||||
classpath += sourceSet.compileClasspath
|
||||
}
|
||||
|
||||
destinationDir = layout.buildDirectory.dir("docs/luaJavadoc").get().asFile
|
||||
|
||||
val options = options as StandardJavadocDocletOptions
|
||||
options.docletpath = configurations["cctJavadoc"].files.toList()
|
||||
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
|
||||
options.addStringOption("project-root", rootProject.file(".").absolutePath)
|
||||
options.noTimestamp(false)
|
||||
|
||||
javadocTool.set(
|
||||
javaToolchains.javadocToolFor {
|
||||
languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||
description = "Lint Lua (and Lua docs) with illuaminate"
|
||||
|
||||
// Config files
|
||||
inputs.file(rootProject.file("illuaminate.sexp")).withPropertyName("illuaminate.sexp")
|
||||
// Sources
|
||||
inputs.files(rootProject.fileTree("doc")).withPropertyName("docs")
|
||||
inputs.files(project(":core").fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||
inputs.files(luaJavadoc)
|
||||
|
||||
args = listOf("lint")
|
||||
workingDir = rootProject.projectDir
|
||||
|
||||
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
@@ -60,18 +60,6 @@ public final class ClientRegistry {
|
||||
* Register any client-side objects which don't have to be done on the main thread.
|
||||
*/
|
||||
public static void register() {
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
||||
));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
||||
));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
||||
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), MonitorBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||
@@ -103,6 +91,20 @@ public final class ClientRegistry {
|
||||
);
|
||||
}
|
||||
|
||||
public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
|
||||
register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
||||
));
|
||||
register.register(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
||||
));
|
||||
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
||||
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
||||
register.register(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||
|
@@ -7,7 +7,7 @@ package dan200.computercraft.client.gui;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.network.ClientNetworking;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||
@@ -19,6 +19,7 @@ import dan200.computercraft.shared.network.server.UploadFileMessage;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -33,7 +34,6 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -145,6 +145,11 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
|| super.mouseDragged(x, y, button, deltaX, deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocused(@Nullable GuiEventListener listener) {
|
||||
// Don't clear and re-focus if we're already focused.
|
||||
if (listener != getFocused()) super.setFocused(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||
@@ -202,7 +207,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
return;
|
||||
}
|
||||
|
||||
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientPlatformHelper.get()::sendToServer);
|
||||
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||
}
|
||||
|
||||
public void uploadResult(UploadResult result, @Nullable Component message) {
|
||||
@@ -219,7 +224,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
|
||||
private void alert(Component title, Component message) {
|
||||
OptionScreen.show(minecraft, title, message,
|
||||
Collections.singletonList(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||
() -> minecraft.setScreen(this)
|
||||
);
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.network.ClientNetworking;
|
||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.network.server.ComputerActionServerMessage;
|
||||
@@ -29,51 +29,51 @@ public final class ClientInputHandler implements InputHandler {
|
||||
|
||||
@Override
|
||||
public void turnOn() {
|
||||
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
|
||||
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN));
|
||||
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reboot() {
|
||||
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
|
||||
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent(String event, @Nullable Object[] arguments) {
|
||||
ClientPlatformHelper.get().sendToServer(new QueueEventServerMessage(menu, event, arguments));
|
||||
ClientNetworking.sendToServer(new QueueEventServerMessage(menu, event, arguments));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyDown(int key, boolean repeat) {
|
||||
ClientPlatformHelper.get().sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyUp(int key) {
|
||||
ClientPlatformHelper.get().sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClick(int button, int x, int y) {
|
||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseUp(int button, int x, int y) {
|
||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDrag(int button, int x, int y) {
|
||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseScroll(int direction, int x, int y) {
|
||||
ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
|
||||
}
|
||||
}
|
||||
|
@@ -8,8 +8,8 @@ import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.StringUtil;
|
||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
@@ -112,26 +112,8 @@ public class TerminalWidget extends AbstractWidget {
|
||||
}
|
||||
|
||||
private void paste() {
|
||||
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
||||
|
||||
// Clip to the first occurrence of \r or \n
|
||||
var newLineIndex1 = clipboard.indexOf('\r');
|
||||
var newLineIndex2 = clipboard.indexOf('\n');
|
||||
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
|
||||
} else if (newLineIndex1 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex1);
|
||||
} else if (newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex2);
|
||||
}
|
||||
|
||||
// Filter the string
|
||||
clipboard = SharedConstants.filterText(clipboard);
|
||||
if (!clipboard.isEmpty()) {
|
||||
// Clip to 512 characters and queue the event
|
||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
}
|
||||
var clipboard = StringUtil.normaliseClipboardString(Minecraft.getInstance().keyboardHandler.getClipboard());
|
||||
if (!clipboard.isEmpty()) computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -264,7 +246,7 @@ public class TerminalWidget extends AbstractWidget {
|
||||
keysDown.clear();
|
||||
|
||||
// When blurring, we should make the last mouse button go up
|
||||
if (lastMouseButton > 0) {
|
||||
if (lastMouseButton >= 0) {
|
||||
computer.mouseUp(lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1);
|
||||
lastMouseButton = -1;
|
||||
}
|
||||
|
@@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.network;
|
||||
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* Methods for sending packets from clients to the server.
|
||||
*/
|
||||
public final class ClientNetworking {
|
||||
private ClientNetworking() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a network message to the server.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
public static void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
||||
var connection = Minecraft.getInstance().getConnection();
|
||||
if (connection != null) connection.send(ClientPlatformHelper.get().createPacket(message));
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.client.platform;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import dan200.computercraft.client.ClientTableFormatter;
|
||||
import dan200.computercraft.client.gui.AbstractComputerScreen;
|
||||
import dan200.computercraft.client.gui.OptionScreen;
|
||||
@@ -17,30 +18,30 @@ import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The base implementation of {@link ClientNetworkContext}.
|
||||
* <p>
|
||||
* This should be extended by mod loader specific modules with the remaining abstract methods.
|
||||
* The client-side implementation of {@link ClientNetworkContext}.
|
||||
*/
|
||||
public abstract class AbstractClientNetworkContext implements ClientNetworkContext {
|
||||
@AutoService(ClientNetworkContext.class)
|
||||
public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||
@Override
|
||||
public final void handleChatTable(TableBuilder table) {
|
||||
public void handleChatTable(TableBuilder table) {
|
||||
ClientTableFormatter.INSTANCE.display(table);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleComputerTerminal(int containerId, TerminalState terminal) {
|
||||
public void handleComputerTerminal(int containerId, TerminalState terminal) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) {
|
||||
menu.updateTerminal(terminal);
|
||||
@@ -48,7 +49,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleMonitorData(BlockPos pos, TerminalState terminal) {
|
||||
public void handleMonitorData(BlockPos pos, TerminalState terminal) {
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (player == null) return;
|
||||
|
||||
@@ -59,44 +60,46 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||
public void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name) {
|
||||
var mc = Minecraft.getInstance();
|
||||
ClientPlatformHelper.get().playStreamingMusic(pos, sound);
|
||||
if (name != null) mc.gui.setNowPlaying(Component.literal(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
||||
computer.setState(state, lightState);
|
||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handlePocketComputerDeleted(int instanceId) {
|
||||
public void handlePocketComputerDeleted(int instanceId) {
|
||||
ClientPocketComputers.remove(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume) {
|
||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume);
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
|
||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleSpeakerAudioPush(UUID source, ByteBuf buffer) {
|
||||
SpeakerManager.getSound(source).pushAudio(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
|
||||
public void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
|
||||
SpeakerManager.moveSound(source, reifyPosition(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, ResourceLocation sound, float volume, float pitch) {
|
||||
public void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, ResourceLocation sound, float volume, float pitch) {
|
||||
SpeakerManager.getSound(source).playSound(reifyPosition(position), sound, volume, pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleSpeakerStop(UUID source) {
|
||||
public void handleSpeakerStop(UUID source) {
|
||||
SpeakerManager.stopSound(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) {
|
||||
public void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) {
|
||||
var minecraft = Minecraft.getInstance();
|
||||
|
||||
var screen = OptionScreen.unwrap(minecraft.screen);
|
@@ -9,6 +9,10 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
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.sounds.SoundEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -18,11 +22,12 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a network message to the server.
|
||||
* Convert a serverbound {@link NetworkMessage} to a Minecraft {@link Packet}.
|
||||
*
|
||||
* @param message The message to send.
|
||||
* @param message The messsge to convert.
|
||||
* @return The converted message.
|
||||
*/
|
||||
void sendToServer(NetworkMessage<ServerNetworkContext> message);
|
||||
Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
|
||||
|
||||
/**
|
||||
* Render a {@link BakedModel}, using any loader-specific hooks.
|
||||
@@ -35,4 +40,13 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
||||
* @param tints Block colour tints to apply to the model.
|
||||
*/
|
||||
void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints);
|
||||
|
||||
/**
|
||||
* Play a record at a particular position.
|
||||
*
|
||||
* @param pos The position to play this record.
|
||||
* @param sound The record to play, or {@code null} to stop it.
|
||||
* @see net.minecraft.client.renderer.LevelRenderer#playStreamingMusic(SoundEvent, BlockPos)
|
||||
*/
|
||||
void playStreamingMusic(BlockPos pos, @Nullable SoundEvent sound);
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -21,7 +20,6 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
@@ -29,8 +27,6 @@ import net.minecraft.world.phys.HitResult;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
|
||||
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
|
||||
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced", "inventory");
|
||||
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
||||
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
|
||||
|
||||
@@ -42,13 +38,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
font = context.getFont();
|
||||
}
|
||||
|
||||
public static ResourceLocation getTurtleModel(ComputerFamily family, boolean coloured) {
|
||||
return switch (family) {
|
||||
default -> coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
|
||||
case ADVANCED -> coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
|
||||
};
|
||||
}
|
||||
|
||||
public static @Nullable ResourceLocation getTurtleOverlayModel(@Nullable ResourceLocation overlay, boolean christmas) {
|
||||
if (overlay != null) return overlay;
|
||||
if (christmas) return ELF_OVERLAY_MODEL;
|
||||
@@ -78,7 +67,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
var matrix = transform.last().pose();
|
||||
var opacity = (int) (mc.options.getBackgroundOpacity(0.25f) * 255) << 24;
|
||||
var width = -font.width(label) / 2.0f;
|
||||
// TODO: Check this looks okay
|
||||
font.drawInBatch(label, width, (float) 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
|
||||
font.drawInBatch(label, width, (float) 0, 0xffffffff, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
|
||||
|
||||
@@ -96,10 +84,18 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
|
||||
// Render the turtle
|
||||
var colour = turtle.getColour();
|
||||
var family = turtle.getFamily();
|
||||
var overlay = turtle.getOverlay();
|
||||
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
|
||||
if (colour == -1) {
|
||||
// Render the turtle using its item model.
|
||||
var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper();
|
||||
var model = modelManager.getItemModel(turtle.getBlockState().getBlock().asItem());
|
||||
if (model == null) model = modelManager.getModelManager().getMissingModel();
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, model, null);
|
||||
} else {
|
||||
// Otherwise render it using the colour item.
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, COLOUR_TURTLE_MODEL, new int[]{ colour });
|
||||
}
|
||||
|
||||
// Render the overlay
|
||||
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
||||
|
@@ -14,10 +14,10 @@ import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
@@ -36,12 +36,12 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.get
|
||||
* @see RenderTypes#getMonitorTextureBufferShader()
|
||||
*/
|
||||
public class MonitorTextureBufferShader extends ShaderInstance {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MonitorTextureBufferShader.class);
|
||||
|
||||
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
|
||||
|
||||
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
private final int monitorData;
|
||||
private int uniformBuffer = 0;
|
||||
|
||||
@@ -75,7 +75,7 @@ public class MonitorTextureBufferShader extends ShaderInstance {
|
||||
private Uniform getUniformChecked(String name) {
|
||||
var uniform = getUniform(name);
|
||||
if (uniform == null) {
|
||||
LOGGER.warn("Monitor shader {} should have uniform {}, but it was not present.", getName(), name);
|
||||
LOG.warn("Monitor shader {} should have uniform {}, but it was not present.", getName(), name);
|
||||
}
|
||||
|
||||
return uniform;
|
||||
|
@@ -6,7 +6,7 @@ package dan200.computercraft.client.sound;
|
||||
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
import net.minecraft.client.sounds.SoundEngine;
|
||||
import org.lwjgl.BufferUtils;
|
||||
@@ -36,7 +36,7 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The {@link Channel} which this sound is playing on.
|
||||
*
|
||||
* @see SpeakerInstance#pushAudio(ByteBuf)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
*/
|
||||
@Nullable
|
||||
Channel channel;
|
||||
@@ -44,7 +44,7 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The underlying {@link SoundEngine} executor.
|
||||
*
|
||||
* @see SpeakerInstance#pushAudio(ByteBuf)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
* @see SoundEngine#executor
|
||||
*/
|
||||
@Nullable
|
||||
@@ -58,12 +58,12 @@ class DfpwmStream implements AudioStream {
|
||||
DfpwmStream() {
|
||||
}
|
||||
|
||||
void push(ByteBuf input) {
|
||||
var readable = input.readableBytes();
|
||||
void push(ByteBuffer input) {
|
||||
var readable = input.remaining();
|
||||
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
||||
|
||||
for (var i = 0; i < readable; i++) {
|
||||
var inputByte = input.readByte();
|
||||
var inputByte = input.get();
|
||||
for (var j = 0; j < 8; j++) {
|
||||
var currentBit = (inputByte & 1) != 0;
|
||||
var target = currentBit ? 127 : -128;
|
||||
|
@@ -7,11 +7,11 @@ package dan200.computercraft.client.sound;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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() {
|
||||
}
|
||||
|
||||
public synchronized void pushAudio(ByteBuf buffer) {
|
||||
private void pushAudio(ByteBuffer buffer) {
|
||||
var sound = this.sound;
|
||||
|
||||
var stream = currentStream;
|
||||
@@ -43,7 +43,9 @@ public class SpeakerInstance {
|
||||
}
|
||||
}
|
||||
|
||||
public void playAudio(SpeakerPosition position, float volume) {
|
||||
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
|
||||
pushAudio(buffer);
|
||||
|
||||
var soundManager = Minecraft.getInstance().getSoundManager();
|
||||
|
||||
if (sound != null && sound.stream != currentStream) {
|
||||
|
@@ -11,11 +11,14 @@ 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.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;
|
||||
@@ -24,14 +27,15 @@ import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A registry of {@link TurtleUpgradeModeller}s.
|
||||
*
|
||||
* @see dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)
|
||||
*/
|
||||
public final class TurtleUpgradeModellers {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TurtleUpgradeModellers.class);
|
||||
|
||||
private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side) ->
|
||||
new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
|
||||
|
||||
private static final Map<TurtleUpgradeSerialiser<?>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
|
||||
private static volatile boolean fetchedModels;
|
||||
|
||||
/**
|
||||
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
|
||||
@@ -45,12 +49,18 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
|
||||
public static <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
synchronized (turtleModels) {
|
||||
if (turtleModels.containsKey(serialiser)) {
|
||||
throw new IllegalStateException("Modeller already registered for serialiser");
|
||||
}
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
turtleModels.put(serialiser, modeller);
|
||||
if (turtleModels.putIfAbsent(serialiser, modeller) != null) {
|
||||
throw new IllegalStateException("Modeller already registered for serialiser");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +85,7 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
|
||||
public static Stream<ResourceLocation> getDependencies() {
|
||||
fetchedModels = true;
|
||||
return turtleModels.values().stream().flatMap(x -> x.getDependencies().stream());
|
||||
}
|
||||
}
|
||||
|
@@ -174,6 +174,7 @@ public final class LanguageProvider implements DataProvider {
|
||||
// Metrics
|
||||
add(Metrics.COMPUTER_TASKS, "Tasks");
|
||||
add(Metrics.SERVER_TASKS, "Server tasks");
|
||||
add(Metrics.JAVA_ALLOCATION, "Java Allocations");
|
||||
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
|
||||
add(Metrics.FS_OPS, "Filesystem operations");
|
||||
add(Metrics.HTTP_REQUESTS, "HTTP requests");
|
||||
@@ -213,7 +214,6 @@ public final class LanguageProvider implements DataProvider {
|
||||
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
|
||||
addConfigEntry(ConfigSpec.uploadMaxSize, "File upload size limit (bytes)");
|
||||
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
|
||||
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
|
||||
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
|
||||
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
|
||||
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");
|
||||
|
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Wraps an existing {@link DataProvider}, passing generated JSON through {@link PrettyJsonWriter}.
|
||||
*
|
||||
* @param provider The provider to wrap.
|
||||
* @param <T> The type of the provider to wrap.
|
||||
*/
|
||||
public record PrettyDataProvider<T extends DataProvider>(T provider) implements DataProvider {
|
||||
@Override
|
||||
public CompletableFuture<?> run(CachedOutput cachedOutput) {
|
||||
return provider.run(new Output(cachedOutput));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return provider.getName();
|
||||
}
|
||||
|
||||
private record Output(CachedOutput output) implements CachedOutput {
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final HashFunction HASH_FUNCTION = Hashing.sha1();
|
||||
|
||||
@Override
|
||||
public void writeIfNeeded(Path path, byte[] bytes, HashCode hashCode) throws IOException {
|
||||
if (path.getFileName().toString().endsWith(".json")) {
|
||||
bytes = PrettyJsonWriter.reformat(bytes);
|
||||
hashCode = HASH_FUNCTION.hashBytes(bytes);
|
||||
}
|
||||
|
||||
output.writeIfNeeded(path, bytes, hashCode);
|
||||
}
|
||||
}
|
||||
}
|
@@ -22,11 +22,10 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Alternative version of {@link JsonWriter} which attempts to lay out the JSON in a more compact format.
|
||||
* <p>
|
||||
* Yes, this is at least a little deranged.
|
||||
*
|
||||
* @see PrettyDataProvider
|
||||
*/
|
||||
public class PrettyJsonWriter extends JsonWriter {
|
||||
public static final boolean ENABLED = System.getProperty("cct.pretty-json") != null;
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
|
||||
private static final int MAX_WIDTH = 120;
|
||||
@@ -44,17 +43,6 @@ public class PrettyJsonWriter extends JsonWriter {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSON writer. This will either be a pretty or normal version, depending on whether the global flag is
|
||||
* set.
|
||||
*
|
||||
* @param out The writer to emit to.
|
||||
* @return The constructed JSON writer.
|
||||
*/
|
||||
public static JsonWriter createWriter(Writer out) {
|
||||
return ENABLED ? new PrettyJsonWriter(out) : new JsonWriter(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformat a JSON string with our pretty printer.
|
||||
*
|
||||
@@ -62,8 +50,6 @@ public class PrettyJsonWriter extends JsonWriter {
|
||||
* @return The reformatted string.
|
||||
*/
|
||||
public static byte[] reformat(byte[] contents) {
|
||||
if (!ENABLED) return contents;
|
||||
|
||||
JsonElement object;
|
||||
try (var reader = new InputStreamReader(new ByteArrayInputStream(contents), StandardCharsets.UTF_8)) {
|
||||
object = GSON.fromJson(reader, JsonElement.class);
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
@@ -12,7 +13,6 @@ import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.platform.RecipeIngredients;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
@@ -25,22 +25,19 @@ import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.recipes.*;
|
||||
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.DyeColor;
|
||||
import net.minecraft.world.item.DyeItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.ShapedRecipe;
|
||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
||||
@@ -107,14 +104,13 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
private void turtleUpgrades(Consumer<FinishedRecipe> add) {
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||
|
||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
var name = RegistryWrappers.ITEMS.getKey(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())
|
||||
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
|
||||
.group(name.toString())
|
||||
.pattern("#T")
|
||||
.define('T', base.getItem())
|
||||
.define('#', upgrade.getCraftingItem().getItem())
|
||||
@@ -122,9 +118,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("turtle_%s/%s/%s",
|
||||
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
|
||||
))
|
||||
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -142,15 +136,13 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
private void pocketUpgrades(Consumer<FinishedRecipe> add) {
|
||||
for (var pocket : pocketComputerItems()) {
|
||||
var base = pocket.create(-1, null, -1, null);
|
||||
if (base.isEmpty()) continue;
|
||||
|
||||
var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
var name = RegistryWrappers.ITEMS.getKey(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())
|
||||
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
|
||||
.group(name.toString())
|
||||
.pattern("#")
|
||||
.pattern("P")
|
||||
.define('P', base.getItem())
|
||||
@@ -159,9 +151,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("pocket_%s/%s/%s",
|
||||
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
|
||||
))
|
||||
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -191,12 +181,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consumer<ShapelessRecipeBuilder> build) {
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||
|
||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
var group = "%s:turtle_%s_overlay".formatted(ComputerCraftAPI.MOD_ID, nameId);
|
||||
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
|
||||
|
||||
var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem())
|
||||
.group(group)
|
||||
.group(name.withSuffix("_overlay").toString())
|
||||
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
|
||||
build.accept(builder);
|
||||
builder
|
||||
@@ -205,7 +193,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
RecipeWrapper
|
||||
.wrap(ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get(), add)
|
||||
.withExtraData(x -> x.addProperty("overlay", new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay).toString())),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_%s_overlays/%s".formatted(nameId, overlay))
|
||||
name.withSuffix("_overlays/" + overlay)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -254,7 +242,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.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).withExtraData(family(ComputerFamily.ADVANCED)),
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")
|
||||
);
|
||||
|
||||
@@ -278,7 +266,7 @@ 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).withExtraData(family(ComputerFamily.NORMAL)));
|
||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||
@@ -289,7 +277,7 @@ 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).withExtraData(family(ComputerFamily.ADVANCED)));
|
||||
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||
@@ -301,7 +289,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.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).withExtraData(family(ComputerFamily.ADVANCED)),
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")
|
||||
);
|
||||
|
||||
@@ -368,7 +356,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.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).withExtraData(family(ComputerFamily.ADVANCED)),
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")
|
||||
);
|
||||
|
||||
@@ -443,7 +431,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
|
||||
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(RecipeSerializer.SHAPELESS_RECIPE, add)
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
|
||||
.withResultTag(playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c")),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy")
|
||||
);
|
||||
@@ -454,7 +442,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
||||
.save(
|
||||
RecipeWrapper.wrap(RecipeSerializer.SHAPELESS_RECIPE, add)
|
||||
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
|
||||
.withResultTag(playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb")),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200")
|
||||
);
|
||||
@@ -513,19 +501,13 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
|
||||
private static CompoundTag playerHead(String name, String uuid) {
|
||||
var owner = new CompoundTag();
|
||||
owner.putString("Name", name);
|
||||
owner.putString("Id", uuid);
|
||||
var owner = NbtUtils.writeGameProfile(new CompoundTag(), new GameProfile(UUID.fromString(uuid), name));
|
||||
|
||||
var tag = new CompoundTag();
|
||||
tag.put("SkullOwner", owner);
|
||||
tag.put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static Consumer<JsonObject> family(ComputerFamily family) {
|
||||
return json -> json.addProperty("family", family.toString());
|
||||
}
|
||||
|
||||
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
|
||||
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
|
||||
}
|
||||
|
@@ -35,6 +35,8 @@ class TagProvider {
|
||||
tags.tag(ComputerCraftTags.Blocks.WIRED_MODEM).add(ModRegistry.Blocks.CABLE.get(), ModRegistry.Blocks.WIRED_MODEM_FULL.get());
|
||||
tags.tag(ComputerCraftTags.Blocks.MONITOR).add(ModRegistry.Blocks.MONITOR_NORMAL.get(), ModRegistry.Blocks.MONITOR_ADVANCED.get());
|
||||
|
||||
tags.tag(ComputerCraftTags.Blocks.PERIPHERAL_HUB_IGNORE).addTag(ComputerCraftTags.Blocks.WIRED_MODEM);
|
||||
|
||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_ALWAYS_BREAKABLE).addTag(BlockTags.LEAVES).add(
|
||||
Blocks.BAMBOO, Blocks.BAMBOO_SAPLING // Bamboo isn't instabreak for some odd reason.
|
||||
);
|
||||
@@ -56,7 +58,10 @@ class TagProvider {
|
||||
|
||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).addTag(BlockTags.WOOL).add(Blocks.COBWEB);
|
||||
|
||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE).addTag(BlockTags.CAULDRONS).addTag(BlockTags.BEEHIVES);
|
||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE)
|
||||
.addTag(BlockTags.BEEHIVES)
|
||||
.addTag(BlockTags.CAULDRONS)
|
||||
.add(Blocks.COMPOSTER);
|
||||
|
||||
// Make all blocks aside from command computer mineable.
|
||||
tags.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(
|
||||
@@ -74,6 +79,8 @@ class TagProvider {
|
||||
ModRegistry.Blocks.WIRED_MODEM_FULL.get(),
|
||||
ModRegistry.Blocks.CABLE.get()
|
||||
);
|
||||
|
||||
tags.tag(BlockTags.WITHER_IMMUNE).add(ModRegistry.Blocks.COMPUTER_COMMAND.get());
|
||||
}
|
||||
|
||||
public static void itemTags(ItemTagConsumer tags) {
|
||||
|
@@ -18,8 +18,8 @@ import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
@@ -37,7 +37,7 @@ import java.util.stream.Collectors;
|
||||
* @see PocketUpgrades
|
||||
*/
|
||||
public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends UpgradeBase> extends SimpleJsonResourceReloadListener {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
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>(
|
||||
@@ -48,8 +48,8 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
private final String kind;
|
||||
private final ResourceKey<Registry<R>> registry;
|
||||
|
||||
private Map<String, UpgradeWrapper<R, T>> current = Collections.emptyMap();
|
||||
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Collections.emptyMap();
|
||||
private Map<String, UpgradeWrapper<R, T>> current = Map.of();
|
||||
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Map.of();
|
||||
|
||||
public UpgradeManager(String kind, String path, ResourceKey<Registry<R>> registry) {
|
||||
super(GSON, path);
|
||||
@@ -103,13 +103,13 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
try {
|
||||
loadUpgrade(newUpgrades, element.getKey(), element.getValue());
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
LOGGER.error("Error loading {} {} from JSON file", kind, element.getKey(), e);
|
||||
LOG.error("Error loading {} {} from JSON file", kind, element.getKey(), e);
|
||||
}
|
||||
}
|
||||
|
||||
current = Collections.unmodifiableMap(newUpgrades);
|
||||
currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x));
|
||||
LOGGER.info("Loaded {} {}s", current.size(), kind);
|
||||
LOG.info("Loaded {} {}s", current.size(), kind);
|
||||
}
|
||||
|
||||
private void loadUpgrade(Map<String, UpgradeWrapper<R, T>> current, ResourceLocation id, JsonElement json) {
|
||||
@@ -123,7 +123,7 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
// TODO: Can we track which mod this resource came from and use that instead? It's theoretically possible,
|
||||
// but maybe not ideal for datapacks.
|
||||
var modId = id.getNamespace();
|
||||
if (modId.equals("minecraft") || modId.equals("")) modId = ComputerCraftAPI.MOD_ID;
|
||||
if (modId.equals("minecraft") || modId.isEmpty()) modId = ComputerCraftAPI.MOD_ID;
|
||||
|
||||
var upgrade = serialiser.fromJson(id, root);
|
||||
if (!upgrade.getUpgradeID().equals(id)) {
|
||||
|
@@ -12,7 +12,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
final class WiredNetworkChangeImpl implements WiredNetworkChange {
|
||||
private static final WiredNetworkChangeImpl EMPTY = new WiredNetworkChangeImpl(Collections.emptyMap(), Collections.emptyMap());
|
||||
private static final WiredNetworkChangeImpl EMPTY = new WiredNetworkChangeImpl(Map.of(), Map.of());
|
||||
|
||||
private final Map<String, IPeripheral> removed;
|
||||
private final Map<String, IPeripheral> added;
|
||||
@@ -27,11 +27,11 @@ final class WiredNetworkChangeImpl implements WiredNetworkChange {
|
||||
}
|
||||
|
||||
static WiredNetworkChangeImpl added(Map<String, IPeripheral> added) {
|
||||
return added.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Collections.emptyMap(), Collections.unmodifiableMap(added));
|
||||
return added.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Map.of(), Collections.unmodifiableMap(added));
|
||||
}
|
||||
|
||||
static WiredNetworkChangeImpl removed(Map<String, IPeripheral> removed) {
|
||||
return removed.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Collections.unmodifiableMap(removed), Collections.emptyMap());
|
||||
return removed.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Collections.unmodifiableMap(removed), Map.of());
|
||||
}
|
||||
|
||||
static WiredNetworkChangeImpl changeOf(Map<String, IPeripheral> oldPeripherals, Map<String, IPeripheral> newPeripherals) {
|
||||
@@ -39,9 +39,9 @@ final class WiredNetworkChangeImpl implements WiredNetworkChange {
|
||||
if (oldPeripherals.isEmpty() && newPeripherals.isEmpty()) {
|
||||
return EMPTY;
|
||||
} else if (oldPeripherals.isEmpty()) {
|
||||
return new WiredNetworkChangeImpl(Collections.emptyMap(), newPeripherals);
|
||||
return new WiredNetworkChangeImpl(Map.of(), newPeripherals);
|
||||
} else if (newPeripherals.isEmpty()) {
|
||||
return new WiredNetworkChangeImpl(oldPeripherals, Collections.emptyMap());
|
||||
return new WiredNetworkChangeImpl(oldPeripherals, Map.of());
|
||||
}
|
||||
|
||||
Map<String, IPeripheral> added = new HashMap<>(newPeripherals);
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.impl.network.wired;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import dan200.computercraft.api.network.Packet;
|
||||
import dan200.computercraft.api.network.wired.WiredNetwork;
|
||||
import dan200.computercraft.api.network.wired.WiredNode;
|
||||
@@ -57,10 +56,10 @@ final class WiredNetworkImpl implements WiredNetwork {
|
||||
// Move all nodes across into this network, destroying the original nodes.
|
||||
nodes.addAll(otherNodes);
|
||||
for (var node : otherNodes) node.network = this;
|
||||
other.nodes = Collections.emptySet();
|
||||
other.nodes = Set.of();
|
||||
|
||||
// Move all peripherals across,
|
||||
other.peripherals = Collections.emptyMap();
|
||||
other.peripherals = Map.of();
|
||||
peripherals.putAll(otherPeripherals);
|
||||
|
||||
if (!thisPeripherals.isEmpty()) {
|
||||
@@ -217,7 +216,7 @@ final class WiredNetworkImpl implements WiredNetwork {
|
||||
try {
|
||||
// We special case the original node: detaching all peripherals when needed.
|
||||
wired.network = wiredNetwork;
|
||||
wired.peripherals = Collections.emptyMap();
|
||||
wired.peripherals = Map.of();
|
||||
|
||||
// Ensure every network is finalised
|
||||
for (var network : maximals) {
|
||||
@@ -260,7 +259,7 @@ final class WiredNetworkImpl implements WiredNetwork {
|
||||
var change = WiredNetworkChangeImpl.changeOf(oldPeripherals, newPeripherals);
|
||||
if (change.isEmpty()) return;
|
||||
|
||||
wired.peripherals = ImmutableMap.copyOf(newPeripherals);
|
||||
wired.peripherals = Map.copyOf(newPeripherals);
|
||||
|
||||
// Detach the old peripherals then remove them.
|
||||
peripherals.keySet().removeAll(change.peripheralsRemoved().keySet());
|
||||
@@ -333,7 +332,7 @@ final class WiredNetworkImpl implements WiredNetwork {
|
||||
// Detach the old peripherals then remove them from the old network
|
||||
wired.network = wiredNetwork;
|
||||
wired.neighbours.clear();
|
||||
wired.peripherals = Collections.emptyMap();
|
||||
wired.peripherals = Map.of();
|
||||
|
||||
// Broadcast the change
|
||||
if (!peripherals.isEmpty()) WiredNetworkChangeImpl.removed(peripherals).broadcast(wired);
|
||||
|
@@ -13,13 +13,16 @@ import dan200.computercraft.api.network.wired.WiredSender;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public final class WiredNodeImpl implements WiredNode {
|
||||
private @Nullable Set<PacketReceiver> receivers;
|
||||
|
||||
final WiredElement element;
|
||||
Map<String, IPeripheral> peripherals = Collections.emptyMap();
|
||||
Map<String, IPeripheral> peripherals = Map.of();
|
||||
|
||||
final HashSet<WiredNodeImpl> neighbours = new HashSet<>();
|
||||
volatile WiredNetworkImpl network;
|
||||
|
@@ -1,25 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.mixin;
|
||||
|
||||
import dan200.computercraft.data.PrettyJsonWriter;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
@Mixin(targets = "net/minecraft/data/HashCache$CacheUpdater")
|
||||
public class CacheUpdaterMixin {
|
||||
@SuppressWarnings("UnusedMethod")
|
||||
@ModifyArg(
|
||||
method = "writeIfNeeded",
|
||||
at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;write(Ljava/nio/file/Path;[B[Ljava/nio/file/OpenOption;)Ljava/nio/file/Path;"),
|
||||
require = 0
|
||||
)
|
||||
private byte[] reformatJson(byte[] contents) {
|
||||
// It would be cleaner to do this inside DataProvider.saveStable, but Forge's version of Mixin doesn't allow us
|
||||
// to inject into interfaces.
|
||||
return PrettyJsonWriter.reformat(contents);
|
||||
}
|
||||
}
|
@@ -14,12 +14,15 @@ import dan200.computercraft.shared.computer.metrics.ComputerMBean;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
|
||||
import dan200.computercraft.shared.util.DropConsumer;
|
||||
import dan200.computercraft.shared.util.TickScheduler;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
|
||||
@@ -111,4 +114,17 @@ public final class CommonHooks {
|
||||
public static boolean onLivingDrop(Entity entity, ItemStack stack) {
|
||||
return DropConsumer.onLivingDrop(entity, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add items to an existing creative tab.
|
||||
*
|
||||
* @param key The {@link ResourceKey} for this creative tab.
|
||||
* @param context Additional parameters used for building the contents.
|
||||
* @param out The creative tab output to append items to.
|
||||
*/
|
||||
public static void onBuildCreativeTab(ResourceKey<CreativeModeTab> key, CreativeModeTab.ItemDisplayParameters context, CreativeModeTab.Output out) {
|
||||
if (key == CreativeModeTabs.OP_BLOCKS && context.hasPermissions()) {
|
||||
out.accept(ModRegistry.Items.COMPUTER_COMMAND.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||
import dan200.computercraft.shared.data.ConstantLootConditionSerializer;
|
||||
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
||||
@@ -70,6 +71,10 @@ import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
|
||||
import dan200.computercraft.shared.recipe.CustomShapedRecipe;
|
||||
import dan200.computercraft.shared.recipe.CustomShapelessRecipe;
|
||||
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
||||
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
||||
import dan200.computercraft.shared.turtle.FurnaceRefuelHandler;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
@@ -79,8 +84,6 @@ 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.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.ImpostorRecipe;
|
||||
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||
@@ -137,19 +140,17 @@ public final class ModRegistry {
|
||||
}
|
||||
|
||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
|
||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
|
||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL));
|
||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
|
||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
|
||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED));
|
||||
|
||||
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new CommandComputerBlock<>(
|
||||
computerProperties().strength(-1, 6000000.0F),
|
||||
ComputerFamily.COMMAND, BlockEntities.COMPUTER_COMMAND
|
||||
));
|
||||
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command",
|
||||
() -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND));
|
||||
|
||||
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
|
||||
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
|
||||
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), BlockEntities.TURTLE_NORMAL));
|
||||
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
|
||||
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
|
||||
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD).explosionResistance(TurtleBlock.IMMUNE_EXPLOSION_RESISTANCE), BlockEntities.TURTLE_ADVANCED));
|
||||
|
||||
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties().mapColor(MapColor.STONE)));
|
||||
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties().mapColor(MapColor.STONE)));
|
||||
@@ -190,9 +191,9 @@ public final class ModRegistry {
|
||||
ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new CommandComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s));
|
||||
|
||||
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_NORMAL =
|
||||
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, ComputerFamily.NORMAL));
|
||||
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, () -> Config.turtleFuelLimit, ComputerFamily.NORMAL));
|
||||
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_ADVANCED =
|
||||
ofBlock(Blocks.TURTLE_ADVANCED, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_ADVANCED.get(), p, s, ComputerFamily.ADVANCED));
|
||||
ofBlock(Blocks.TURTLE_ADVANCED, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_ADVANCED.get(), p, s, () -> Config.advancedTurtleFuelLimit, ComputerFamily.ADVANCED));
|
||||
|
||||
public static final RegistryEntry<BlockEntityType<SpeakerBlockEntity>> SPEAKER =
|
||||
ofBlock(Blocks.SPEAKER, (p, s) -> new SpeakerBlockEntity(BlockEntities.SPEAKER.get(), p, s));
|
||||
@@ -309,7 +310,10 @@ public final class ModRegistry {
|
||||
() -> new MenuType<>(PrinterMenu::new, FeatureFlags.VANILLA_SET));
|
||||
|
||||
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
|
||||
() -> ContainerData.toType(HeldItemContainerData::new, HeldItemMenu::createPrintout));
|
||||
() -> ContainerData.toType(
|
||||
HeldItemContainerData::new,
|
||||
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
|
||||
));
|
||||
|
||||
public static final RegistryEntry<MenuType<ViewComputerMenu>> VIEW_COMPUTER = REGISTRY.register("view_computer",
|
||||
() -> ContainerData.toType(ComputerContainerData::new, ViewComputerMenu::new));
|
||||
@@ -359,17 +363,21 @@ public final class ModRegistry {
|
||||
return REGISTRY.register(name, () -> new SimpleCraftingRecipeSerializer<>(factory));
|
||||
}
|
||||
|
||||
public static final RegistryEntry<RecipeSerializer<CustomShapedRecipe>> SHAPED = REGISTRY.register("shaped", () -> CustomShapedRecipe.serialiser(CustomShapedRecipe::new));
|
||||
public static final RegistryEntry<RecipeSerializer<CustomShapelessRecipe>> SHAPELESS = REGISTRY.register("shapeless", () -> CustomShapelessRecipe.serialiser(CustomShapelessRecipe::new));
|
||||
|
||||
public static final RegistryEntry<RecipeSerializer<ImpostorShapedRecipe>> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", () -> CustomShapedRecipe.serialiser(ImpostorShapedRecipe::new));
|
||||
public static final RegistryEntry<RecipeSerializer<ImpostorShapelessRecipe>> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", () -> CustomShapelessRecipe.serialiser(ImpostorShapelessRecipe::new));
|
||||
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
|
||||
public static final RegistryEntry<TurtleRecipe.Serializer> TURTLE = REGISTRY.register("turtle", TurtleRecipe.Serializer::new);
|
||||
public static final RegistryEntry<RecipeSerializer<TurtleRecipe>> TURTLE = REGISTRY.register("turtle", () -> TurtleRecipe.validatingSerialiser(TurtleRecipe::of));
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
|
||||
public static final RegistryEntry<TurtleOverlayRecipe.Serializer> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serializer::new);
|
||||
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serialiser::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
|
||||
public static final RegistryEntry<ComputerUpgradeRecipe.Serializer> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
|
||||
public static final RegistryEntry<ImpostorRecipe.Serializer> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", ImpostorRecipe.Serializer::new);
|
||||
public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new);
|
||||
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", () -> CustomShapedRecipe.validatingSerialiser(ComputerUpgradeRecipe::of));
|
||||
}
|
||||
|
||||
public static class Permissions {
|
||||
@@ -458,8 +466,8 @@ public final class ModRegistry {
|
||||
* Register any objects which must be done on the main thread.
|
||||
*/
|
||||
public static void registerMainThread() {
|
||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
CauldronInteraction.WATER.put(Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
CauldronInteraction.WATER.put(Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
}
|
||||
|
||||
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
||||
|
@@ -89,7 +89,7 @@ public final class CommandComputerCraft {
|
||||
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument("computer", manyComputers())
|
||||
.suggests((context, builder) -> Suggestions.empty())
|
||||
)
|
||||
.argManyValue("args", StringArgumentType.string(), Collections.emptyList())
|
||||
.argManyValue("args", StringArgumentType.string(), List.of())
|
||||
.executes((c, a) -> queue(getComputersArgument(c, "computer"), a)))
|
||||
|
||||
.then(command("view")
|
||||
@@ -300,7 +300,7 @@ public final class CommandComputerCraft {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static final List<AggregatedMetric> DEFAULT_FIELDS = Arrays.asList(
|
||||
private static final List<AggregatedMetric> DEFAULT_FIELDS = List.of(
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.COUNT),
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.NONE),
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG)
|
||||
|
@@ -32,7 +32,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
|
||||
private static final ComputersArgumentType MANY = new ComputersArgumentType(false);
|
||||
private static final ComputersArgumentType SOME = new ComputersArgumentType(true);
|
||||
|
||||
private static final List<String> EXAMPLES = Arrays.asList(
|
||||
private static final List<String> EXAMPLES = List.of(
|
||||
"0", "#0", "@Label", "~Advanced"
|
||||
);
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
|
||||
var instance = reader.readInt();
|
||||
computers = s -> {
|
||||
var computer = ServerContext.get(s.getServer()).registry().get(instance);
|
||||
return computer == null ? Collections.emptyList() : Collections.singletonList(computer);
|
||||
return computer == null ? List.of() : List.of(computer);
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,6 @@ import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
@@ -63,7 +62,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
|
||||
}
|
||||
|
||||
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argManyValue(String name, ArgumentType<T> type, T defaultValue) {
|
||||
return argManyValue(name, type, Collections.singletonList(defaultValue));
|
||||
return argManyValue(name, type, List.of(defaultValue));
|
||||
}
|
||||
|
||||
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argMany(String name, ArgumentType<T> type, Supplier<List<T>> empty) {
|
||||
|
@@ -7,7 +7,7 @@ package dan200.computercraft.shared.command.text;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.command.CommandUtils;
|
||||
import dan200.computercraft.shared.network.client.ChatTableClientMessage;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -105,7 +105,7 @@ public class TableBuilder {
|
||||
if (CommandUtils.isPlayer(source)) {
|
||||
trim(18);
|
||||
var player = (ServerPlayer) Nullability.assertNonNull(source.getEntity());
|
||||
PlatformHelper.get().sendToPlayer(new ChatTableClientMessage(this), player);
|
||||
ServerNetworking.sendToPlayer(new ChatTableClientMessage(this), player);
|
||||
} else {
|
||||
trim(100);
|
||||
new ServerTableFormatter(source).display(this);
|
||||
|
@@ -4,8 +4,6 @@
|
||||
|
||||
package dan200.computercraft.shared.common;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.network.container.HeldItemContainerData;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
@@ -28,10 +26,6 @@ public class HeldItemMenu extends AbstractContainerMenu {
|
||||
stack = player.getItemInHand(hand).copy();
|
||||
}
|
||||
|
||||
public static HeldItemMenu createPrintout(int id, Inventory inventory, HeldItemContainerData data) {
|
||||
return new HeldItemMenu(ModRegistry.Menus.PRINTOUT.get(), id, inventory.player, data.getHand());
|
||||
}
|
||||
|
||||
public ItemStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
@@ -134,12 +134,12 @@ public class CommandAPI implements ILuaAPI {
|
||||
public final List<String> list(IArguments args) throws LuaException {
|
||||
var server = computer.getLevel().getServer();
|
||||
|
||||
if (server == null) return Collections.emptyList();
|
||||
if (server == null) return List.of();
|
||||
CommandNode<CommandSourceStack> node = server.getCommands().getDispatcher().getRoot();
|
||||
for (var j = 0; j < args.count(); j++) {
|
||||
var name = args.getString(j);
|
||||
node = node.getChild(name);
|
||||
if (!(node instanceof LiteralCommandNode)) return Collections.emptyList();
|
||||
if (!(node instanceof LiteralCommandNode)) return List.of();
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.shared.computer.blocks;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||
@@ -42,13 +41,11 @@ import javax.annotation.Nullable;
|
||||
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
|
||||
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
|
||||
|
||||
private final ComputerFamily family;
|
||||
protected final RegistryEntry<BlockEntityType<T>> type;
|
||||
private final BlockEntityTicker<T> serverTicker = (level, pos, state, computer) -> computer.serverTick();
|
||||
|
||||
protected AbstractComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
|
||||
protected AbstractComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
|
||||
super(settings);
|
||||
this.family = family;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -82,10 +79,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
|
||||
protected abstract ItemStack getItem(AbstractComputerBlockEntity tile);
|
||||
|
||||
public ComputerFamily getFamily() {
|
||||
return family;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public int getSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {
|
||||
|
@@ -25,7 +25,6 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.*;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -51,7 +50,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
private boolean fresh = false;
|
||||
|
||||
private int invalidSides = 0;
|
||||
private final ComponentAccess<IPeripheral> peripherals = PlatformHelper.get().createPeripheralAccess(d -> invalidSides |= 1 << d.ordinal());
|
||||
private final ComponentAccess<IPeripheral> peripherals = PlatformHelper.get().createPeripheralAccess(this, d -> invalidSides |= 1 << d.ordinal());
|
||||
|
||||
private LockCode lockCode = LockCode.NO_LOCK;
|
||||
|
||||
@@ -218,7 +217,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
var localDir = remapToLocalSide(dir);
|
||||
if (isPeripheralBlockedOnSide(localDir)) return;
|
||||
|
||||
var peripheral = peripherals.get((ServerLevel) getLevel(), getBlockPos(), dir);
|
||||
var peripheral = peripherals.get(dir);
|
||||
computer.setPeripheral(localDir, peripheral);
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import net.minecraft.world.level.block.GameMasterBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
@@ -17,7 +16,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
|
||||
*/
|
||||
public class CommandComputerBlock<T extends CommandComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
|
||||
public CommandComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
|
||||
super(settings, family, type);
|
||||
public CommandComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
|
||||
super(settings, type);
|
||||
}
|
||||
}
|
||||
|
@@ -20,28 +20,24 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandComputerBlockEntity extends ComputerBlockEntity {
|
||||
public class CommandReceiver implements CommandSource {
|
||||
private final Map<Integer, String> output = new HashMap<>();
|
||||
private final List<String> output = new ArrayList<>();
|
||||
|
||||
public void clearOutput() {
|
||||
output.clear();
|
||||
}
|
||||
|
||||
public Map<Integer, String> getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
public Map<Integer, String> copyOutput() {
|
||||
return new HashMap<>(output);
|
||||
public List<String> copyOutput() {
|
||||
return new ArrayList<>(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSystemMessage(Component textComponent) {
|
||||
output.put(output.size() + 1, textComponent.getString());
|
||||
output.add(textComponent.getString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -4,9 +4,8 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItemFactory;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -25,8 +24,8 @@ public class ComputerBlock<T extends ComputerBlockEntity> extends AbstractComput
|
||||
public static final EnumProperty<ComputerState> STATE = EnumProperty.create("state", ComputerState.class);
|
||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||
|
||||
public ComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
|
||||
super(settings, family, type);
|
||||
public ComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
|
||||
super(settings, type);
|
||||
registerDefaultState(defaultBlockState()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(STATE, ComputerState.OFF)
|
||||
@@ -46,6 +45,9 @@ public class ComputerBlock<T extends ComputerBlockEntity> extends AbstractComput
|
||||
|
||||
@Override
|
||||
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
|
||||
return tile instanceof ComputerBlockEntity ? ComputerItemFactory.create((ComputerBlockEntity) tile) : ItemStack.EMPTY;
|
||||
if (!(tile instanceof ComputerBlockEntity computer)) return ItemStack.EMPTY;
|
||||
if (!(asItem() instanceof ComputerItem item)) return ItemStack.EMPTY;
|
||||
|
||||
return item.create(computer.getComputerID(), computer.getLabel());
|
||||
}
|
||||
}
|
||||
|
@@ -32,11 +32,9 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
|
||||
|
||||
@Override
|
||||
protected ServerComputer createComputer(int id) {
|
||||
var family = getFamily();
|
||||
return new ServerComputer(
|
||||
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
||||
family, Config.computerTermWidth,
|
||||
Config.computerTermHeight
|
||||
getFamily(), Config.computerTermWidth, Config.computerTermHeight
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -7,5 +7,5 @@ package dan200.computercraft.shared.computer.core;
|
||||
public enum ComputerFamily {
|
||||
NORMAL,
|
||||
ADVANCED,
|
||||
COMMAND
|
||||
COMMAND,
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.api.filesystem.MountConstants.NO_SUCH_FILE;
|
||||
|
||||
/**
|
||||
* A mount backed by Minecraft's {@link ResourceManager}.
|
||||
*
|
||||
@@ -29,8 +31,6 @@ import java.util.Map;
|
||||
public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResourceMount.class);
|
||||
|
||||
private static final byte[] TEMP_BUFFER = new byte[8192];
|
||||
|
||||
/**
|
||||
* Maintain a cache of currently loaded resource mounts. This cache is invalidated when currentManager changes.
|
||||
*/
|
||||
@@ -60,7 +60,7 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
|
||||
var hasAny = false;
|
||||
String existingNamespace = null;
|
||||
|
||||
var newRoot = new FileEntry("", new ResourceLocation(namespace, subPath));
|
||||
var newRoot = new FileEntry(new ResourceLocation(namespace, subPath));
|
||||
for (var file : manager.listResources(subPath, s -> true).keySet()) {
|
||||
existingNamespace = file.getNamespace();
|
||||
|
||||
@@ -68,7 +68,11 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
|
||||
if (!FileSystem.contains(subPath, file.getPath())) continue; // Some packs seem to include the parent?
|
||||
|
||||
var localPath = FileSystem.toLocal(file.getPath(), subPath);
|
||||
create(newRoot, localPath);
|
||||
try {
|
||||
getOrCreateChild(newRoot, localPath, this::createEntry);
|
||||
} catch (ResourceLocationException e) {
|
||||
LOG.warn("Cannot create resource location for {} ({})", localPath, e.getMessage());
|
||||
}
|
||||
hasAny = true;
|
||||
}
|
||||
|
||||
@@ -83,65 +87,24 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
|
||||
}
|
||||
}
|
||||
|
||||
private void create(FileEntry lastEntry, String path) {
|
||||
var lastIndex = 0;
|
||||
while (lastIndex < path.length()) {
|
||||
var nextIndex = path.indexOf('/', lastIndex);
|
||||
if (nextIndex < 0) nextIndex = path.length();
|
||||
|
||||
var part = path.substring(lastIndex, nextIndex);
|
||||
if (lastEntry.children == null) lastEntry.children = new HashMap<>();
|
||||
|
||||
var nextEntry = lastEntry.children.get(part);
|
||||
if (nextEntry == null) {
|
||||
ResourceLocation childPath;
|
||||
try {
|
||||
childPath = new ResourceLocation(namespace, subPath + "/" + path);
|
||||
} catch (ResourceLocationException e) {
|
||||
LOG.warn("Cannot create resource location for {} ({})", part, e.getMessage());
|
||||
return;
|
||||
}
|
||||
lastEntry.children.put(part, nextEntry = new FileEntry(path, childPath));
|
||||
}
|
||||
|
||||
lastEntry = nextEntry;
|
||||
lastIndex = nextIndex + 1;
|
||||
}
|
||||
private FileEntry createEntry(String path) {
|
||||
return new FileEntry(new ResourceLocation(namespace, subPath + "/" + path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize(FileEntry file) {
|
||||
protected byte[] getFileContents(String path, FileEntry file) throws IOException {
|
||||
var resource = manager.getResource(file.identifier).orElse(null);
|
||||
if (resource == null) return 0;
|
||||
|
||||
try (var stream = resource.open()) {
|
||||
int total = 0, read = 0;
|
||||
do {
|
||||
total += read;
|
||||
read = stream.read(TEMP_BUFFER);
|
||||
} while (read > 0);
|
||||
|
||||
return total;
|
||||
} catch (IOException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getContents(FileEntry file) throws IOException {
|
||||
var resource = manager.getResource(file.identifier).orElse(null);
|
||||
if (resource == null) throw new FileOperationException(file.path, NO_SUCH_FILE);
|
||||
if (resource == null) throw new FileOperationException(path, NO_SUCH_FILE);
|
||||
|
||||
try (var stream = resource.open()) {
|
||||
return stream.readAllBytes();
|
||||
}
|
||||
}
|
||||
|
||||
protected static class FileEntry extends ArchiveMount.FileEntry<FileEntry> {
|
||||
protected static final class FileEntry extends ArchiveMount.FileEntry<FileEntry> {
|
||||
final ResourceLocation identifier;
|
||||
|
||||
FileEntry(String path, ResourceLocation identifier) {
|
||||
super(path);
|
||||
FileEntry(ResourceLocation identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
@@ -142,7 +142,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
|
||||
for (var player : server.getPlayerList().getPlayers()) {
|
||||
if (player.containerMenu instanceof ComputerMenu && ((ComputerMenu) player.containerMenu).getComputer() == this) {
|
||||
PlatformHelper.get().sendToPlayer(createPacket.apply(player.containerMenu), player);
|
||||
ServerNetworking.sendToPlayer(createPacket.apply(player.containerMenu), player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -22,11 +21,8 @@ import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractComputerItem extends BlockItem implements IComputerItem, IMedia {
|
||||
private final ComputerFamily family;
|
||||
|
||||
public AbstractComputerItem(AbstractComputerBlock<?> block, Properties settings) {
|
||||
super(block, settings);
|
||||
family = block.getFamily();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,13 +41,6 @@ public abstract class AbstractComputerItem extends BlockItem implements ICompute
|
||||
return IComputerItem.super.getLabel(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ComputerFamily getFamily() {
|
||||
return family;
|
||||
}
|
||||
|
||||
// IMedia implementation
|
||||
|
||||
@Override
|
||||
public boolean setLabel(ItemStack stack, @Nullable String label) {
|
||||
if (label != null) {
|
||||
|
@@ -5,8 +5,8 @@
|
||||
package dan200.computercraft.shared.computer.items;
|
||||
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -24,9 +24,9 @@ public class ComputerItem extends AbstractComputerItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
||||
var result = ComputerItemFactory.create(getComputerID(stack), null, family);
|
||||
if (stack.hasCustomHoverName()) result.setHoverName(stack.getHoverName());
|
||||
return result;
|
||||
public ItemStack changeItem(ItemStack stack, Item newItem) {
|
||||
return newItem instanceof ComputerItem computer
|
||||
? computer.create(getComputerID(stack), getLabel(stack))
|
||||
: ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +0,0 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
package dan200.computercraft.shared.computer.items;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class ComputerItemFactory {
|
||||
private ComputerItemFactory() {
|
||||
}
|
||||
|
||||
public static ItemStack create(ComputerBlockEntity tile) {
|
||||
return create(tile.getComputerID(), tile.getLabel(), tile.getFamily());
|
||||
}
|
||||
|
||||
public static ItemStack create(int id, @Nullable String label, ComputerFamily family) {
|
||||
return switch (family) {
|
||||
case NORMAL -> ModRegistry.Items.COMPUTER_NORMAL.get().create(id, label);
|
||||
case ADVANCED -> ModRegistry.Items.COMPUTER_ADVANCED.get().create(id, label);
|
||||
case COMMAND -> ModRegistry.Items.COMPUTER_COMMAND.get().create(id, label);
|
||||
};
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.items;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -21,7 +21,15 @@ public interface IComputerItem {
|
||||
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
|
||||
}
|
||||
|
||||
ComputerFamily getFamily();
|
||||
|
||||
ItemStack withFamily(ItemStack stack, ComputerFamily family);
|
||||
/**
|
||||
* Create a new stack, changing the underlying item.
|
||||
* <p>
|
||||
* This should copy the computer's data to a different item of the same type (for instance, converting a normal
|
||||
* computer to an advanced one).
|
||||
*
|
||||
* @param stack The current computer stack.
|
||||
* @param newItem The new item.
|
||||
* @return The new stack, possibly {@linkplain ItemStack#EMPTY empty} if {@code newItem} is of the same type.
|
||||
*/
|
||||
ItemStack changeItem(ItemStack stack, Item newItem);
|
||||
}
|
||||
|
@@ -4,9 +4,14 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.menu;
|
||||
|
||||
import dan200.computercraft.shared.computer.upload.*;
|
||||
import dan200.computercraft.core.apis.handles.ByteBufferChannel;
|
||||
import dan200.computercraft.core.apis.transfer.TransferredFile;
|
||||
import dan200.computercraft.core.apis.transfer.TransferredFiles;
|
||||
import dan200.computercraft.shared.computer.upload.FileSlice;
|
||||
import dan200.computercraft.shared.computer.upload.FileUpload;
|
||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.network.client.UploadResultMessage;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -18,7 +23,6 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The default concrete implementation of {@link ServerInputHandler}.
|
||||
@@ -134,7 +138,7 @@ public class ServerInputState<T extends AbstractContainerMenu & ComputerMenu> im
|
||||
return;
|
||||
}
|
||||
|
||||
PlatformHelper.get().sendToPlayer(finishUpload(uploader), uploader);
|
||||
ServerNetworking.sendToPlayer(finishUpload(uploader), uploader);
|
||||
}
|
||||
|
||||
private UploadResultMessage finishUpload(ServerPlayer player) {
|
||||
@@ -150,8 +154,14 @@ public class ServerInputState<T extends AbstractContainerMenu & ComputerMenu> im
|
||||
}
|
||||
}
|
||||
|
||||
computer.queueEvent("file_transfer", new Object[]{
|
||||
new TransferredFiles(player, owner, toUpload.stream().map(x -> new TransferredFile(x.getName(), x.getBytes())).collect(Collectors.toList())),
|
||||
computer.queueEvent(TransferredFiles.EVENT, new Object[]{
|
||||
new TransferredFiles(
|
||||
toUpload.stream().map(x -> new TransferredFile(x.getName(), new ByteBufferChannel(x.getBytes()))).toList(),
|
||||
() -> {
|
||||
if (player.isAlive() && player.containerMenu == owner) {
|
||||
ServerNetworking.sendToPlayer(UploadResultMessage.consumed(owner), player);
|
||||
}
|
||||
}),
|
||||
});
|
||||
return UploadResultMessage.queued(owner);
|
||||
}
|
||||
|
@@ -46,12 +46,14 @@ public final class GlobalMetrics {
|
||||
* Add a new global metrics observer. This will receive metrics data for all computers.
|
||||
*
|
||||
* @param tracker The observer to add.
|
||||
* @return Whether the observer was added. {@code false} if the observer was already registered.
|
||||
*/
|
||||
public void addObserver(ComputerMetricsObserver tracker) {
|
||||
public boolean addObserver(ComputerMetricsObserver tracker) {
|
||||
synchronized (lock) {
|
||||
if (trackers.contains(tracker)) return;
|
||||
if (trackers.contains(tracker)) return false;
|
||||
trackers.add(tracker);
|
||||
enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +61,13 @@ public final class GlobalMetrics {
|
||||
* Remove a previously-registered global metrics observer.
|
||||
*
|
||||
* @param tracker The observer to add.
|
||||
* @return Whether the observer was removed. {@code false} if the observer was not registered.
|
||||
*/
|
||||
public void removeObserver(ComputerMetricsObserver tracker) {
|
||||
public boolean removeObserver(ComputerMetricsObserver tracker) {
|
||||
synchronized (lock) {
|
||||
trackers.remove(tracker);
|
||||
var changed = trackers.remove(tracker);
|
||||
enabled = !trackers.isEmpty();
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.metrics.ComputerMetricsObserver;
|
||||
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -21,29 +22,31 @@ import java.util.Map;
|
||||
*/
|
||||
public class BasicComputerMetricsObserver implements ComputerMetricsObserver {
|
||||
private final GlobalMetrics owner;
|
||||
private boolean tracking = false;
|
||||
|
||||
@GuardedBy("this")
|
||||
private final List<ComputerMetrics> timings = new ArrayList<>();
|
||||
|
||||
@GuardedBy("this")
|
||||
private final Map<ServerComputer, ComputerMetrics> timingLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
public BasicComputerMetricsObserver(GlobalMetrics owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
if (!tracking) owner.addObserver(this);
|
||||
tracking = true;
|
||||
public void start() {
|
||||
if (!owner.addObserver(this)) return;
|
||||
|
||||
timings.clear();
|
||||
timingLookup.clear();
|
||||
synchronized (this) {
|
||||
timings.clear();
|
||||
timingLookup.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean stop() {
|
||||
if (!tracking) return false;
|
||||
|
||||
owner.removeObserver(this);
|
||||
tracking = false;
|
||||
timingLookup.clear();
|
||||
public boolean stop() {
|
||||
if (!owner.removeObserver(this)) return false;
|
||||
synchronized (this) {
|
||||
timingLookup.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,6 +60,7 @@ public class BasicComputerMetricsObserver implements ComputerMetricsObserver {
|
||||
return new ArrayList<>(timings);
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private ComputerMetrics getMetrics(ServerComputer computer) {
|
||||
var existing = timingLookup.get(computer);
|
||||
if (existing != null) return existing;
|
||||
|
@@ -5,31 +5,20 @@
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import dan200.computercraft.shared.recipe.CustomShapedRecipe;
|
||||
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
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.level.Level;
|
||||
|
||||
/**
|
||||
* Represents a recipe which converts a computer from one form into another.
|
||||
* A recipe which converts a computer from one form into another.
|
||||
*/
|
||||
public abstract class ComputerConvertRecipe extends ShapedRecipe {
|
||||
private final String group;
|
||||
private final ItemStack result;
|
||||
|
||||
public ComputerConvertRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
|
||||
super(identifier, group, category, width, height, ingredients, result);
|
||||
this.group = group;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public ItemStack getResultItem() {
|
||||
return result;
|
||||
public abstract class ComputerConvertRecipe extends CustomShapedRecipe {
|
||||
public ComputerConvertRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) {
|
||||
super(identifier, recipe);
|
||||
}
|
||||
|
||||
protected abstract ItemStack convert(IComputerItem item, ItemStack stack);
|
||||
@@ -55,9 +44,4 @@ public abstract class ComputerConvertRecipe extends ShapedRecipe {
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
@@ -1,72 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2018 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.util.RecipeUtil;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
|
||||
public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe {
|
||||
private final ComputerFamily family;
|
||||
|
||||
public ComputerFamilyRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
|
||||
super(identifier, group, category, width, height, ingredients, result);
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
public ComputerFamily getFamily() {
|
||||
return family;
|
||||
}
|
||||
|
||||
public abstract static class Serializer<T extends ComputerFamilyRecipe> implements RecipeSerializer<T> {
|
||||
protected abstract T create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family);
|
||||
|
||||
@Override
|
||||
public T fromJson(ResourceLocation identifier, JsonObject json) {
|
||||
var group = GsonHelper.getAsString(json, "group", "");
|
||||
var category = CraftingBookCategory.CODEC.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC);
|
||||
var family = RecipeUtil.getFamily(json, "family");
|
||||
|
||||
var template = RecipeUtil.getTemplate(json);
|
||||
var result = itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
|
||||
|
||||
return create(identifier, group, category, template.width(), template.height(), template.ingredients(), result, family);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
|
||||
var width = buf.readVarInt();
|
||||
var height = buf.readVarInt();
|
||||
var group = buf.readUtf();
|
||||
var category = buf.readEnum(CraftingBookCategory.class);
|
||||
|
||||
var ingredients = NonNullList.withSize(width * height, Ingredient.EMPTY);
|
||||
for (var i = 0; i < ingredients.size(); i++) ingredients.set(i, Ingredient.fromNetwork(buf));
|
||||
|
||||
var result = buf.readItem();
|
||||
var family = buf.readEnum(ComputerFamily.class);
|
||||
return create(identifier, group, category, width, height, ingredients, result, family);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNetwork(FriendlyByteBuf buf, T recipe) {
|
||||
buf.writeVarInt(recipe.getWidth());
|
||||
buf.writeVarInt(recipe.getHeight());
|
||||
buf.writeUtf(recipe.getGroup());
|
||||
buf.writeEnum(recipe.category());
|
||||
for (var ingredient : recipe.getIngredients()) ingredient.toNetwork(buf);
|
||||
buf.writeItem(recipe.getResultItem());
|
||||
buf.writeEnum(recipe.getFamily());
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,35 +4,44 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import com.mojang.serialization.DataResult;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
|
||||
public final class ComputerUpgradeRecipe extends ComputerFamilyRecipe {
|
||||
private ComputerUpgradeRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
|
||||
super(identifier, group, category, width, height, ingredients, result, family);
|
||||
/**
|
||||
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting to it a new item (for instance a normal
|
||||
* turtle to an advanced one).
|
||||
*
|
||||
* @see IComputerItem#changeItem(ItemStack, Item)
|
||||
*/
|
||||
public final class ComputerUpgradeRecipe extends ComputerConvertRecipe {
|
||||
private final Item result;
|
||||
|
||||
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) {
|
||||
super(identifier, recipe);
|
||||
this.result = recipe.result().getItem();
|
||||
}
|
||||
|
||||
public static DataResult<ComputerUpgradeRecipe> of(ResourceLocation id, ShapedRecipeSpec recipe) {
|
||||
if (!(recipe.result().getItem() instanceof IComputerItem)) {
|
||||
return DataResult.error(() -> recipe.result().getItem() + " is not a computer item");
|
||||
}
|
||||
|
||||
return DataResult.success(new ComputerUpgradeRecipe(id, recipe));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack convert(IComputerItem item, ItemStack stack) {
|
||||
return item.withFamily(stack, getFamily());
|
||||
return item.changeItem(stack, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeSerializer<?> getSerializer() {
|
||||
public RecipeSerializer<ComputerUpgradeRecipe> getSerializer() {
|
||||
return ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get();
|
||||
}
|
||||
|
||||
public static class Serializer extends ComputerFamilyRecipe.Serializer<ComputerUpgradeRecipe> {
|
||||
@Override
|
||||
protected ComputerUpgradeRecipe create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
|
||||
return new ComputerUpgradeRecipe(identifier, group, category, width, height, ingredients, result, family);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,6 @@ public final class ConfigSpec {
|
||||
public static final ConfigFile.Value<Integer> computerSpaceLimit;
|
||||
public static final ConfigFile.Value<Integer> floppySpaceLimit;
|
||||
public static final ConfigFile.Value<Integer> maximumFilesOpen;
|
||||
public static final ConfigFile.Value<Boolean> disableLua51Features;
|
||||
public static final ConfigFile.Value<String> defaultComputerSettings;
|
||||
public static final ConfigFile.Value<Boolean> logComputerErrors;
|
||||
public static final ConfigFile.Value<Boolean> commandRequireCreative;
|
||||
@@ -115,12 +114,6 @@ public final class ConfigSpec {
|
||||
.comment("Set how many files a computer can have open at the same time. Set to 0 for unlimited.")
|
||||
.defineInRange("maximum_open_files", CoreConfig.maximumFilesOpen, 0, Integer.MAX_VALUE);
|
||||
|
||||
disableLua51Features = builder
|
||||
.comment("""
|
||||
Set this to true to disable Lua 5.1 functions that will be removed in a future
|
||||
update. Useful for ensuring forward compatibility of your programs now.""")
|
||||
.define("disable_lua51_features", CoreConfig.disableLua51Features);
|
||||
|
||||
defaultComputerSettings = builder
|
||||
.comment("""
|
||||
A comma separated list of default system settings to set on new computers.
|
||||
@@ -231,7 +224,7 @@ public final class ConfigSpec {
|
||||
.defineInRange("max_requests", CoreConfig.httpMaxRequests, 0, Integer.MAX_VALUE);
|
||||
|
||||
httpMaxWebsockets = builder
|
||||
.comment("The number of websockets a computer can have open at one time. Set to 0 for unlimited.")
|
||||
.comment("The number of websockets a computer can have open at one time.")
|
||||
.defineInRange("max_websockets", CoreConfig.httpMaxWebsockets, 1, Integer.MAX_VALUE);
|
||||
|
||||
builder
|
||||
@@ -395,7 +388,6 @@ public final class ConfigSpec {
|
||||
Config.floppySpaceLimit = floppySpaceLimit.get();
|
||||
Config.uploadMaxSize = uploadMaxSize.get();
|
||||
CoreConfig.maximumFilesOpen = maximumFilesOpen.get();
|
||||
CoreConfig.disableLua51Features = disableLua51Features.get();
|
||||
CoreConfig.defaultComputerSettings = defaultComputerSettings.get();
|
||||
Config.commandRequireCreative = commandRequireCreative.get();
|
||||
|
||||
|
@@ -12,7 +12,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ public final class BlockNamedEntityLootCondition implements LootItemCondition {
|
||||
|
||||
@Override
|
||||
public Set<LootContextParam<?>> getReferencedContextParams() {
|
||||
return Collections.singleton(LootContextParams.BLOCK_ENTITY);
|
||||
return Set.of(LootContextParams.BLOCK_ENTITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -12,7 +12,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ public final class HasComputerIdLootCondition implements LootItemCondition {
|
||||
|
||||
@Override
|
||||
public Set<LootContextParam<?>> getReferencedContextParams() {
|
||||
return Collections.singleton(LootContextParams.BLOCK_ENTITY);
|
||||
return Set.of(LootContextParams.BLOCK_ENTITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -12,7 +12,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ public final class PlayerCreativeLootCondition implements LootItemCondition {
|
||||
|
||||
@Override
|
||||
public Set<LootContextParam<?>> getReferencedContextParams() {
|
||||
return Collections.singleton(LootContextParams.THIS_ENTITY);
|
||||
return Set.of(LootContextParams.THIS_ENTITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -61,7 +61,7 @@ public class ItemDetails {
|
||||
|
||||
/*
|
||||
* Used to hide some data from ItemStack tooltip.
|
||||
* @see https://minecraft.gamepedia.com/Tutorials/Command_NBT_tags
|
||||
* @see https://minecraft.wiki/w/Tutorials/Command_NBT_tags
|
||||
* @see ItemStack#getTooltip
|
||||
*/
|
||||
var hideFlags = tag != null ? tag.getInt("HideFlags") : 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user