mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-01 22:22:59 +00:00
Compare commits
12 Commits
v1.20.1-1.
...
v1.20.1-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8be6b1b772 | ||
|
|
104d5e70de | ||
|
|
e3bda2f763 | ||
|
|
234f69e8e5 | ||
|
|
ed3a17f9b9 | ||
|
|
0349c2b1f9 | ||
|
|
03f9e6bd6d | ||
|
|
9d8c933a14 | ||
|
|
78bb3da58c | ||
|
|
39a5e40c92 | ||
|
|
763ba51919 | ||
|
|
cf6ec8c28f |
@@ -13,6 +13,8 @@ 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")
|
||||
}
|
||||
@@ -102,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
|
||||
@@ -75,3 +77,9 @@ gradlePlugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
versionCatalogUpdate {
|
||||
sortByKey.set(false)
|
||||
keep { keepUnusedLibraries.set(true) }
|
||||
catalogFile.set(file("../gradle/libs.versions.toml"))
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -201,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() }
|
||||
}
|
||||
|
||||
@@ -246,6 +254,20 @@ abstract class CCTweakedExtension(
|
||||
).resolve().single()
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude a dependency from being publisehd 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(
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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.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.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>
|
||||
|
||||
init {
|
||||
description = "Check :core's dependencies are consistent with Minecraft's."
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
}
|
||||
|
||||
@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
|
||||
if (requested.version != selected) {
|
||||
logger.error("Requested dependency {} (via {}) but got version {}", requested, from, selected)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.file.FileSystemLocation
|
||||
import org.gradle.api.file.FileSystemLocationProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.JavaExec
|
||||
@@ -129,3 +128,30 @@ fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
|
||||
|
||||
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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 = "") {
|
||||
|
||||
@@ -21,7 +21,7 @@ of the mod should run fine on later versions.
|
||||
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
|
||||
as documentation for breaking changes and "gotchas" one should look out for between versions.
|
||||
|
||||
## CC: Tweaked 1.109.0 {#cct-1.109}
|
||||
## CC: Tweaked 1.109.0 to 1.109.2 {#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.
|
||||
@@ -31,6 +31,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
|
||||
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
|
||||
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.
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ kotlin.stdlib.default.dependency=false
|
||||
kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=true
|
||||
modVersion=1.109.1
|
||||
isUnstable=false
|
||||
modVersion=1.109.3
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.1
|
||||
|
||||
@@ -10,28 +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.8.0"
|
||||
cobalt-next = "0.8.1" # Not a real version, used to constrain the version we accept.
|
||||
commonsCli = "1.3.1"
|
||||
# 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.8.2"
|
||||
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 = "2.0.1"
|
||||
|
||||
# Minecraft mods
|
||||
emi = "1.0.8+1.20.1"
|
||||
@@ -47,29 +48,31 @@ sodium = "mc1.20-0.4.10"
|
||||
|
||||
# Testing
|
||||
hamcrest = "2.2"
|
||||
jqwik = "1.7.4"
|
||||
junit = "5.10.0"
|
||||
jqwik = "1.8.2"
|
||||
junit = "5.10.1"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.2"
|
||||
checkstyle = "10.12.3"
|
||||
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"
|
||||
fabric-loom = "1.3.9"
|
||||
forgeGradle = "6.0.8"
|
||||
githubRelease = "2.4.1"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.50.0"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-44-g9ee0055"
|
||||
librarian = "1.+"
|
||||
lwjgl = "3.3.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"
|
||||
versionCatalogUpdate = "0.8.1"
|
||||
vineflower = "1.11.0"
|
||||
|
||||
[libraries]
|
||||
@@ -162,10 +165,12 @@ vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vinef
|
||||
[plugins]
|
||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
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"]
|
||||
@@ -184,5 +189,5 @@ 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" ]
|
||||
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
||||
teavm-tooling = ["teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl"]
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -13,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;
|
||||
@@ -38,7 +37,6 @@ 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;
|
||||
|
||||
@@ -106,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())
|
||||
@@ -121,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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -141,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())
|
||||
@@ -158,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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -190,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
|
||||
@@ -204,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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -253,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")
|
||||
);
|
||||
|
||||
@@ -277,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())
|
||||
@@ -288,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())
|
||||
@@ -300,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")
|
||||
);
|
||||
|
||||
@@ -367,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")
|
||||
);
|
||||
|
||||
@@ -519,10 +508,6 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static Consumer<JsonObject> family(ComputerFamily family) {
|
||||
return json -> json.addProperty("family", family.getSerializedName());
|
||||
}
|
||||
|
||||
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
|
||||
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
|
||||
}
|
||||
|
||||
@@ -79,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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -139,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)));
|
||||
@@ -192,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));
|
||||
@@ -311,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));
|
||||
@@ -371,11 +373,11 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::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<RecipeSerializer<TurtleOverlayRecipe>> 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<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
|
||||
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", () -> CustomShapedRecipe.validatingSerialiser(ComputerUpgradeRecipe::of));
|
||||
}
|
||||
|
||||
public static class Permissions {
|
||||
@@ -464,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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,33 +4,8 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.core;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
|
||||
public enum ComputerFamily implements StringRepresentable {
|
||||
NORMAL("normal"),
|
||||
ADVANCED("advanced"),
|
||||
COMMAND("command");
|
||||
|
||||
private final String name;
|
||||
|
||||
ComputerFamily(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static ComputerFamily getFamily(JsonObject json, String name) {
|
||||
var familyName = GsonHelper.getAsString(json, name);
|
||||
for (var family : values()) {
|
||||
if (family.getSerializedName().equalsIgnoreCase(familyName)) return family;
|
||||
}
|
||||
|
||||
throw new JsonSyntaxException("Unknown computer family '" + familyName + "' for field " + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializedName() {
|
||||
return name;
|
||||
}
|
||||
public enum ComputerFamily {
|
||||
NORMAL,
|
||||
ADVANCED,
|
||||
COMMAND,
|
||||
}
|
||||
|
||||
@@ -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,57 +4,44 @@
|
||||
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
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 dan200.computercraft.shared.recipe.ShapedRecipeSpec;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
|
||||
/**
|
||||
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting it from one {@linkplain ComputerFamily
|
||||
* family} to another.
|
||||
* 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 ComputerFamily family;
|
||||
private final Item result;
|
||||
|
||||
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe, ComputerFamily family) {
|
||||
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) {
|
||||
super(identifier, recipe);
|
||||
this.family = family;
|
||||
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, family);
|
||||
return item.changeItem(stack, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeSerializer<ComputerUpgradeRecipe> getSerializer() {
|
||||
return ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get();
|
||||
}
|
||||
|
||||
public static class Serializer implements RecipeSerializer<ComputerUpgradeRecipe> {
|
||||
@Override
|
||||
public ComputerUpgradeRecipe fromJson(ResourceLocation identifier, JsonObject json) {
|
||||
var recipe = ShapedRecipeSpec.fromJson(json);
|
||||
var family = ComputerFamily.getFamily(json, "family");
|
||||
return new ComputerUpgradeRecipe(identifier, recipe, family);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerUpgradeRecipe fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
|
||||
var recipe = ShapedRecipeSpec.fromNetwork(buf);
|
||||
var family = buf.readEnum(ComputerFamily.class);
|
||||
return new ComputerUpgradeRecipe(identifier, recipe, family);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNetwork(FriendlyByteBuf buf, ComputerUpgradeRecipe recipe) {
|
||||
recipe.toSpec().toNetwork(buf);
|
||||
buf.writeEnum(recipe.family);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.network;
|
||||
|
||||
/**
|
||||
* A type of message to send over the network.
|
||||
* <p>
|
||||
* Much like recipe or argument serialisers, each type of {@link NetworkMessage} should have a unique type associated
|
||||
* with it. This holds platform-specific information about how the packet should be sent over the network.
|
||||
*
|
||||
* @param <T> The type of message to send
|
||||
* @see NetworkMessages
|
||||
* @see NetworkMessage#type()
|
||||
*/
|
||||
public interface MessageType<T extends NetworkMessage<?>> {
|
||||
}
|
||||
@@ -17,6 +17,13 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||
* @see ServerNetworkContext
|
||||
*/
|
||||
public interface NetworkMessage<T> {
|
||||
/**
|
||||
* Get the type of this message.
|
||||
*
|
||||
* @return The type of this message.
|
||||
*/
|
||||
MessageType<?> type();
|
||||
|
||||
/**
|
||||
* Write this packet to a buffer.
|
||||
* <p>
|
||||
@@ -24,7 +31,7 @@ public interface NetworkMessage<T> {
|
||||
*
|
||||
* @param buf The buffer to write data to.
|
||||
*/
|
||||
void toBytes(FriendlyByteBuf buf);
|
||||
void write(FriendlyByteBuf buf);
|
||||
|
||||
/**
|
||||
* Handle this {@link NetworkMessage}.
|
||||
|
||||
@@ -4,48 +4,84 @@
|
||||
|
||||
package dan200.computercraft.shared.network;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.network.client.*;
|
||||
import dan200.computercraft.shared.network.server.*;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Registry for all packets provided by CC: Tweaked.
|
||||
* List of all {@link MessageType}s provided by CC: Tweaked.
|
||||
*
|
||||
* @see PlatformHelper The platform helper is used to send packets.
|
||||
*/
|
||||
public final class NetworkMessages {
|
||||
private static final IntSet seenIds = new IntOpenHashSet();
|
||||
private static final Set<String> seenChannel = new HashSet<>();
|
||||
private static final List<MessageType<? extends NetworkMessage<ServerNetworkContext>>> serverMessages = new ArrayList<>();
|
||||
private static final List<MessageType<? extends NetworkMessage<ClientNetworkContext>>> clientMessages = new ArrayList<>();
|
||||
|
||||
public static final MessageType<ComputerActionServerMessage> COMPUTER_ACTION = registerServerbound(0, "computer_action", ComputerActionServerMessage.class, ComputerActionServerMessage::new);
|
||||
public static final MessageType<QueueEventServerMessage> QUEUE_EVENT = registerServerbound(1, "queue_event", QueueEventServerMessage.class, QueueEventServerMessage::new);
|
||||
public static final MessageType<KeyEventServerMessage> KEY_EVENT = registerServerbound(2, "key_event", KeyEventServerMessage.class, KeyEventServerMessage::new);
|
||||
public static final MessageType<MouseEventServerMessage> MOUSE_EVENT = registerServerbound(3, "mouse_event", MouseEventServerMessage.class, MouseEventServerMessage::new);
|
||||
public static final MessageType<UploadFileMessage> UPLOAD_FILE = registerServerbound(4, "upload_file", UploadFileMessage.class, UploadFileMessage::new);
|
||||
|
||||
public static final MessageType<ChatTableClientMessage> CHAT_TABLE = registerClientbound(10, "chat_table", ChatTableClientMessage.class, ChatTableClientMessage::new);
|
||||
public static final MessageType<PocketComputerDataMessage> POCKET_COMPUTER_DATA = registerClientbound(11, "pocket_computer_data", PocketComputerDataMessage.class, PocketComputerDataMessage::new);
|
||||
public static final MessageType<PocketComputerDeletedClientMessage> POCKET_COMPUTER_DELETED = registerClientbound(12, "pocket_computer_deleted", PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new);
|
||||
public static final MessageType<ComputerTerminalClientMessage> COMPUTER_TERMINAL = registerClientbound(13, "computer_terminal", ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new);
|
||||
public static final MessageType<PlayRecordClientMessage> PLAY_RECORD = registerClientbound(14, "play_record", PlayRecordClientMessage.class, PlayRecordClientMessage::new);
|
||||
public static final MessageType<MonitorClientMessage> MONITOR_CLIENT = registerClientbound(15, "monitor_client", MonitorClientMessage.class, MonitorClientMessage::new);
|
||||
public static final MessageType<SpeakerAudioClientMessage> SPEAKER_AUDIO = registerClientbound(16, "speaker_audio", SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new);
|
||||
public static final MessageType<SpeakerMoveClientMessage> SPEAKER_MOVE = registerClientbound(17, "speaker_move", SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new);
|
||||
public static final MessageType<SpeakerPlayClientMessage> SPEAKER_PLAY = registerClientbound(18, "speaker_play", SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new);
|
||||
public static final MessageType<SpeakerStopClientMessage> SPEAKER_STOP = registerClientbound(19, "speaker_stop", SpeakerStopClientMessage.class, SpeakerStopClientMessage::new);
|
||||
public static final MessageType<UploadResultMessage> UPLOAD_RESULT = registerClientbound(20, "upload_result", UploadResultMessage.class, UploadResultMessage::new);
|
||||
public static final MessageType<UpgradesLoadedMessage> UPGRADES_LOADED = registerClientbound(21, "upgrades_loaded", UpgradesLoadedMessage.class, UpgradesLoadedMessage::new);
|
||||
|
||||
private NetworkMessages() {
|
||||
}
|
||||
|
||||
public interface PacketRegistry {
|
||||
<T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder);
|
||||
|
||||
<T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder);
|
||||
private static <C, T extends NetworkMessage<C>> MessageType<T> register(
|
||||
List<MessageType<? extends NetworkMessage<C>>> messages,
|
||||
int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader
|
||||
) {
|
||||
if (!seenIds.add(id)) throw new IllegalArgumentException("Duplicate id " + id);
|
||||
if (!seenChannel.add(channel)) throw new IllegalArgumentException("Duplicate channel " + channel);
|
||||
var type = PlatformHelper.get().createMessageType(id, new ResourceLocation(ComputerCraftAPI.MOD_ID, channel), klass, reader);
|
||||
messages.add(type);
|
||||
return type;
|
||||
}
|
||||
|
||||
public static void register(PacketRegistry registry) {
|
||||
// Server messages
|
||||
registry.registerServerbound(0, ComputerActionServerMessage.class, ComputerActionServerMessage::new);
|
||||
registry.registerServerbound(1, QueueEventServerMessage.class, QueueEventServerMessage::new);
|
||||
registry.registerServerbound(2, KeyEventServerMessage.class, KeyEventServerMessage::new);
|
||||
registry.registerServerbound(3, MouseEventServerMessage.class, MouseEventServerMessage::new);
|
||||
registry.registerServerbound(4, UploadFileMessage.class, UploadFileMessage::new);
|
||||
private static <T extends NetworkMessage<ServerNetworkContext>> MessageType<T> registerServerbound(int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
|
||||
return register(serverMessages, id, channel, klass, reader);
|
||||
}
|
||||
|
||||
// Client messages
|
||||
registry.registerClientbound(10, ChatTableClientMessage.class, ChatTableClientMessage::new);
|
||||
registry.registerClientbound(11, PocketComputerDataMessage.class, PocketComputerDataMessage::new);
|
||||
registry.registerClientbound(12, PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new);
|
||||
registry.registerClientbound(13, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new);
|
||||
registry.registerClientbound(14, PlayRecordClientMessage.class, PlayRecordClientMessage::new);
|
||||
registry.registerClientbound(15, MonitorClientMessage.class, MonitorClientMessage::new);
|
||||
registry.registerClientbound(16, SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new);
|
||||
registry.registerClientbound(17, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new);
|
||||
registry.registerClientbound(18, SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new);
|
||||
registry.registerClientbound(19, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new);
|
||||
registry.registerClientbound(20, UploadResultMessage.class, UploadResultMessage::new);
|
||||
registry.registerClientbound(21, UpgradesLoadedMessage.class, UpgradesLoadedMessage::new);
|
||||
private static <T extends NetworkMessage<ClientNetworkContext>> MessageType<T> registerClientbound(int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
|
||||
return register(clientMessages, id, channel, klass, reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all serverbound message types.
|
||||
*
|
||||
* @return An unmodifiable sequence of all serverbound message types.
|
||||
*/
|
||||
public static Collection<MessageType<? extends NetworkMessage<ServerNetworkContext>>> getServerbound() {
|
||||
return Collections.unmodifiableCollection(serverMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all clientbound message types.
|
||||
*
|
||||
* @return An unmodifiable sequence of all clientbound message types.
|
||||
*/
|
||||
public static Collection<MessageType<? extends NetworkMessage<ClientNetworkContext>>> getClientbound() {
|
||||
return Collections.unmodifiableCollection(clientMessages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
@@ -43,7 +45,7 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeUtf(table.getId(), MAX_LEN);
|
||||
buf.writeVarInt(table.getColumns());
|
||||
buf.writeBoolean(table.getHeaders() != null);
|
||||
@@ -63,4 +65,9 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleChatTable(table);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<ChatTableClientMessage> type() {
|
||||
return NetworkMessages.CHAT_TABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
|
||||
@@ -25,7 +27,7 @@ public class ComputerTerminalClientMessage implements NetworkMessage<ClientNetwo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(containerId);
|
||||
terminal.write(buf);
|
||||
}
|
||||
@@ -34,4 +36,9 @@ public class ComputerTerminalClientMessage implements NetworkMessage<ClientNetwo
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleComputerTerminal(containerId, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<ComputerTerminalClientMessage> type() {
|
||||
return NetworkMessages.COMPUTER_TERMINAL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
@@ -25,7 +27,7 @@ public class MonitorClientMessage implements NetworkMessage<ClientNetworkContext
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeBlockPos(pos);
|
||||
state.write(buf);
|
||||
}
|
||||
@@ -34,4 +36,9 @@ public class MonitorClientMessage implements NetworkMessage<ClientNetworkContext
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleMonitorData(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<MonitorClientMessage> type() {
|
||||
return NetworkMessages.MONITOR_CLIENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -43,7 +45,7 @@ public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkCont
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeBlockPos(pos);
|
||||
buf.writeNullable(soundEvent, (b, e) -> e.writeToNetwork(b));
|
||||
buf.writeNullable(name, FriendlyByteBuf::writeUtf);
|
||||
@@ -53,4 +55,9 @@ public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkCont
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handlePlayRecord(pos, soundEvent, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<PlayRecordClientMessage> type() {
|
||||
return NetworkMessages.PLAY_RECORD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ package dan200.computercraft.shared.network.client;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
@@ -35,7 +37,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(instanceId);
|
||||
buf.writeEnum(state);
|
||||
buf.writeVarInt(lightState);
|
||||
@@ -46,4 +48,9 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handlePocketComputerData(instanceId, state, lightState, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<PocketComputerDataMessage> type() {
|
||||
return NetworkMessages.POCKET_COMPUTER_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
|
||||
@@ -20,7 +22,7 @@ public class PocketComputerDeletedClientMessage implements NetworkMessage<Client
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(instanceId);
|
||||
}
|
||||
|
||||
@@ -28,4 +30,9 @@ public class PocketComputerDeletedClientMessage implements NetworkMessage<Client
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handlePocketComputerDeleted(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<PocketComputerDeletedClientMessage> type() {
|
||||
return NetworkMessages.POCKET_COMPUTER_DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -47,7 +49,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(source);
|
||||
pos.write(buf);
|
||||
buf.writeFloat(volume);
|
||||
@@ -58,4 +60,9 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleSpeakerAudio(source, pos, volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<SpeakerAudioClientMessage> type() {
|
||||
return NetworkMessages.SPEAKER_AUDIO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -33,7 +35,7 @@ public class SpeakerMoveClientMessage implements NetworkMessage<ClientNetworkCon
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(source);
|
||||
pos.write(buf);
|
||||
}
|
||||
@@ -42,4 +44,9 @@ public class SpeakerMoveClientMessage implements NetworkMessage<ClientNetworkCon
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleSpeakerMove(source, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<SpeakerMoveClientMessage> type() {
|
||||
return NetworkMessages.SPEAKER_MOVE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -43,7 +45,7 @@ public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkCon
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(source);
|
||||
pos.write(buf);
|
||||
buf.writeResourceLocation(sound);
|
||||
@@ -55,4 +57,9 @@ public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkCon
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleSpeakerPlay(source, pos, sound, volume, pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<SpeakerPlayClientMessage> type() {
|
||||
return NetworkMessages.SPEAKER_PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
@@ -29,7 +31,7 @@ public class SpeakerStopClientMessage implements NetworkMessage<ClientNetworkCon
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(source);
|
||||
}
|
||||
|
||||
@@ -37,4 +39,9 @@ public class SpeakerStopClientMessage implements NetworkMessage<ClientNetworkCon
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleSpeakerStop(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<SpeakerStopClientMessage> type() {
|
||||
return NetworkMessages.SPEAKER_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -27,7 +29,7 @@ import java.util.Objects;
|
||||
/**
|
||||
* Syncs turtle and pocket upgrades to the client.
|
||||
*/
|
||||
public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
|
||||
public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
|
||||
private final Map<String, UpgradeManager.UpgradeWrapper<TurtleUpgradeSerialiser<?>, ITurtleUpgrade>> turtleUpgrades;
|
||||
private final Map<String, UpgradeManager.UpgradeWrapper<PocketUpgradeSerialiser<?>, IPocketUpgrade>> pocketUpgrades;
|
||||
|
||||
@@ -65,7 +67,7 @@ public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContex
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
toBytes(buf, TurtleUpgradeSerialiser.registryId(), turtleUpgrades);
|
||||
toBytes(buf, PocketUpgradeSerialiser.registryId(), pocketUpgrades);
|
||||
}
|
||||
@@ -95,4 +97,9 @@ public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContex
|
||||
TurtleUpgrades.instance().loadFromNetwork(turtleUpgrades);
|
||||
PocketUpgrades.instance().loadFromNetwork(pocketUpgrades);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<UpgradesLoadedMessage> type() {
|
||||
return NetworkMessages.UPGRADES_LOADED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
@@ -43,7 +45,7 @@ public class UploadResultMessage implements NetworkMessage<ClientNetworkContext>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(containerId);
|
||||
buf.writeEnum(result);
|
||||
if (result == UploadResult.ERROR) buf.writeComponent(Nullability.assertNonNull(errorMessage));
|
||||
@@ -53,4 +55,9 @@ public class UploadResultMessage implements NetworkMessage<ClientNetworkContext>
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handleUploadResult(containerId, result, errorMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<UploadResultMessage> type() {
|
||||
return NetworkMessages.UPLOAD_RESULT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package dan200.computercraft.shared.network.server;
|
||||
|
||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
|
||||
@@ -23,8 +25,8 @@ public class ComputerActionServerMessage extends ComputerServerMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
super.toBytes(buf);
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
super.write(buf);
|
||||
buf.writeEnum(action);
|
||||
}
|
||||
|
||||
@@ -37,6 +39,11 @@ public class ComputerActionServerMessage extends ComputerServerMessage {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<ComputerActionServerMessage> type() {
|
||||
return NetworkMessages.COMPUTER_ACTION;
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
TURN_ON,
|
||||
SHUTDOWN,
|
||||
|
||||
@@ -28,7 +28,7 @@ public abstract class ComputerServerMessage implements NetworkMessage<ServerNetw
|
||||
|
||||
@Override
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(containerId);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package dan200.computercraft.shared.network.server;
|
||||
|
||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
|
||||
@@ -30,8 +32,8 @@ public class KeyEventServerMessage extends ComputerServerMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
super.toBytes(buf);
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
super.write(buf);
|
||||
buf.writeByte(type);
|
||||
buf.writeVarInt(key);
|
||||
}
|
||||
@@ -45,4 +47,9 @@ public class KeyEventServerMessage extends ComputerServerMessage {
|
||||
input.keyDown(key, type == TYPE_REPEAT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<KeyEventServerMessage> type() {
|
||||
return NetworkMessages.KEY_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package dan200.computercraft.shared.network.server;
|
||||
|
||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
|
||||
@@ -37,8 +39,8 @@ public class MouseEventServerMessage extends ComputerServerMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
super.toBytes(buf);
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
super.write(buf);
|
||||
buf.writeByte(type);
|
||||
buf.writeVarInt(arg);
|
||||
buf.writeVarInt(x);
|
||||
@@ -55,4 +57,9 @@ public class MouseEventServerMessage extends ComputerServerMessage {
|
||||
case TYPE_SCROLL -> input.mouseScroll(arg, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<MouseEventServerMessage> type() {
|
||||
return NetworkMessages.MOUSE_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ package dan200.computercraft.shared.network.server;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.computer.menu.ServerInputHandler;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
@@ -37,8 +39,8 @@ public class QueueEventServerMessage extends ComputerServerMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
super.toBytes(buf);
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
super.write(buf);
|
||||
buf.writeUtf(event);
|
||||
buf.writeNbt(args == null ? null : NBTUtil.encodeObjects(args));
|
||||
}
|
||||
@@ -47,4 +49,9 @@ public class QueueEventServerMessage extends ComputerServerMessage {
|
||||
protected void handle(ServerNetworkContext context, ComputerMenu container) {
|
||||
container.getInput().queueEvent(event, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<QueueEventServerMessage> type() {
|
||||
return NetworkMessages.QUEUE_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||
import dan200.computercraft.shared.computer.upload.FileSlice;
|
||||
import dan200.computercraft.shared.computer.upload.FileUpload;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
@@ -91,8 +93,8 @@ public class UploadFileMessage extends ComputerServerMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
super.toBytes(buf);
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
super.write(buf);
|
||||
buf.writeUUID(uuid);
|
||||
buf.writeByte(flag);
|
||||
|
||||
@@ -166,4 +168,9 @@ public class UploadFileMessage extends ComputerServerMessage {
|
||||
input.continueUpload(uuid, slices);
|
||||
if ((flag & FLAG_LAST) != 0) input.finishUpload(player, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType<UploadFileMessage> type() {
|
||||
return NetworkMessages.UPLOAD_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
@@ -164,6 +165,18 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
*/
|
||||
void openMenu(Player player, MenuProvider owner, ContainerData menu);
|
||||
|
||||
/**
|
||||
* Create a new {@link MessageType}.
|
||||
*
|
||||
* @param id The descriminator for this message type.
|
||||
* @param channel The channel name for this message type.
|
||||
* @param klass The type of this message.
|
||||
* @param reader The function which reads the packet from a buffer. Should be the inverse to {@link NetworkMessage#write(FriendlyByteBuf)}.
|
||||
* @param <T> The type of this message.
|
||||
* @return The new {@link MessageType} instance.
|
||||
*/
|
||||
<T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader);
|
||||
|
||||
/**
|
||||
* Send a message to a specific player.
|
||||
*
|
||||
|
||||
@@ -12,7 +12,6 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
@@ -60,14 +59,6 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
public static ItemStack create(int id, @Nullable String label, int colour, ComputerFamily family, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
return switch (family) {
|
||||
case NORMAL -> ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().create(id, label, colour, upgrade);
|
||||
case ADVANCED -> ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().create(id, label, colour, upgrade);
|
||||
default -> ItemStack.EMPTY;
|
||||
};
|
||||
}
|
||||
|
||||
public ItemStack create(int id, @Nullable String label, int colour, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
var result = new ItemStack(this);
|
||||
if (id >= 0) result.getOrCreateTag().putInt(NBT_ID, id);
|
||||
@@ -243,17 +234,16 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
return IComputerItem.super.getLabel(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerFamily getFamily() {
|
||||
return family;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
||||
return create(
|
||||
public ItemStack changeItem(ItemStack stack, Item newItem) {
|
||||
return newItem instanceof PocketComputerItem pocket ? pocket.create(
|
||||
getComputerID(stack), getLabel(stack), getColour(stack),
|
||||
family, getUpgradeWithData(stack)
|
||||
);
|
||||
getUpgradeWithData(stack)
|
||||
) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// IMedia
|
||||
|
||||
@@ -81,7 +81,6 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
||||
if (upgrade == null) return ItemStack.EMPTY;
|
||||
|
||||
// Construct the new stack
|
||||
var family = itemComputer.getFamily();
|
||||
var computerID = itemComputer.getComputerID(computer);
|
||||
var label = itemComputer.getLabel(computer);
|
||||
var colour = itemComputer.getColour(computer);
|
||||
|
||||
@@ -10,7 +10,6 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
@@ -52,6 +51,16 @@ import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStat
|
||||
public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implements SimpleWaterloggedBlock {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||
|
||||
/**
|
||||
* The explosion resistance to use when a turtle is "immune" to explosions.
|
||||
* <p>
|
||||
* This is used as the default explosion resistance for advanced turtles, and the resistance for entity-based
|
||||
* explosions (e.g. creepers).
|
||||
*
|
||||
* @see #getExplosionResistance(BlockState, BlockGetter, BlockPos, Explosion)
|
||||
*/
|
||||
public static final float IMMUNE_EXPLOSION_RESISTANCE = 2000f;
|
||||
|
||||
private static final VoxelShape DEFAULT_SHAPE = Shapes.box(
|
||||
0.125, 0.125, 0.125,
|
||||
0.875, 0.875, 0.875
|
||||
@@ -59,8 +68,8 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
|
||||
|
||||
private final BlockEntityTicker<TurtleBlockEntity> clientTicker = (level, pos, state, computer) -> computer.clientTick();
|
||||
|
||||
public TurtleBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<TurtleBlockEntity>> type) {
|
||||
super(settings, family, type);
|
||||
public TurtleBlock(Properties settings, RegistryEntry<BlockEntityType<TurtleBlockEntity>> type) {
|
||||
super(settings, type);
|
||||
registerDefaultState(getStateDefinition().any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(WATERLOGGED, false)
|
||||
@@ -149,20 +158,21 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
|
||||
@ForgeOverride
|
||||
public float getExplosionResistance(BlockState state, BlockGetter world, BlockPos pos, Explosion explosion) {
|
||||
var exploder = explosion.getDirectSourceEntity();
|
||||
if (getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile) {
|
||||
return 2000;
|
||||
if (exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile) {
|
||||
return IMMUNE_EXPLOSION_RESISTANCE;
|
||||
}
|
||||
|
||||
return explosionResistance;
|
||||
return getExplosionResistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
|
||||
if (!(tile instanceof TurtleBlockEntity turtle)) return ItemStack.EMPTY;
|
||||
if (!(asItem() instanceof TurtleItem item)) return ItemStack.EMPTY;
|
||||
|
||||
var access = turtle.getAccess();
|
||||
return TurtleItem.create(
|
||||
turtle.getComputerID(), turtle.getLabel(), access.getColour(), turtle.getFamily(),
|
||||
return item.create(
|
||||
turtle.getComputerID(), turtle.getLabel(), access.getColour(),
|
||||
withPersistedData(access.getUpgradeWithData(TurtleSide.LEFT)),
|
||||
withPersistedData(access.getUpgradeWithData(TurtleSide.RIGHT)),
|
||||
access.getFuelLevel(), turtle.getOverlay()
|
||||
|
||||
@@ -38,6 +38,7 @@ import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
public class TurtleBlockEntity extends AbstractComputerBlockEntity implements BasicContainer {
|
||||
public static final int INVENTORY_SIZE = 16;
|
||||
@@ -53,13 +54,17 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
private final NonNullList<ItemStack> inventory = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY);
|
||||
private final NonNullList<ItemStack> inventorySnapshot = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY);
|
||||
private boolean inventoryChanged = false;
|
||||
|
||||
private final IntSupplier fuelLimit;
|
||||
|
||||
private TurtleBrain brain = new TurtleBrain(this);
|
||||
private MoveState moveState = MoveState.NOT_MOVED;
|
||||
private @Nullable IPeripheral peripheral;
|
||||
private @Nullable Runnable onMoved;
|
||||
|
||||
public TurtleBlockEntity(BlockEntityType<? extends TurtleBlockEntity> type, BlockPos pos, BlockState state, ComputerFamily family) {
|
||||
public TurtleBlockEntity(BlockEntityType<? extends TurtleBlockEntity> type, BlockPos pos, BlockState state, IntSupplier fuelLimit, ComputerFamily family) {
|
||||
super(type, pos, state, family);
|
||||
this.fuelLimit = fuelLimit;
|
||||
}
|
||||
|
||||
boolean hasMoved() {
|
||||
@@ -172,8 +177,6 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
return hasPeripheralUpgradeOnSide(localSide);
|
||||
}
|
||||
|
||||
// IDirectionalTile
|
||||
|
||||
@Override
|
||||
public Direction getDirection() {
|
||||
return getBlockState().getValue(TurtleBlock.FACING);
|
||||
@@ -272,6 +275,10 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
|
||||
// Privates
|
||||
|
||||
public int getFuelLimit() {
|
||||
return fuelLimit.getAsInt();
|
||||
}
|
||||
|
||||
private boolean hasPeripheralUpgradeOnSide(ComputerSide side) {
|
||||
ITurtleUpgrade upgrade;
|
||||
switch (side) {
|
||||
|
||||
@@ -395,11 +395,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
|
||||
@Override
|
||||
public int getFuelLimit() {
|
||||
if (owner.getFamily() == ComputerFamily.ADVANCED) {
|
||||
return Config.advancedTurtleFuelLimit;
|
||||
} else {
|
||||
return Config.turtleFuelLimit;
|
||||
}
|
||||
return owner.getFuelLimit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,9 +10,7 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
@@ -20,6 +18,7 @@ import net.minecraft.core.cauldron.CauldronInteraction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.LayeredCauldronBlock;
|
||||
|
||||
@@ -32,20 +31,6 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
super(block, settings);
|
||||
}
|
||||
|
||||
public static ItemStack create(
|
||||
int id, @Nullable String label, int colour, ComputerFamily family,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade, @Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
int fuelLevel, @Nullable ResourceLocation overlay
|
||||
) {
|
||||
return switch (family) {
|
||||
case NORMAL ->
|
||||
ModRegistry.Items.TURTLE_NORMAL.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay);
|
||||
case ADVANCED ->
|
||||
ModRegistry.Items.TURTLE_ADVANCED.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay);
|
||||
default -> ItemStack.EMPTY;
|
||||
};
|
||||
}
|
||||
|
||||
public ItemStack create(
|
||||
int id, @Nullable String label, int colour,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade, @Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
@@ -119,13 +104,13 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
||||
return create(
|
||||
public ItemStack changeItem(ItemStack stack, Item newItem) {
|
||||
return newItem instanceof TurtleItem turtle ? turtle.create(
|
||||
getComputerID(stack), getLabel(stack),
|
||||
getColour(stack), family,
|
||||
getColour(stack),
|
||||
getUpgradeWithData(stack, TurtleSide.LEFT), getUpgradeWithData(stack, TurtleSide.RIGHT),
|
||||
getFuelLevel(stack), getOverlay(stack)
|
||||
);
|
||||
) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) {
|
||||
|
||||
@@ -58,7 +58,7 @@ public class TurtleOverlayRecipe extends CustomShapelessRecipe {
|
||||
return ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get();
|
||||
}
|
||||
|
||||
public static class Serializer implements RecipeSerializer<TurtleOverlayRecipe> {
|
||||
public static class Serialiser implements RecipeSerializer<TurtleOverlayRecipe> {
|
||||
@Override
|
||||
public TurtleOverlayRecipe fromJson(ResourceLocation id, JsonObject json) {
|
||||
var recipe = ShapelessRecipeSpec.fromJson(json);
|
||||
|
||||
@@ -10,6 +10,7 @@ accessWidener v1 named
|
||||
accessible method net/minecraft/client/renderer/item/ItemProperties register (Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/renderer/item/ClampedItemPropertyFunction;)V
|
||||
accessible method net/minecraft/client/renderer/blockentity/BlockEntityRenderers register (Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/client/renderer/blockentity/BlockEntityRendererProvider;)V
|
||||
accessible class net/minecraft/world/item/CreativeModeTab$Output
|
||||
accessible field net/minecraft/world/item/CreativeModeTabs OP_BLOCKS Lnet/minecraft/resources/ResourceKey;
|
||||
|
||||
# Containers
|
||||
accessible class net/minecraft/world/inventory/MenuType$MenuSupplier
|
||||
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
@@ -168,6 +169,13 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
|
||||
throw new UnsupportedOperationException("Cannot open menu inside tests");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
|
||||
record TypeImpl<T extends NetworkMessage<?>>(Function<FriendlyByteBuf, T> reader) implements MessageType<T> {
|
||||
}
|
||||
return new TypeImpl<>(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentAccess<IPeripheral> createPeripheralAccess(Consumer<Direction> invalidate) {
|
||||
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
|
||||
|
||||
@@ -29,7 +29,7 @@ class PlayRecordClientMessageTest {
|
||||
@Property
|
||||
public void testRoundTrip(@ForAll("message") PlayRecordClientMessage message) {
|
||||
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
|
||||
message.toBytes(buffer);
|
||||
message.write(buffer);
|
||||
|
||||
var converted = new PlayRecordClientMessage(buffer);
|
||||
assertEquals(buffer.readableBytes(), 0, "Whole packet was read");
|
||||
|
||||
@@ -63,7 +63,7 @@ public class UploadFileMessageTest {
|
||||
private static List<UploadFileMessage> roundtripPackets(List<UploadFileMessage> packets) {
|
||||
return packets.stream().map(packet -> {
|
||||
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
|
||||
packet.toBytes(buffer);
|
||||
packet.write(buffer);
|
||||
// We include things like file size in the packet, but not in the count, so grant a slightly larger threshold.
|
||||
assertThat("Packet is too large", buffer.writerIndex(), lessThanOrEqualTo(MAX_PACKET_SIZE + 128));
|
||||
if ((packet.flag & FLAG_LAST) == 0) {
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
package dan200.computercraft.core.apis.http.websocket;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.http.HTTPRequestException;
|
||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||
import dan200.computercraft.core.apis.http.Resource;
|
||||
import dan200.computercraft.core.apis.http.ResourceGroup;
|
||||
import dan200.computercraft.core.apis.http.*;
|
||||
import dan200.computercraft.core.apis.http.options.Options;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.core.util.AtomicHelpers;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
@@ -24,10 +23,8 @@ import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
|
||||
import io.netty.handler.codec.http.websocketx.*;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -35,6 +32,7 @@ import javax.annotation.Nullable;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Provides functionality to verify and connect to a remote websocket.
|
||||
@@ -57,6 +55,9 @@ public class Websocket extends Resource<Websocket> implements WebsocketClient {
|
||||
private final HttpHeaders headers;
|
||||
private final int timeout;
|
||||
|
||||
private final AtomicInteger inFlight = new AtomicInteger(0);
|
||||
private final GenericFutureListener<? extends io.netty.util.concurrent.Future<? super Void>> onSend = f -> inFlight.decrementAndGet();
|
||||
|
||||
public Websocket(ResourceGroup<Websocket> limiter, IAPIEnvironment environment, URI uri, String address, HttpHeaders headers, int timeout) {
|
||||
super(limiter);
|
||||
this.environment = environment;
|
||||
@@ -170,18 +171,27 @@ public class Websocket extends Resource<Websocket> implements WebsocketClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendText(String message) {
|
||||
environment.observe(Metrics.WEBSOCKET_OUTGOING, message.length());
|
||||
|
||||
var channel = channel();
|
||||
if (channel != null) channel.writeAndFlush(new TextWebSocketFrame(message));
|
||||
public void sendText(String message) throws LuaException {
|
||||
sendMessage(new TextWebSocketFrame(message), message.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBinary(ByteBuffer message) {
|
||||
environment.observe(Metrics.WEBSOCKET_OUTGOING, message.remaining());
|
||||
public void sendBinary(ByteBuffer message) throws LuaException {
|
||||
long size = message.remaining();
|
||||
sendMessage(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(message)), size);
|
||||
}
|
||||
|
||||
private void sendMessage(WebSocketFrame frame, long size) throws LuaException {
|
||||
var channel = channel();
|
||||
if (channel != null) channel.writeAndFlush(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(message)));
|
||||
if (channel == null) return;
|
||||
|
||||
// Grow the number of in-flight requests, aborting if we've hit the limit. This is then decremented when the
|
||||
// promise finishes.
|
||||
if (!AtomicHelpers.incrementToLimit(inFlight, ResourceQueue.DEFAULT_LIMIT)) {
|
||||
throw new LuaException("Too many ongoing websocket messages");
|
||||
}
|
||||
|
||||
environment.observe(Metrics.WEBSOCKET_OUTGOING, size);
|
||||
channel.writeAndFlush(frame).addListener(onSend);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis.http.websocket;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.http.HTTPRequestException;
|
||||
|
||||
import java.io.Closeable;
|
||||
@@ -39,15 +40,17 @@ public interface WebsocketClient extends Closeable {
|
||||
* Send a text websocket frame.
|
||||
*
|
||||
* @param message The message to send.
|
||||
* @throws LuaException If the message could not be sent.
|
||||
*/
|
||||
void sendText(String message);
|
||||
void sendText(String message) throws LuaException;
|
||||
|
||||
/**
|
||||
* Send a binary websocket frame.
|
||||
*
|
||||
* @param message The message to send.
|
||||
* @throws LuaException If the message could not be sent.
|
||||
*/
|
||||
void sendBinary(ByteBuffer message);
|
||||
void sendBinary(ByteBuffer message) throws LuaException;
|
||||
|
||||
/**
|
||||
* Parse an address, ensuring it is a valid websocket URI.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class AtomicHelpers {
|
||||
private AtomicHelpers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of {@link AtomicInteger#getAndIncrement()}, which increments until a limit is reached.
|
||||
*
|
||||
* @param atomic The atomic to increment.
|
||||
* @param limit The maximum value of {@code value}.
|
||||
* @return Whether the value was sucessfully incremented.
|
||||
*/
|
||||
public static boolean incrementToLimit(AtomicInteger atomic, int limit) {
|
||||
int value;
|
||||
do {
|
||||
value = atomic.get();
|
||||
if (value >= limit) return false;
|
||||
} while (!atomic.compareAndSet(value, value + 1));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,20 @@
|
||||
# New features in CC: Tweaked 1.109.3
|
||||
|
||||
* Command computers now display in the operator items creative tab.
|
||||
|
||||
Several bug fixes:
|
||||
* Error if too many websocket messages are queued to be sent at once.
|
||||
* Fix trailing-comma on method calls (e.g. `x:f(a, )` not using our custom error message.
|
||||
* Fix internal compiler error when using `goto` as the first statement in an `if` block.
|
||||
* Fix incorrect incorrect resizing of a tables' hash part when adding and removing keys.
|
||||
|
||||
# New features in CC: Tweaked 1.109.2
|
||||
|
||||
* `math.random` now uses Lua 5.4's random number generator.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix errors involving `goto` statements having the wrong line number.
|
||||
|
||||
# New features in CC: Tweaked 1.109.1
|
||||
|
||||
Several bug fixes:
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
New features in CC: Tweaked 1.109.1
|
||||
New features in CC: Tweaked 1.109.3
|
||||
|
||||
* Command computers now display in the operator items creative tab.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix `mouse_drag` event not firing for right and middle mouse buttons.
|
||||
* Fix crash when syntax errors involve `goto` or `::`.
|
||||
* Fix deadlock occuring when adding/removing observers.
|
||||
* Allow placing seeds into compostor barrels with `turtle.place()`.
|
||||
* Error if too many websocket messages are queued to be sent at once.
|
||||
* Fix trailing-comma on method calls (e.g. `x:f(a, )` not using our custom error message.
|
||||
* Fix internal compiler error when using `goto` as the first statement in an `if` block.
|
||||
* Fix incorrect incorrect resizing of a tables' hash part when adding and removing keys.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -89,6 +89,30 @@ class TestHttpApi {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Errors if too many websocket messages are sent`() {
|
||||
runServer {
|
||||
LuaTaskRunner.runTest {
|
||||
val httpApi = addApi(HTTPAPI(environment))
|
||||
assertThat("http.websocket succeeded", httpApi.websocket(ObjectArguments(WS_URL)), array(equalTo(true)))
|
||||
|
||||
val connectEvent = pullEvent()
|
||||
assertThat(connectEvent, array(equalTo("websocket_success"), equalTo(WS_URL), isA(WebsocketHandle::class.java)))
|
||||
|
||||
val websocket = connectEvent[2] as WebsocketHandle
|
||||
val error = assertThrows<LuaException> {
|
||||
for (i in 0 until 10_000) {
|
||||
websocket.send(Coerced(LuaValues.encode("Hello")), Optional.of(false))
|
||||
}
|
||||
}
|
||||
|
||||
websocket.close()
|
||||
|
||||
assertThat(error.message, equalTo("Too many ongoing websocket messages"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Queues an event when the socket is externally closed`() {
|
||||
runServer { stop ->
|
||||
|
||||
@@ -478,6 +478,7 @@ Unexpected ) in function call.
|
||||
1 | f(2, )
|
||||
| ^ Tip: Try removing this ,.
|
||||
```
|
||||
|
||||
```lua
|
||||
f(2, 3, )
|
||||
```
|
||||
@@ -491,3 +492,17 @@ Unexpected ) in function call.
|
||||
1 | f(2, 3, )
|
||||
| ^ Tip: Try removing this ,.
|
||||
```
|
||||
|
||||
```lua
|
||||
x:f(2, 3, )
|
||||
```
|
||||
|
||||
```txt
|
||||
Unexpected ) in function call.
|
||||
|
|
||||
1 | x:f(2, 3, )
|
||||
| ^
|
||||
|
|
||||
1 | x:f(2, 3, )
|
||||
| ^ Tip: Try removing this ,.
|
||||
```
|
||||
|
||||
@@ -48,7 +48,7 @@ addRemappedConfiguration("testWithIris")
|
||||
|
||||
dependencies {
|
||||
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||
modImplementation(libs.bundles.externalMods.fabric)
|
||||
modImplementation(libs.bundles.externalMods.fabric) { cct.exclude(this) }
|
||||
modCompileOnly(libs.bundles.externalMods.fabric.compile) {
|
||||
exclude("net.fabricmc", "fabric-loader")
|
||||
exclude("net.fabricmc.fabric-api")
|
||||
@@ -72,9 +72,9 @@ dependencies {
|
||||
include(libs.nightConfig.toml)
|
||||
|
||||
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
||||
api(commonClasses(project(":fabric-api")))
|
||||
clientApi(clientClasses(project(":fabric-api")))
|
||||
implementation(project(":core"))
|
||||
api(commonClasses(project(":fabric-api"))) { cct.exclude(this) }
|
||||
clientApi(clientClasses(project(":fabric-api"))) { cct.exclude(this) }
|
||||
implementation(project(":core")) { cct.exclude(this) }
|
||||
// These are transitive deps of :core, so we don't need these deps. However, we want them to appear as runtime deps
|
||||
// in our POM, and this is the easiest way.
|
||||
runtimeOnly(libs.cobalt)
|
||||
@@ -168,7 +168,11 @@ loom {
|
||||
configureForGameTest(this)
|
||||
|
||||
property("fabric-api.gametest")
|
||||
property("fabric-api.gametest.report-file", layout.buildDirectory.dir("test-results/runGametest.xml").getAbsolutePath())
|
||||
property(
|
||||
"fabric-api.gametest.report-file",
|
||||
layout.buildDirectory.dir("test-results/runGametest.xml")
|
||||
.getAbsolutePath(),
|
||||
)
|
||||
runDir("run/gametest")
|
||||
}
|
||||
}
|
||||
@@ -258,9 +262,7 @@ publishing {
|
||||
publications {
|
||||
named("maven", MavenPublication::class) {
|
||||
mavenDependencies {
|
||||
exclude(dependencies.create("cc.tweaked:"))
|
||||
exclude(libs.jei.fabric.get())
|
||||
exclude(libs.modmenu.get())
|
||||
cct.configureExcludes(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.model.CustomModelLoader;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
||||
import dan200.computercraft.shared.platform.FabricConfigFile;
|
||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||
import dan200.computercraft.shared.platform.FabricMessageType;
|
||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.PreparableModelLoadingPlugin;
|
||||
@@ -30,10 +31,11 @@ import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
|
||||
public class ComputerCraftClient {
|
||||
public static void init() {
|
||||
ClientPlayNetworking.registerGlobalReceiver(NetworkHandler.ID, (client, handler, buf, responseSender) -> {
|
||||
var packet = NetworkHandler.decodeClient(buf);
|
||||
if (packet != null) client.execute(() -> packet.handle(ClientNetworkContext.get()));
|
||||
});
|
||||
for (var type : NetworkMessages.getClientbound()) {
|
||||
ClientPlayNetworking.registerGlobalReceiver(
|
||||
FabricMessageType.toFabricType(type), (packet, player, responseSender) -> packet.payload().handle(ClientNetworkContext.get())
|
||||
);
|
||||
}
|
||||
|
||||
ClientRegistry.register();
|
||||
ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register);
|
||||
|
||||
@@ -10,9 +10,9 @@ import dan200.computercraft.client.model.FoiledModel;
|
||||
import dan200.computercraft.client.render.ModelRenderer;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||
import dan200.computercraft.shared.platform.FabricMessageType;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
@@ -29,7 +29,7 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper {
|
||||
|
||||
@Override
|
||||
public void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
||||
Minecraft.getInstance().player.connection.send(NetworkHandler.encodeServer(message));
|
||||
ClientPlayNetworking.send(FabricMessageType.toFabricPacket(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {"#": {"tag": "c:gold_ingots"}, "C": {"item": "computercraft:computer_normal"}},
|
||||
"pattern": ["###", "#C#", "# #"],
|
||||
"result": {"item": "computercraft:computer_advanced"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {"#": {"tag": "c:gold_ingots"}, "C": {"item": "computercraft:pocket_computer_normal"}},
|
||||
"pattern": ["###", "#C#", "# #"],
|
||||
"result": {"item": "computercraft:pocket_computer_advanced"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:turtle",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {
|
||||
"#": {"tag": "c:gold_ingots"},
|
||||
"C": {"item": "computercraft:computer_advanced"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {
|
||||
"#": {"tag": "c:gold_ingots"},
|
||||
"B": {"item": "minecraft:gold_block"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:turtle",
|
||||
"category": "redstone",
|
||||
"family": "normal",
|
||||
"key": {
|
||||
"#": {"tag": "c:iron_ingots"},
|
||||
"C": {"item": "computercraft:computer_normal"},
|
||||
|
||||
1
projects/fabric/src/generated/resources/data/minecraft/tags/blocks/wither_immune.json
generated
Normal file
1
projects/fabric/src/generated/resources/data/minecraft/tags/blocks/wither_immune.json
generated
Normal file
@@ -0,0 +1 @@
|
||||
{"replace": false, "values": ["computercraft:computer_command"]}
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.details.FluidDetails;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage;
|
||||
import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
||||
@@ -20,16 +21,19 @@ import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
|
||||
import dan200.computercraft.shared.platform.FabricConfigFile;
|
||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||
import dan200.computercraft.shared.platform.FabricMessageType;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
|
||||
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
@@ -45,7 +49,12 @@ public class ComputerCraft {
|
||||
private static final LevelResource SERVERCONFIG = new LevelResource("serverconfig");
|
||||
|
||||
public static void init() {
|
||||
NetworkHandler.init();
|
||||
for (var type : NetworkMessages.getServerbound()) {
|
||||
ServerPlayNetworking.registerGlobalReceiver(
|
||||
FabricMessageType.toFabricType(type), (packet, player, sender) -> packet.payload().handle(() -> player)
|
||||
);
|
||||
}
|
||||
|
||||
ModRegistry.register();
|
||||
ModRegistry.registerMainThread();
|
||||
|
||||
@@ -96,6 +105,11 @@ public class ComputerCraft {
|
||||
if (pool != null) tableBuilder.withPool(pool);
|
||||
});
|
||||
|
||||
ItemGroupEvents.MODIFY_ENTRIES_ALL.register((tab, entries) -> CommonHooks.onBuildCreativeTab(
|
||||
BuiltInRegistries.CREATIVE_MODE_TAB.getResourceKey(tab).orElseThrow(),
|
||||
entries.getContext(), entries
|
||||
));
|
||||
|
||||
CommonHooks.onDatapackReload((name, listener) -> ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new ReloadListener(name, listener)));
|
||||
|
||||
FabricDetailRegistries.FLUID_VARIANT.addProvider(FluidDetails::fill);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.platform;
|
||||
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* An implementation of {@link MessageType} for Fabric.
|
||||
* <p>
|
||||
* This provides conversions between the {@link FabricPacket}/{@link PacketType} and {@link NetworkMessage}/{@link MessageType}
|
||||
* interfaces, allowing us to interop between the two.
|
||||
*
|
||||
* @param type The underlying {@link PacketType}
|
||||
* @param <T> The type of the message.
|
||||
*/
|
||||
public record FabricMessageType<T extends NetworkMessage<?>>(
|
||||
PacketType<PacketWrapper<T>> type
|
||||
) implements MessageType<T> {
|
||||
public FabricMessageType(ResourceLocation id, Function<FriendlyByteBuf, T> reader) {
|
||||
this(PacketType.create(id, b -> new PacketWrapper<>(reader.apply(b))));
|
||||
}
|
||||
|
||||
public static <T extends NetworkMessage<?>> PacketType<PacketWrapper<T>> toFabricType(MessageType<T> type) {
|
||||
return ((FabricMessageType<T>) type).type();
|
||||
}
|
||||
|
||||
public static FabricPacket toFabricPacket(NetworkMessage<?> message) {
|
||||
return new PacketWrapper<>(message);
|
||||
}
|
||||
|
||||
public record PacketWrapper<T extends NetworkMessage<?>>(T payload) implements FabricPacket {
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
payload().write(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return FabricMessageType.toFabricType(payload().type());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.platform;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class NetworkHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NetworkHandler.class);
|
||||
|
||||
public static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "main");
|
||||
|
||||
private static final Int2ObjectMap<Function<FriendlyByteBuf, ? extends NetworkMessage<ClientNetworkContext>>> clientPackets = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<Function<FriendlyByteBuf, ? extends NetworkMessage<ServerNetworkContext>>> serverPackets = new Int2ObjectOpenHashMap<>();
|
||||
private static final Object2IntMap<Class<? extends NetworkMessage<?>>> packetIds = new Object2IntOpenHashMap<>();
|
||||
|
||||
public static void init() {
|
||||
ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> {
|
||||
var packet = decodeServer(buf);
|
||||
if (packet != null) server.execute(() -> packet.handle(handler::getPlayer));
|
||||
});
|
||||
|
||||
NetworkMessages.register(new NetworkMessages.PacketRegistry() {
|
||||
@Override
|
||||
public <T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
|
||||
clientPackets.put(id, decoder);
|
||||
packetIds.put(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
|
||||
serverPackets.put(id, decoder);
|
||||
packetIds.put(type, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static FriendlyByteBuf encode(NetworkMessage<?> message) {
|
||||
var buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeByte(packetIds.getInt(message.getClass()));
|
||||
message.toBytes(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static ClientboundCustomPayloadPacket encodeClient(NetworkMessage<ClientNetworkContext> message) {
|
||||
return new ClientboundCustomPayloadPacket(ID, encode(message));
|
||||
}
|
||||
|
||||
public static ServerboundCustomPayloadPacket encodeServer(NetworkMessage<ServerNetworkContext> message) {
|
||||
return new ServerboundCustomPayloadPacket(ID, encode(message));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> NetworkMessage<T> decode(Int2ObjectMap<Function<FriendlyByteBuf, ? extends NetworkMessage<T>>> packets, FriendlyByteBuf buffer) {
|
||||
int type = buffer.readByte();
|
||||
var reader = packets.get(type);
|
||||
if (reader == null) {
|
||||
LOGGER.debug("Unknown packet {}", type);
|
||||
return null;
|
||||
}
|
||||
|
||||
return reader.apply(buffer);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NetworkMessage<ServerNetworkContext> decodeServer(FriendlyByteBuf buffer) {
|
||||
return decode(serverPackets, buffer);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NetworkMessage<ClientNetworkContext> decodeClient(FriendlyByteBuf buffer) {
|
||||
return decode(clientPackets, buffer);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import dan200.computercraft.api.peripheral.PeripheralLookup;
|
||||
import dan200.computercraft.impl.Peripherals;
|
||||
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
@@ -27,6 +28,8 @@ import net.fabricmc.fabric.api.event.player.UseEntityCallback;
|
||||
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
||||
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache;
|
||||
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||
import net.fabricmc.fabric.api.registry.FuelRegistry;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.DefaultResourceConditions;
|
||||
@@ -44,6 +47,8 @@ import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -170,31 +175,42 @@ public class PlatformHelperImpl implements PlatformHelper {
|
||||
player.openMenu(new WrappedMenuProvider(owner, menu));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
|
||||
return new FabricMessageType<>(channel, reader);
|
||||
}
|
||||
|
||||
private Packet<ClientGamePacketListener> encodeClientbound(NetworkMessage<ClientNetworkContext> message) {
|
||||
var buf = PacketByteBufs.create();
|
||||
message.write(buf);
|
||||
return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricType(message.type()).getId(), buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
|
||||
player.connection.send(NetworkHandler.encodeClient(message));
|
||||
player.connection.send(encodeClientbound(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToPlayers(NetworkMessage<ClientNetworkContext> message, Collection<ServerPlayer> players) {
|
||||
if (players.isEmpty()) return;
|
||||
var packet = NetworkHandler.encodeClient(message);
|
||||
var packet = encodeClientbound(message);
|
||||
for (var player : players) player.connection.send(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToAllPlayers(NetworkMessage<ClientNetworkContext> message, MinecraftServer server) {
|
||||
server.getPlayerList().broadcastAll(NetworkHandler.encodeClient(message));
|
||||
server.getPlayerList().broadcastAll(encodeClientbound(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToAllAround(NetworkMessage<ClientNetworkContext> message, ServerLevel level, Vec3 pos, float distance) {
|
||||
level.getServer().getPlayerList().broadcast(null, pos.x, pos.y, pos.z, distance, level.dimension(), NetworkHandler.encodeClient(message));
|
||||
level.getServer().getPlayerList().broadcast(null, pos.x, pos.y, pos.z, distance, level.dimension(), encodeClientbound(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToAllTracking(NetworkMessage<ClientNetworkContext> message, LevelChunk chunk) {
|
||||
var packet = NetworkHandler.encodeClient(message);
|
||||
var packet = encodeClientbound(message);
|
||||
for (var player : ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false)) {
|
||||
player.connection.send(packet);
|
||||
}
|
||||
|
||||
@@ -131,12 +131,13 @@ dependencies {
|
||||
libs.bundles.externalMods.forge.runtime.get().map { runtimeOnly(fg.deobf(it)) }
|
||||
|
||||
// Depend on our other projects.
|
||||
api(commonClasses(project(":forge-api")))
|
||||
api(clientClasses(project(":forge-api")))
|
||||
implementation(project(":core"))
|
||||
api(commonClasses(project(":forge-api"))) { cct.exclude(this) }
|
||||
clientApi(clientClasses(project(":forge-api"))) { cct.exclude(this) }
|
||||
implementation(project(":core")) { cct.exclude(this) }
|
||||
|
||||
minecraftEmbed(libs.cobalt) {
|
||||
jarJar.ranged(this, "[${libs.versions.cobalt.asProvider().get()},${libs.versions.cobalt.next.get()})")
|
||||
val version = libs.versions.cobalt.get()
|
||||
jarJar.ranged(this, "[$version,${getNextVersion(version)})")
|
||||
}
|
||||
minecraftEmbed(libs.jzlib) {
|
||||
jarJar.ranged(this, "[${libs.versions.jzlib.get()},)")
|
||||
@@ -254,7 +255,7 @@ publishing {
|
||||
artifact(tasks.jarJar)
|
||||
|
||||
mavenDependencies {
|
||||
exclude(dependencies.create("cc.tweaked:"))
|
||||
cct.configureExcludes(this)
|
||||
exclude(libs.jei.forge.get())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {"#": {"tag": "forge:ingots/gold"}, "C": {"item": "computercraft:computer_normal"}},
|
||||
"pattern": ["###", "#C#", "# #"],
|
||||
"result": {"item": "computercraft:computer_advanced"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {"#": {"tag": "forge:ingots/gold"}, "C": {"item": "computercraft:pocket_computer_normal"}},
|
||||
"pattern": ["###", "#C#", "# #"],
|
||||
"result": {"item": "computercraft:pocket_computer_advanced"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:turtle",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {
|
||||
"#": {"tag": "forge:ingots/gold"},
|
||||
"C": {"item": "computercraft:computer_advanced"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"category": "redstone",
|
||||
"family": "advanced",
|
||||
"key": {
|
||||
"#": {"tag": "forge:ingots/gold"},
|
||||
"B": {"tag": "forge:storage_blocks/gold"},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"type": "computercraft:turtle",
|
||||
"category": "redstone",
|
||||
"family": "normal",
|
||||
"key": {
|
||||
"#": {"tag": "forge:ingots/iron"},
|
||||
"C": {"item": "computercraft:computer_normal"},
|
||||
|
||||
1
projects/forge/src/generated/resources/data/minecraft/tags/blocks/wither_immune.json
generated
Normal file
1
projects/forge/src/generated/resources/data/minecraft/tags/blocks/wither_immune.json
generated
Normal file
@@ -0,0 +1 @@
|
||||
{"values": ["computercraft:computer_command"]}
|
||||
@@ -12,6 +12,7 @@ import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import dan200.computercraft.shared.details.FluidData;
|
||||
@@ -23,6 +24,7 @@ import dan200.computercraft.shared.platform.ForgeConfigFile;
|
||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
@@ -100,4 +102,9 @@ public final class ComputerCraft {
|
||||
ConfigSpec.syncClient(path);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onCreativeTab(BuildCreativeModeTabContentsEvent event) {
|
||||
CommonHooks.onBuildCreativeTab(event.getTabKey(), event.getParameters(), event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
package dan200.computercraft.shared.platform;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -47,20 +46,15 @@ public final class NetworkHandler {
|
||||
}
|
||||
|
||||
public static void setup() {
|
||||
IntSet usedIds = new IntOpenHashSet();
|
||||
NetworkMessages.register(new NetworkMessages.PacketRegistry() {
|
||||
@Override
|
||||
public <T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
|
||||
if (!usedIds.add(id)) throw new IllegalArgumentException("Already have a packet with id " + id);
|
||||
registerMainThread(id, NetworkDirection.PLAY_TO_CLIENT, type, decoder, x -> ClientNetworkContext.get());
|
||||
}
|
||||
for (var type : NetworkMessages.getServerbound()) {
|
||||
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ServerNetworkContext>>) type;
|
||||
registerMainThread(forgeType, NetworkDirection.PLAY_TO_SERVER, c -> () -> assertNonNull(c.getSender()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
|
||||
if (!usedIds.add(id)) throw new IllegalArgumentException("Already have a packet with id " + id);
|
||||
registerMainThread(id, NetworkDirection.PLAY_TO_SERVER, type, decoder, c -> () -> assertNonNull(c.getSender()));
|
||||
}
|
||||
});
|
||||
for (var type : NetworkMessages.getClientbound()) {
|
||||
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ClientNetworkContext>>) type;
|
||||
registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientNetworkContext.get());
|
||||
}
|
||||
}
|
||||
|
||||
static void sendToPlayer(NetworkMessage<ClientNetworkContext> packet, ServerPlayer player) {
|
||||
@@ -96,19 +90,16 @@ public final class NetworkHandler {
|
||||
*
|
||||
* @param <T> The type of the packet to send.
|
||||
* @param <H> The context this packet is evaluated under.
|
||||
* @param type The class of the type of packet to send.
|
||||
* @param id The identifier for this packet type.
|
||||
* @param type The message type to register.
|
||||
* @param direction A network direction which will be asserted before any processing of this message occurs
|
||||
* @param decoder The factory for this type of packet.
|
||||
* @param handler Gets or constructs the handler for this packet.
|
||||
*/
|
||||
static <H, T extends NetworkMessage<H>> void registerMainThread(
|
||||
int id, NetworkDirection direction, Class<T> type, Function<FriendlyByteBuf, T> decoder,
|
||||
Function<NetworkEvent.Context, H> handler
|
||||
MessageTypeImpl<T> type, NetworkDirection direction, Function<NetworkEvent.Context, H> handler
|
||||
) {
|
||||
network.messageBuilder(type, id, direction)
|
||||
.encoder(NetworkMessage::toBytes)
|
||||
.decoder(decoder)
|
||||
network.messageBuilder(type.klass(), type.id(), direction)
|
||||
.encoder(NetworkMessage::write)
|
||||
.decoder(type.reader())
|
||||
.consumerMainThread((packet, contextSup) -> {
|
||||
try {
|
||||
packet.handle(handler.apply(contextSup.get()));
|
||||
@@ -119,4 +110,9 @@ public final class NetworkHandler {
|
||||
})
|
||||
.add();
|
||||
}
|
||||
|
||||
public record MessageTypeImpl<T extends NetworkMessage<?>>(
|
||||
int id, Class<T> klass, Function<FriendlyByteBuf, T> reader
|
||||
) implements MessageType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.impl.Peripherals;
|
||||
import dan200.computercraft.shared.Capabilities;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
@@ -157,6 +158,11 @@ public class PlatformHelperImpl implements PlatformHelper {
|
||||
NetworkHooks.openScreen((ServerPlayer) player, owner, menu::toBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
|
||||
return new NetworkHandler.MessageTypeImpl<>(id, klass, reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
|
||||
NetworkHandler.sendToPlayer(message, player);
|
||||
|
||||
@@ -6,11 +6,15 @@ package cc.tweaked.standalone;
|
||||
|
||||
|
||||
import dan200.computercraft.core.ComputerContext;
|
||||
import dan200.computercraft.core.CoreConfig;
|
||||
import dan200.computercraft.core.apis.http.options.Action;
|
||||
import dan200.computercraft.core.apis.http.options.AddressRule;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.opengl.GL;
|
||||
@@ -20,13 +24,16 @@ import org.lwjgl.system.MemoryStack;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -53,7 +60,7 @@ public class Main {
|
||||
public static final Pattern PATTERN = Pattern.compile("^(\\d+)x(\\d+)$");
|
||||
}
|
||||
|
||||
private static <T> T getParsedOptionValue(CommandLine cli, String opt, Class<T> klass) throws ParseException {
|
||||
private static <T> T getParsedOptionValue(CommandLine cli, Option opt, Class<T> klass) throws ParseException {
|
||||
var res = cli.getOptionValue(opt);
|
||||
if (klass == Path.class) {
|
||||
try {
|
||||
@@ -71,30 +78,47 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("_, _, _, !null -> !null")
|
||||
private static <T> @Nullable T getParsedOptionValue(CommandLine cli, Option opt, Class<T> klass, @Nullable T defaultValue) throws ParseException {
|
||||
return cli.hasOption(opt) ? getParsedOptionValue(cli, opt, klass) : defaultValue;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
var options = new Options();
|
||||
options.addOption(Option.builder("r").argName("PATH").longOpt("resources").hasArg()
|
||||
Option resourceOpt, computerOpt, termSizeOpt, allowLocalDomainsOpt, helpOpt;
|
||||
options.addOption(resourceOpt = Option.builder("r").argName("PATH").longOpt("resources").hasArg()
|
||||
.desc("The path to the resources directory")
|
||||
.build());
|
||||
options.addOption(Option.builder("c").argName("PATH").longOpt("computer").hasArg()
|
||||
options.addOption(computerOpt = Option.builder("c").argName("PATH").longOpt("computer").hasArg()
|
||||
.desc("The root directory of the computer. Defaults to a temporary directory.")
|
||||
.build());
|
||||
options.addOption(Option.builder("t").argName("WIDTHxHEIGHT").longOpt("term-size").hasArg()
|
||||
.desc("The size of the terminal, defaults to 51x19")
|
||||
options.addOption(termSizeOpt = Option.builder("t").argName("WIDTHxHEIGHT").longOpt("term-size").hasArg()
|
||||
.desc("The size of the terminal, defaults to 51x19.")
|
||||
.build());
|
||||
options.addOption(allowLocalDomainsOpt = Option.builder("L").longOpt("allow-local-domains")
|
||||
.desc("Allow accessing local domains with the HTTP API.")
|
||||
.build());
|
||||
|
||||
options.addOption(new Option("h", "help", false, "Print help message"));
|
||||
options.addOption(helpOpt = Option.builder("h").longOpt("help")
|
||||
.desc("Print help message")
|
||||
.build());
|
||||
|
||||
Path resourcesDirectory;
|
||||
Path computerDirectory;
|
||||
TermSize termSize;
|
||||
boolean allowLocalDomains;
|
||||
try {
|
||||
var cli = new DefaultParser().parse(options, args);
|
||||
if (!cli.hasOption("r")) throw new ParseException("--resources directory is required");
|
||||
if (cli.hasOption(helpOpt)) {
|
||||
new HelpFormatter().printHelp("standalone.jar", options, true);
|
||||
return;
|
||||
}
|
||||
if (!cli.hasOption(resourceOpt)) throw new ParseException("--resources directory is required");
|
||||
|
||||
resourcesDirectory = getParsedOptionValue(cli, "r", Path.class);
|
||||
computerDirectory = cli.hasOption("c") ? getParsedOptionValue(cli, "c", Path.class) : null;
|
||||
termSize = cli.hasOption("t") ? getParsedOptionValue(cli, "t", TermSize.class) : TermSize.DEFAULT;
|
||||
resourcesDirectory = getParsedOptionValue(cli, resourceOpt, Path.class);
|
||||
computerDirectory = getParsedOptionValue(cli, computerOpt, Path.class, null);
|
||||
termSize = getParsedOptionValue(cli, termSizeOpt, TermSize.class, TermSize.DEFAULT);
|
||||
allowLocalDomains = cli.hasOption(allowLocalDomainsOpt);
|
||||
} catch (ParseException e) {
|
||||
System.err.println(e.getLocalizedMessage());
|
||||
|
||||
@@ -106,6 +130,10 @@ public class Main {
|
||||
return;
|
||||
}
|
||||
|
||||
if (allowLocalDomains) {
|
||||
CoreConfig.httpRules = List.of(AddressRule.parse("*", OptionalInt.empty(), Action.ALLOW.toPartial()));
|
||||
}
|
||||
|
||||
var context = ComputerContext.builder(new StandaloneGlobalEnvironment(resourcesDirectory)).build();
|
||||
try (var gl = new GLObjects()) {
|
||||
var isDirty = new AtomicBoolean(true);
|
||||
|
||||
Reference in New Issue
Block a user