From fc834cd97fe941a192e40962ac3bb27be102ce09 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 31 Jan 2024 20:55:14 +0000 Subject: [PATCH] Update to 1.20.4 --- README.md | 5 +- buildSrc/build.gradle.kts | 19 +- .../main/kotlin/cc-tweaked.forge.gradle.kts | 30 +- .../cc-tweaked.java-convention.gradle.kts | 11 - .../cc/tweaked/gradle/ForgeExtensions.kt | 48 +++- .../kotlin/cc/tweaked/gradle/MinecraftExec.kt | 9 - .../gradle/common/util/runs/RunConfigSetup.kt | 51 ---- gradle.properties | 4 +- gradle/libs.versions.toml | 58 ++-- .../api/client/ComputerCraftAPIClient.java | 43 --- .../turtle/RegisterTurtleUpgradeModeller.java | 4 +- .../client/turtle/TurtleUpgradeModeller.java | 37 +-- .../client/turtle/TurtleUpgradeModellers.java | 13 +- .../api/pocket/IPocketAccess.java | 12 - .../api/pocket/IPocketUpgrade.java | 44 ++- .../api/pocket/PocketUpgradeDataProvider.java | 8 +- .../api/pocket/PocketUpgradeSerialiser.java | 79 ------ .../api/turtle/ITurtleUpgrade.java | 51 +++- .../api/turtle/TurtleUpgradeDataProvider.java | 21 +- .../api/turtle/TurtleUpgradeSerialiser.java | 109 -------- .../api/upgrades/UpgradeBase.java | 15 +- .../api/upgrades/UpgradeDataProvider.java | 35 +-- .../api/upgrades/UpgradeSerialiser.java | 59 +++- .../impl/ComputerCraftAPIService.java | 9 +- .../computercraft/impl/PlatformHelper.java | 38 --- .../computercraft/impl/RegistryHelper.java | 49 ++++ .../upgrades/SerialiserWithCraftingItem.java | 10 +- .../impl/upgrades/SimpleSerialiser.java | 8 +- .../computercraft/client/ClientHooks.java | 4 +- .../computercraft/client/ClientRegistry.java | 32 ++- .../client/gui/AbstractComputerScreen.java | 1 - .../client/gui/DiskDriveScreen.java | 1 - .../computercraft/client/gui/ItemToast.java | 12 +- .../client/gui/NoTermComputerScreen.java | 6 +- .../client/gui/OptionScreen.java | 2 - .../client/gui/PrinterScreen.java | 1 - .../client/gui/PrintoutScreen.java | 14 +- .../gui/widgets/DynamicImageButton.java | 12 +- .../client/gui/widgets/TerminalWidget.java | 2 +- .../client/platform/ClientPlatformHelper.java | 4 +- .../client/render/SpriteRenderer.java | 4 +- .../monitor/MonitorBlockEntityRenderer.java | 7 + .../client/turtle/TurtleModemModeller.java | 3 +- .../client/turtle/TurtleUpgradeModellers.java | 29 +- .../computercraft/data/LanguageProvider.java | 15 +- .../computercraft/data/ModelProvider.java | 7 +- .../data/PocketUpgradeProvider.java | 5 +- .../computercraft/data/RecipeProvider.java | 201 +++++++------- .../computercraft/data/RecipeWrapper.java | 93 ------- .../computercraft/data/TagProvider.java | 7 +- .../data/TurtleUpgradeProvider.java | 5 +- .../data/recipe/AbstractRecipeBuilder.java | 129 +++++++++ .../data/recipe/ShapedSpecBuilder.java | 71 +++++ .../data/recipe/ShapelessSpecBuilder.java | 61 ++++ .../impl/AbstractComputerCraftAPI.java | 13 +- .../computercraft/impl/Peripherals.java | 10 +- .../computercraft/impl/PocketUpgrades.java | 7 +- .../computercraft/impl/TurtleUpgrades.java | 7 +- .../computercraft/impl/UpgradeManager.java | 32 +-- .../computercraft/shared/CommonHooks.java | 6 + .../computercraft/shared/ModRegistry.java | 51 ++-- .../command/arguments/ArgumentUtils.java | 9 +- .../shared/common/ClearColourRecipe.java | 5 +- .../shared/common/ColourableRecipe.java | 5 +- .../shared/computer/apis/CommandAPI.java | 12 +- .../blocks/AbstractComputerBlock.java | 10 +- .../computer/blocks/CommandComputerBlock.java | 13 + .../shared/computer/blocks/ComputerBlock.java | 13 + .../recipe/ComputerConvertRecipe.java | 5 +- .../recipe/ComputerUpgradeRecipe.java | 9 +- .../data/ConstantLootConditionSerializer.java | 33 --- .../shared/details/BlockDetails.java | 4 +- .../shared/details/DetailHelpers.java | 7 +- .../shared/details/ItemDetails.java | 33 ++- .../integration/UpgradeRecipeGenerator.java | 23 +- .../integration/jei/JEIComputerCraft.java | 2 +- .../shared/media/recipes/DiskRecipe.java | 5 +- .../shared/media/recipes/PrintoutRecipe.java | 5 +- .../shared/network/MessageType.java | 10 + .../shared/network/NetworkMessages.java | 50 ++-- .../network/client/UpgradesLoadedMessage.java | 37 ++- .../peripheral/diskdrive/DiskDriveBlock.java | 8 + .../peripheral/generic/ComponentLookup.java | 7 +- .../peripheral/generic/GenericPeripheral.java | 5 +- .../generic/GenericPeripheralProvider.java | 20 +- .../methods/AbstractInventoryMethods.java | 9 +- .../peripheral/modem/wired/CableBlock.java | 4 +- .../modem/wired/CableBlockEntity.java | 9 +- .../modem/wired/CableBlockItem.java | 5 +- .../modem/wireless/WirelessModemBlock.java | 9 + .../wireless/WirelessModemBlockEntity.java | 8 +- .../peripheral/monitor/MonitorBlock.java | 9 + .../monitor/MonitorBlockEntity.java | 2 - .../peripheral/printer/PrinterBlock.java | 9 + .../peripheral/speaker/SpeakerBlock.java | 7 + .../shared/platform/PlatformHelper.java | 44 +-- .../shared/platform/RegistryEntry.java | 23 ++ .../shared/platform/RegistryWrappers.java | 62 ----- .../pocket/core/PocketServerComputer.java | 13 +- .../recipes/PocketComputerUpgradeRecipe.java | 5 +- .../shared/recipe/CustomShapedRecipe.java | 57 ++-- .../shared/recipe/CustomShapelessRecipe.java | 54 ++-- .../shared/recipe/ImpostorShapedRecipe.java | 5 +- .../recipe/ImpostorShapelessRecipe.java | 5 +- .../shared/recipe/MoreCodecs.java | 47 ++++ .../shared/recipe/RecipeProperties.java | 31 ++- .../shared/recipe/RecipeUtil.java | 48 ---- .../shared/recipe/ShapedRecipeSpec.java | 24 +- .../shared/recipe/ShapedTemplate.java | 99 ------- .../shared/recipe/ShapelessRecipeSpec.java | 15 +- .../shared/turtle/blocks/TurtleBlock.java | 13 + .../turtle/blocks/TurtleBlockEntity.java | 8 +- .../shared/turtle/core/TurtlePlayer.java | 2 +- .../turtle/recipes/TurtleOverlayRecipe.java | 28 +- .../shared/turtle/recipes/TurtleRecipe.java | 9 +- .../turtle/recipes/TurtleUpgradeRecipe.java | 5 +- .../upgrades/TurtleInventoryCrafting.java | 3 +- .../turtle/upgrades/TurtleToolSerialiser.java | 12 +- .../shared/util/ArgumentHelpers.java | 6 +- .../shared/util/BlockCodecs.java | 42 +++ .../models/block/computer_on.json | 2 +- .../computercraft-common.accesswidener | 1 - .../common/src/main/resources/pack.mcmeta | 2 +- .../computercraft/TestPlatformHelper.java | 111 ++------ .../shared/recipe/RecipeArbitraries.java | 12 +- .../shared/recipe/RecipeEqualities.java | 11 +- .../test/shared/MinecraftArbitraries.java | 6 +- .../test/shared/MinecraftEqualities.java | 12 +- .../dan200/computercraft/export/Exporter.java | 25 +- .../dan200/computercraft/export/JsonDump.java | 14 +- .../gametest/core/CCTestCommand.java | 6 +- .../StructureTemplateManagerMixin.java | 25 ++ .../mixin/gametest/client/MinecraftMixin.java | 4 +- .../gametest/client/WorldOpenFlowsMixin.java | 12 +- .../computercraft/gametest/Details_Test.kt | 38 +++ .../computercraft/gametest/Recipe_Test.kt | 2 +- .../gametest/api/ClientTestExtensions.kt | 5 +- .../gametest/api/TestExtensions.kt | 8 +- .../gametest/core/ClientTestHooks.kt | 12 +- .../computercraft/gametest/core/TestHooks.kt | 1 + .../computercraft-gametest.mixins.json | 1 + .../api/filesystem/MountConstants.java | 2 +- .../api/filesystem/WritableMount.java | 29 +- .../computercraft/api/lua/IArguments.java | 7 +- .../core/asm/LuaMethodSupplier.java | 4 +- .../core/asm/PeripheralMethodSupplier.java | 4 +- .../core/filesystem/MemoryMount.java | 12 - .../core/filesystem/WritableFileMount.java | 12 - .../core/lua/VarargArguments.java | 5 +- .../client/FabricComputerCraftAPIClient.java | 13 +- .../FabricComputerCraftAPIClientService.java} | 17 +- .../wired/WiredElementLookup.java | 3 +- .../client/ComputerCraftClient.java | 2 + .../rei/UpgradeDisplayGenerator.java | 35 ++- .../platform/ClientPlatformHelperImpl.java | 9 +- .../FabricComputerCraftAPIClientImpl.java} | 11 +- .../mixin/client/MinecraftMixin.java | 14 +- .../data/FabricDataGenerators.java | 6 +- .../computercraft/mixin/ChunkMapMixin.java | 25 -- .../mixin/PlayerChunkSenderMixin.java | 24 ++ .../computercraft/shared/ComputerCraft.java | 11 +- .../shared/details/FluidDetails.java | 4 +- .../shared/platform/FabricConfigFile.java | 13 +- .../shared/platform/FabricMessageType.java | 5 + .../shared/platform/PlatformHelperImpl.java | 93 +------ .../computercraft.fabric.mixins.json | 2 +- .../fabric/src/main/resources/fabric.mod.json | 2 +- projects/forge-api/build.gradle.kts | 8 - .../turtle/RegisterTurtleModellersEvent.java | 23 +- .../api/ForgeComputerCraftAPI.java | 34 +-- .../api/detail/ForgeDetailRegistries.java | 2 +- .../network/wired/WiredElementCapability.java | 27 ++ .../api/peripheral/IPeripheralProvider.java | 34 --- .../api/peripheral/PeripheralCapability.java | 27 ++ .../impl/ComputerCraftAPIForgeService.java | 15 +- projects/forge/build.gradle.kts | 196 +++++++------ .../client/ForgeClientHooks.java | 14 +- .../client/ForgeClientRegistry.java | 26 +- .../client/integration/IrisShaderMod.java | 2 +- .../client/model/FoiledModel.java | 4 +- .../client/model/TransformedBakedModel.java | 4 +- .../client/model/turtle/TurtleModel.java | 2 +- .../model/turtle/TurtleModelLoader.java | 6 +- .../platform/ClientPlatformHelperImpl.java | 11 +- .../client/BlockRenderDispatcherMixin.java | 2 +- .../client/ClientPacketListenerMixin.java | 2 +- .../dan200/computercraft/ComputerCraft.java | 157 ++++++++--- .../dan200/computercraft/data/Generators.java | 35 ++- .../impl/ComputerCraftAPIImpl.java | 50 ++-- .../computercraft/impl/Peripherals.java | 96 ------- .../computercraft/shared/Capabilities.java | 22 -- .../shared/ForgeCommonHooks.java | 98 ++----- .../shared/details/FluidData.java | 6 +- .../integration/ForgePermissionRegistry.java | 14 +- .../integration/MoreRedIntegration.java | 66 ----- .../generic/methods/EnergyMethods.java | 2 +- .../generic/methods/FluidMethods.java | 25 +- .../generic/methods/InventoryMethods.java | 19 +- .../shared/platform/FakePlayerExt.java | 2 +- .../shared/platform/ForgeConfigFile.java | 24 +- .../platform/ForgeContainerTransfer.java | 2 +- .../shared/platform/ForgeMessageType.java | 45 +++ .../shared/platform/InvalidateCallback.java | 33 --- .../shared/platform/NetworkHandler.java | 119 -------- .../shared/platform/PlatformHelperImpl.java | 260 ++++++------------ .../shared/util/CapabilityProvider.java | 68 ----- .../shared/util/CapabilityUtil.java | 57 ---- .../shared/util/SidedCapabilityProvider.java | 80 ------ .../resources/META-INF/accesstransformer.cfg | 44 +-- .../src/main/resources/META-INF/mods.toml | 11 +- .../platform/ForgeContainerTransferTest.java | 2 +- .../computercraft/gametest/core/TestMod.java | 33 ++- .../src/testMod/resources/META-INF/mods.toml | 7 +- projects/lints/build.gradle.kts | 9 +- .../kotlin/cc/tweaked/linter/SideChecker.kt | 2 +- .../tweaked/linter/AnnotatedClientClass.java | 4 +- settings.gradle.kts | 31 +-- 217 files changed, 2303 insertions(+), 3027 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/net/minecraftforge/gradle/common/util/runs/RunConfigSetup.kt delete mode 100644 projects/common-api/src/client/java/dan200/computercraft/api/client/ComputerCraftAPIClient.java delete mode 100644 projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeSerialiser.java delete mode 100644 projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeSerialiser.java create mode 100644 projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/data/RecipeWrapper.java create mode 100644 projects/common/src/main/java/dan200/computercraft/data/recipe/AbstractRecipeBuilder.java create mode 100644 projects/common/src/main/java/dan200/computercraft/data/recipe/ShapedSpecBuilder.java create mode 100644 projects/common/src/main/java/dan200/computercraft/data/recipe/ShapelessSpecBuilder.java rename projects/{fabric => common}/src/main/java/dan200/computercraft/impl/Peripherals.java (70%) delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/data/ConstantLootConditionSerializer.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryWrappers.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedTemplate.java create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/util/BlockCodecs.java create mode 100644 projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/StructureTemplateManagerMixin.java create mode 100644 projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Details_Test.kt rename projects/{common-api/src/client/java/dan200/computercraft/impl/client/ComputerCraftAPIClientService.java => fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java} (58%) rename projects/fabric-api/src/main/java/dan200/computercraft/api/{node => network}/wired/WiredElementLookup.java (89%) rename projects/{common/src/client/java/dan200/computercraft/client/ComputerCraftAPIClientImpl.java => fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java} (55%) delete mode 100644 projects/fabric/src/main/java/dan200/computercraft/mixin/ChunkMapMixin.java create mode 100644 projects/fabric/src/main/java/dan200/computercraft/mixin/PlayerChunkSenderMixin.java create mode 100644 projects/forge-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementCapability.java delete mode 100644 projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java create mode 100644 projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/PeripheralCapability.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/impl/Peripherals.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/Capabilities.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/integration/MoreRedIntegration.java create mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeMessageType.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/platform/InvalidateCallback.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityProvider.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java delete mode 100644 projects/forge/src/main/java/dan200/computercraft/shared/util/SidedCapabilityProvider.java diff --git a/README.md b/README.md index bebcba142..91a961bb9 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,8 @@ dependencies { compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion") // Forge Gradle - compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion") - compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")) - runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion")) + compileOnly("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion") + runtimeOnly("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion") // Fabric Loom modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion") diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index fc045e0db..1af023cf1 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -14,18 +14,14 @@ repositories { mavenCentral() gradlePluginPortal() - maven("https://maven.minecraftforge.net") { - name = "Forge" + maven("https://maven.neoforged.net/releases") { + name = "NeoForge" content { includeGroup("net.minecraftforge") - includeGroup("net.minecraftforge.gradle") - } - } - - maven("https://maven.parchmentmc.org") { - name = "Librarian" - content { - includeGroupByRegex("^org\\.parchmentmc.*") + includeGroup("net.neoforged") + includeGroup("net.neoforged.gradle") + includeModule("codechicken", "DiffPatch") + includeModule("net.covers1624", "Quack") } } @@ -51,10 +47,9 @@ dependencies { implementation(libs.curseForgeGradle) implementation(libs.fabric.loom) - implementation(libs.forgeGradle) implementation(libs.ideaExt) - implementation(libs.librarian) implementation(libs.minotaur) + implementation(libs.neoGradle.userdev) implementation(libs.vanillaExtract) } diff --git a/buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts b/buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts index ece233e6f..84979f2df 100644 --- a/buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts +++ b/buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts @@ -10,10 +10,8 @@ import cc.tweaked.gradle.IdeaRunConfigurations import cc.tweaked.gradle.MinecraftConfigurations plugins { - id("net.minecraftforge.gradle") - // We must apply java-convention after Forge, as we need the fg extension to be present. id("cc-tweaked.java-convention") - id("org.parchmentmc.librarian.forgegradle") + id("net.neoforged.gradle.userdev") } plugins.apply(CCTweakedPlugin::class.java) @@ -21,10 +19,20 @@ plugins.apply(CCTweakedPlugin::class.java) val mcVersion: String by extra minecraft { - val libs = project.extensions.getByType().named("libs") - mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion") + modIdentifier("computercraft") +} - accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg")) +subsystems { + parchment { + val libs = project.extensions.getByType().named("libs") + minecraftVersion = libs.findVersion("parchmentMc").get().toString() + mappingsVersion = libs.findVersion("parchment").get().toString() + } +} + +dependencies { + val libs = project.extensions.getByType().named("libs") + implementation("net.neoforged:neoforge:${libs.findVersion("neoForge").get()}") } MinecraftConfigurations.setup(project) @@ -32,13 +40,3 @@ MinecraftConfigurations.setup(project) extensions.configure(CCTweakedExtension::class.java) { linters(minecraft = true, loader = "forge") } - -dependencies { - val libs = project.extensions.getByType().named("libs") - "minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}") -} - -tasks.configureEach { - // genIntellijRuns isn't registered until much later, so we need this silly hijinks. - if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() } -} diff --git a/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts b/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts index 981e16de8..50161c085 100644 --- a/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts +++ b/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts @@ -40,21 +40,10 @@ repositories { val mainMaven = maven("https://squiddev.cc/maven") { name = "SquidDev" - content { - // Until https://github.com/SpongePowered/Mixin/pull/593 is merged - includeModule("org.spongepowered", "mixin") - } } exclusiveContent { forRepositories(mainMaven) - - // Include the ForgeGradle repository if present. This requires that ForgeGradle is already present, which we - // enforce in our Forge overlay. - val fg = - project.extensions.findByType(net.minecraftforge.gradle.userdev.DependencyManagementExtension::class.java) - if (fg != null) forRepositories(fg.repository) - filter { includeGroup("cc.tweaked") // Things we mirror diff --git a/buildSrc/src/main/kotlin/cc/tweaked/gradle/ForgeExtensions.kt b/buildSrc/src/main/kotlin/cc/tweaked/gradle/ForgeExtensions.kt index bbf6e86c5..9fca2ce6c 100644 --- a/buildSrc/src/main/kotlin/cc/tweaked/gradle/ForgeExtensions.kt +++ b/buildSrc/src/main/kotlin/cc/tweaked/gradle/ForgeExtensions.kt @@ -4,23 +4,59 @@ package cc.tweaked.gradle -import net.minecraftforge.gradle.common.util.RunConfig -import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal +import net.neoforged.gradle.common.runs.run.RunImpl +import net.neoforged.gradle.common.runs.tasks.RunExec +import net.neoforged.gradle.dsl.common.extensions.RunnableSourceSet +import net.neoforged.gradle.dsl.common.runs.run.Run import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.SourceSet import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.kotlin.dsl.assign +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.findByType import java.nio.file.Files /** * Set [JavaExec] task to run a given [RunConfig]. + * + * See also [RunExec]. */ -fun JavaExec.setRunConfig(config: RunConfig) { - dependsOn("prepareRuns") - setRunConfigInternal(project, this, config) - doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) } +fun JavaExec.setRunConfig(config: Run) { + mainClass.set(config.mainClass) + workingDir = config.workingDirectory.get().asFile + argumentProviders.add { config.programArguments.get() } + jvmArgumentProviders.add { config.jvmArguments.get() } + + environment(config.environmentVariables.get()) + systemProperties(config.systemProperties.get()) + + config.modSources.get().forEach { classpath(it.runtimeClasspath) } + classpath(config.classpath) + classpath(config.dependencies.get().configuration) + + (config as RunImpl).taskDependencies.forEach { dependsOn(it) } javaLauncher.set( project.extensions.getByType(JavaToolchainService::class.java) .launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain), ) + + doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) } +} + +/** + * Add a new [Run.modSource] with a specific mod id. + */ +fun Run.modSourceAs(sourceSet: SourceSet, mod: String) { + // NeoGradle requires a RunnableSourceSet to be present, so we inject it into other project's source sets. + val runnable = sourceSet.extensions.findByType() ?: run { + val extension = sourceSet.extensions.create(RunnableSourceSet.NAME, project) + extension.modIdentifier = mod + extension.modIdentifier.finalizeValueOnRead() + extension + } + if (runnable.modIdentifier.get() != mod) throw IllegalArgumentException("Multiple mod identifiers") + + modSource(sourceSet) } diff --git a/buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt b/buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt index f923383c5..1719d4e5a 100644 --- a/buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt +++ b/buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt @@ -4,7 +4,6 @@ package cc.tweaked.gradle -import net.minecraftforge.gradle.common.util.RunConfig import org.gradle.api.GradleException import org.gradle.api.file.FileSystemOperations import org.gradle.api.invocation.Gradle @@ -65,14 +64,6 @@ abstract class ClientJavaExec : JavaExec() { setTestProperties() } - /** - * Set this task to run a given [RunConfig]. - */ - fun setRunConfig(config: RunConfig) { - (this as JavaExec).setRunConfig(config) - setTestProperties() // setRunConfig may clobber some properties, ensure everything is set. - } - /** * Copy configuration from a task with the given name. */ diff --git a/buildSrc/src/main/kotlin/net/minecraftforge/gradle/common/util/runs/RunConfigSetup.kt b/buildSrc/src/main/kotlin/net/minecraftforge/gradle/common/util/runs/RunConfigSetup.kt deleted file mode 100644 index a74d0008d..000000000 --- a/buildSrc/src/main/kotlin/net/minecraftforge/gradle/common/util/runs/RunConfigSetup.kt +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package net.minecraftforge.gradle.common.util.runs - -import net.minecraftforge.gradle.common.util.RunConfig -import org.gradle.api.Project -import org.gradle.process.CommandLineArgumentProvider -import org.gradle.process.JavaExecSpec -import java.io.File -import java.util.function.Supplier -import java.util.stream.Collectors -import java.util.stream.Stream - -/** - * Set up a [JavaExecSpec] to execute a [RunConfig]. - * - * [MinecraftRunTask] sets up all its properties when the task is executed, rather than when configured. As such, it's - * not possible to use [cc.tweaked.gradle.copyToFull] like we do for Fabric. Instead, we set up the task manually. - * - * Unfortunately most of the functionality we need is package-private, and so we have to put our code into the package. - */ -internal fun setRunConfigInternal(project: Project, spec: JavaExecSpec, config: RunConfig) { - spec.workingDir = File(config.workingDirectory) - - spec.mainClass.set(config.main) - for (source in config.allSources) spec.classpath(source.runtimeClasspath) - - val originalTask = project.tasks.named(config.taskName, MinecraftRunTask::class.java) - - // Add argument and JVM argument via providers, to be as lazy as possible with fetching artifacts. - val lazyTokens = RunConfigGenerator.configureTokensLazy( - project, config, RunConfigGenerator.mapModClassesToGradle(project, config), - originalTask.get().minecraftArtifacts, - originalTask.get().runtimeClasspathArtifacts, - ) - spec.argumentProviders.add( - CommandLineArgumentProvider { - RunConfigGenerator.getArgsStream(config, lazyTokens, false).toList() - }, - ) - spec.jvmArgumentProviders.add( - CommandLineArgumentProvider { - (if (config.isClient) config.jvmArgs + originalTask.get().additionalClientArgs.get() else config.jvmArgs).map { config.replace(lazyTokens, it) } + - config.properties.map { (k, v) -> "-D${k}=${config.replace(lazyTokens, v)}" } - }, - ) - - for ((key, value) in config.environment) spec.environment(key, config.replace(lazyTokens, value)) -} diff --git a/gradle.properties b/gradle.properties index e03753386..57e039f1c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,8 +9,8 @@ kotlin.stdlib.default.dependency=false kotlin.jvm.target.validation.mode=error # Mod properties -isUnstable=false +isUnstable=true modVersion=1.109.5 # Minecraft properties: We want to configure this here so we can read it in settings.gradle -mcVersion=1.20.1 +mcVersion=1.20.4 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d8b938a0..d3238e56a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,20 +7,20 @@ # Minecraft # MC version is specified in gradle.properties, as we need that in settings.gradle. # Remember to update corresponding versions in fabric.mod.json/mods.toml -fabric-api = "0.86.1+1.20.1" -fabric-loader = "0.14.21" -forge = "47.1.0" -forgeSpi = "7.0.1" +fabric-api = "0.93.1+1.20.4" +fabric-loader = "0.15.3" +neoForge = "20.4.142-beta" +neoForgeSpi = "8.0.1" mixin = "0.8.5" -parchment = "2023.08.20" -parchmentMc = "1.20.1" -yarn = "1.20.1+build.10" +parchment = "2023.12.31" +parchmentMc = "1.20.3" +yarn = "1.20.4+build.3" # Core dependencies (these versions are tied to the version Minecraft uses) -fastutil = "8.5.9" -guava = "31.1-jre" -netty = "4.1.82.Final" -slf4j = "2.0.1" +fastutil = "8.5.12" +guava = "32.1.2-jre" +netty = "4.1.97.Final" +slf4j = "2.0.7" # Core dependencies (independent of Minecraft) asm = "9.6" @@ -36,14 +36,14 @@ kotlin-coroutines = "1.7.3" nightConfig = "3.6.7" # Minecraft mods -emi = "1.0.8+1.20.1" +emi = "1.0.30+1.20.4" fabricPermissions = "0.3.20230723" -iris = "1.6.4+1.20" -jei = "15.2.0.22" -modmenu = "7.1.0" +iris = "1.6.14+1.20.4" +jei = "16.0.0.28" +modmenu = "9.0.0" moreRed = "4.0.0.4" oculus = "1.2.5" -rei = "12.0.626" +rei = "14.0.688" rubidium = "0.6.1" sodium = "mc1.20-0.4.10" @@ -55,19 +55,17 @@ junit = "5.10.1" # Build tools cctJavadoc = "1.8.2" checkstyle = "10.12.6" -curseForgeGradle = "1.0.14" +curseForgeGradle = "1.1.18" errorProne-core = "2.23.0" errorProne-plugin = "3.1.0" fabric-loom = "1.5.7" -forgeGradle = "6.0.20" githubRelease = "2.5.2" gradleVersions = "0.50.0" ideaExt = "1.1.7" illuaminate = "0.1.0-44-g9ee0055" -librarian = "1.+" lwjgl = "3.3.3" -minotaur = "2.+" -mixinGradle = "0.7.38" +minotaur = "2.8.7" +neoGradle = "7.0.85" nullAway = "0.9.9" spotless = "6.23.3" taskTree = "2.1.1" @@ -84,7 +82,7 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" } commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" } fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" } -forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" } +neoForgeSpi = { module = "net.neoforged:neoforgespi", version.ref = "neoForgeSpi" } guava = { module = "com.google.guava:guava", version.ref = "guava" } jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" } jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" } @@ -106,9 +104,9 @@ fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fab fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" } emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" } iris = { module = "maven.modrinth:iris", version.ref = "iris" } -jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" } -jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" } -jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" } +jei-api = { module = "mezz.jei:jei-1.20.2-common-api", version.ref = "jei" } +jei-fabric = { module = "mezz.jei:jei-1.20.2-fabric", version.ref = "jei" } +jei-forge = { module = "mezz.jei:jei-1.20.2-forge", version.ref = "jei" } mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" } modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" } @@ -144,11 +142,10 @@ errorProne-core = { module = "com.google.errorprone:error_prone_core", version.r errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" } errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" } fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" } -forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" } ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" } kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } -librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" } minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" } +neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "neoGradle" } nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" } spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" } @@ -163,12 +160,9 @@ vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = " yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" } [plugins] -forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" } githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" } gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" } -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" } @@ -179,9 +173,9 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"] # Minecraft externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"] externalMods-forge-compile = ["moreRed", "oculus", "jei-api"] -externalMods-forge-runtime = ["jei-forge"] +externalMods-forge-runtime = [] externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"] -externalMods-fabric-runtime = ["jei-fabric", "modmenu"] +externalMods-fabric-runtime = [] # Testing test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"] diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/ComputerCraftAPIClient.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/ComputerCraftAPIClient.java deleted file mode 100644 index 0d6bb52c7..000000000 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/ComputerCraftAPIClient.java +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.api.client; - -import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; -import dan200.computercraft.impl.client.ComputerCraftAPIClientService; - -/** - * The public API for client-only code. - * - * @see dan200.computercraft.api.ComputerCraftAPI The main API - */ -public final class ComputerCraftAPIClient { - private ComputerCraftAPIClient() { - } - - /** - * Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades. - *

- * This may be called at any point after registry creation, though it is recommended to call it within your client - * setup step. - * - * @param serialiser The turtle upgrade serialiser. - * @param modeller The upgrade modeller. - * @param The type of the turtle upgrade. - * @deprecated This method can lead to confusing load behaviour on Forge. Use - * {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} on Fabric, or - * {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} on Forge. - */ - @Deprecated(forRemoval = true) - public static void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { - // TODO(1.20.4): Remove this - getInstance().registerTurtleUpgradeModeller(serialiser, modeller); - } - - private static ComputerCraftAPIClientService getInstance() { - return ComputerCraftAPIClientService.get(); - } -} diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java index fb4f6121b..6a170d1d6 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java +++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java @@ -5,7 +5,7 @@ package dan200.computercraft.api.client.turtle; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; /** * A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades. @@ -22,5 +22,5 @@ public interface RegisterTurtleUpgradeModeller { * @param modeller The upgrade modeller. * @param The type of the turtle upgrade. */ - void register(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller); + void register(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller); } diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java index 7623f7d30..ae1b8a8c0 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java +++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java @@ -8,7 +8,6 @@ import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; -import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; @@ -31,30 +30,15 @@ public interface TurtleUpgradeModeller { /** * Obtain the model to be used when rendering a turtle peripheral. *

- * When the current turtle is {@literal null}, this function should be constant for a given upgrade and side. + * When the current turtle is {@literal null}, this function should be constant for a given upgrade, side and data. * * @param upgrade The upgrade that you're getting the model for. - * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless - * {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden. + * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models. * @param side Which side of the turtle (left or right) the upgrade resides on. - * @return The model that you wish to be used to render your upgrade. - */ - TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side); - - /** - * Obtain the model to be used when rendering a turtle peripheral. - *

- * This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available. - * - * @param upgrade The upgrade that you're getting the model for. * @param data Upgrade data instance for current turtle side. - * @param side Which side of the turtle (left or right) the upgrade resides on. * @return The model that you wish to be used to render your upgrade. */ - default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) { - return getModel(upgrade, (ITurtleAccess) null, side); - } - + TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data); /** * Get a list of models that this turtle modeller depends on. @@ -85,19 +69,6 @@ public interface TurtleUpgradeModeller { return (TurtleUpgradeModeller) TurtleUpgradeModellers.UPGRADE_ITEM; } - /** - * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side. - * - * @param left The model to use on the left. - * @param right The model to use on the right. - * @param The type of the turtle upgrade. - * @return The constructed modeller. - */ - static TurtleUpgradeModeller sided(ModelResourceLocation left, ModelResourceLocation right) { - // TODO(1.21.0): Remove this. - return sided((ResourceLocation) left, right); - } - /** * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side. * @@ -109,7 +80,7 @@ public interface TurtleUpgradeModeller { static TurtleUpgradeModeller sided(ResourceLocation left, ResourceLocation right) { return new TurtleUpgradeModeller<>() { @Override - public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { + public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) { return TransformedModel.of(side == TurtleSide.LEFT ? left : right); } diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java index 1af04e61b..5493cae27 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java +++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java @@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.impl.client.ClientPlatformHelper; import net.minecraft.client.Minecraft; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.item.ItemStack; import org.joml.Matrix4f; import javax.annotation.Nullable; @@ -37,16 +36,8 @@ final class TurtleUpgradeModellers { private static final class UpgradeItemModeller implements TurtleUpgradeModeller { @Override - public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { - return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side); - } - - @Override - public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) { - return getModel(upgrade.getUpgradeItem(data), side); - } - - private TransformedModel getModel(ItemStack stack, TurtleSide side) { + public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) { + var stack = upgrade.getUpgradeItem(data); var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack); if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model); return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform); diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java index 99ac916b9..5f5627a1c 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java @@ -4,15 +4,12 @@ package dan200.computercraft.api.pocket; -import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.upgrades.UpgradeBase; import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; import javax.annotation.Nullable; -import java.util.Map; /** * Wrapper class for pocket computers. @@ -90,13 +87,4 @@ public interface IPocketAccess { * entity} changes. */ void invalidatePeripheral(); - - /** - * Get a list of all upgrades for the pocket computer. - * - * @return A collection of all upgrade names. - * @deprecated This is a relic of a previous API, which no longer makes sense with newer versions of ComputerCraft. - */ - @Deprecated(forRemoval = true) - Map getUpgrades(); } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java index 1cf22e2d4..0f4d5be86 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java @@ -6,6 +6,10 @@ package dan200.computercraft.api.pocket; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.upgrades.UpgradeBase; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.impl.ComputerCraftAPIService; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; import javax.annotation.Nullable; @@ -13,16 +17,46 @@ import javax.annotation.Nullable; /** * A peripheral which can be equipped to the back side of a pocket computer. *

- * Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding - * {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry. + * Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding + * {@link UpgradeSerialiser} instance, which are then registered in a registry. *

* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and - * the upgrade registered internally. See the documentation in {@link PocketUpgradeSerialiser} for details on this process - * and where files should be located. + * the upgrade automatically registered. It is recommended this is done via {@linkplain PocketUpgradeDataProvider data + * generators}. * - * @see PocketUpgradeSerialiser For how to register a pocket computer upgrade. + *

Example

+ *
{@code
+ * // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
+ * static final DeferredRegister> SERIALISERS = DeferredRegister.create(IPocketUpgrade.serialiserRegistryKey(), "my_mod");
+ *
+ * // Register a new upgrade serialiser called "my_upgrade".
+ * public static final RegistryObject> MY_UPGRADE =
+ *     SERIALISERS.register("my_upgrade", () -> UpgradeSerialiser.simple(MyUpgrade::new));
+ *
+ * // Then in your constructor
+ * SERIALISERS.register(bus);
+ * }
+ *

+ * We can then define a new upgrade using JSON by placing the following in + * {@code data//computercraft/pocket_upgrades/.json}. + *

{@code
+ * {
+ *     "type": my_mod:my_upgrade",
+ * }
+ * }
+ *

+ * {@link PocketUpgradeDataProvider} provides a data provider to aid with generating these JSON files. */ public interface IPocketUpgrade extends UpgradeBase { + /** + * The registry key for upgrade serialisers. + * + * @return The registry key. + */ + static ResourceKey>> serialiserRegistryKey() { + return ComputerCraftAPIService.get().pocketUpgradeRegistryId(); + } + /** * Creates a peripheral for the pocket computer. *

diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java index 80250c1da..80a2ebc21 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java @@ -5,6 +5,7 @@ package dan200.computercraft.api.pocket; import dan200.computercraft.api.upgrades.UpgradeDataProvider; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; @@ -17,10 +18,11 @@ import java.util.function.Consumer; * {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to * generate them. * - * @see PocketUpgradeSerialiser + * @see IPocketUpgrade + * @see UpgradeSerialiser */ -public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider> { +public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider { public PocketUpgradeDataProvider(PackOutput output) { - super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId()); + super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey()); } } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeSerialiser.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeSerialiser.java deleted file mode 100644 index 40619c0df..000000000 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeSerialiser.java +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.api.pocket; - -import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import dan200.computercraft.impl.ComputerCraftAPIService; -import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; -import dan200.computercraft.impl.upgrades.SimpleSerialiser; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * Reads a {@link IPocketUpgrade} from disk and reads/writes it to a network packet. - *

- * This follows the same format as {@link dan200.computercraft.api.turtle.TurtleUpgradeSerialiser} - consult the - * documentation there for more information. - * - * @param The type of pocket computer upgrade this is responsible for serialising. - * @see IPocketUpgrade - * @see PocketUpgradeDataProvider - */ -public interface PocketUpgradeSerialiser extends UpgradeSerialiser { - /** - * The ID for the associated registry. - * - * @return The registry key. - */ - static ResourceKey>> registryId() { - return ComputerCraftAPIService.get().pocketUpgradeRegistryId(); - } - - /** - * Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer}, - * but for upgrades. - *

- * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead. - * - * @param factory Generate a new upgrade with a specific ID. - * @param The type of the generated upgrade. - * @return The serialiser for this upgrade - */ - static PocketUpgradeSerialiser simple(Function factory) { - final class Impl extends SimpleSerialiser implements PocketUpgradeSerialiser { - private Impl(Function constructor) { - super(constructor); - } - } - - return new Impl(factory); - } - - /** - * Create an upgrade serialiser for a simple upgrade whose crafting item can be specified. - * - * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's - * {@link UpgradeBase#getCraftingItem()} MUST equal the provided item. - * @param The type of the generated upgrade. - * @return The serialiser for this upgrade. - * @see #simple(Function) For upgrades whose crafting stack should not vary. - */ - static PocketUpgradeSerialiser simpleWithCustomItem(BiFunction factory) { - final class Impl extends SerialiserWithCraftingItem implements PocketUpgradeSerialiser { - private Impl(BiFunction factory) { - super(factory); - } - } - - return new Impl(factory); - } -} diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java index 2e4d7616b..c3f7c471f 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java @@ -6,8 +6,12 @@ package dan200.computercraft.api.turtle; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.upgrades.UpgradeBase; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.impl.ComputerCraftAPIService; import net.minecraft.core.Direction; +import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; import javax.annotation.Nullable; @@ -16,15 +20,54 @@ import javax.annotation.Nullable; * peripheral. *

* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding - * {@link TurtleUpgradeSerialiser} instance, which are then registered in a Forge registry. + * {@link UpgradeSerialiser} instance, which are then registered in a registry. *

* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and - * the upgrade registered internally. See the documentation in {@link TurtleUpgradeSerialiser} for details on this process - * and where files should be located. + * the upgrade automatically registered. It is recommended this is done via {@linkplain TurtleUpgradeDataProvider data + * generators}. * - * @see TurtleUpgradeSerialiser For how to register a turtle upgrade. + *

Example

+ *
{@code
+ * // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
+ * static final DeferredRegister> SERIALISERS = DeferredRegister.create(ITurtleUpgrade.serialiserRegistryKey(), "my_mod");
+ *
+ * // Register a new upgrade serialiser called "my_upgrade".
+ * public static final RegistryObject> MY_UPGRADE =
+ *     SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
+ *
+ * // Then in your constructor
+ * SERIALISERS.register( bus );
+ * }
+ *

+ * We can then define a new upgrade using JSON by placing the following in + * {@literal data//computercraft/turtle_upgrades/.json}}. + * + *

{@code
+ * {
+ *     "type": my_mod:my_upgrade",
+ * }
+ * }
+ *

+ * {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files. + *

+ * Finally, we need to register a model for our upgrade, see + * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information. + * + *

{@code
+ * // Register our model inside FMLClientSetupEvent
+ * ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
+ * }
*/ public interface ITurtleUpgrade extends UpgradeBase { + /** + * The registry key for upgrade serialisers. + * + * @return The registry key. + */ + static ResourceKey>> serialiserRegistryKey() { + return ComputerCraftAPIService.get().turtleUpgradeRegistryId(); + } + /** * Return whether this turtle adds a tool or a peripheral to the turtle. * diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java index 1a8118544..02019f0ca 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java @@ -7,8 +7,9 @@ package dan200.computercraft.api.turtle; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.upgrades.UpgradeDataProvider; -import dan200.computercraft.impl.PlatformHelper; -import net.minecraft.core.registries.Registries; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.impl.RegistryHelper; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; @@ -29,13 +30,13 @@ import java.util.function.Consumer; * {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to * generate them. * - * @see TurtleUpgradeSerialiser + * @see ITurtleUpgrade */ -public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider> { +public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider { private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool"); public TurtleUpgradeDataProvider(PackOutput output) { - super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId()); + super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey()); } /** @@ -57,7 +58,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider serialiser; + private final UpgradeSerialiser serialiser; private final Item toolItem; private @Nullable String adjective; private @Nullable Item craftingItem; @@ -66,7 +67,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider serialiser, Item toolItem) { + ToolBuilder(ResourceLocation id, UpgradeSerialiser serialiser, Item toolItem) { this.id = id; this.serialiser = serialiser; this.toolItem = toolItem; @@ -149,12 +150,12 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider>> add) { + public void add(Consumer>> add) { add.accept(new Upgrade<>(id, serialiser, s -> { - s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString()); + s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, toolItem).toString()); if (adjective != null) s.addProperty("adjective", adjective); if (craftingItem != null) { - s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString()); + s.addProperty("craftItem", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, craftingItem).toString()); } if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier); if (breakable != null) s.addProperty("breakable", breakable.location().toString()); diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeSerialiser.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeSerialiser.java deleted file mode 100644 index 863f36fa0..000000000 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeSerialiser.java +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.api.turtle; - -import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import dan200.computercraft.impl.ComputerCraftAPIService; -import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; -import dan200.computercraft.impl.upgrades.SimpleSerialiser; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet. - *

- * These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s. - *

- * If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use - * {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser. - * - *

Example (Forge)

- *
{@code
- * static final DeferredRegister> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
- *
- * // Register a new upgrade serialiser called "my_upgrade".
- * public static final RegistryObject> MY_UPGRADE =
- *     SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
- *
- * // Then in your constructor
- * SERIALISERS.register( bus );
- * }
- *

- * We can then define a new upgrade using JSON by placing the following in - * {@literal data//computercraft/turtle_upgrades/.json}}. - * - *

{@code
- * {
- *     "type": my_mod:my_upgrade",
- * }
- * }
- *

- * Finally, we need to register a model for our upgrade. The way to do this varies on mod loader, see - * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information. - *

- * {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files. - * - * @param The type of turtle upgrade this is responsible for serialising. - * @see ITurtleUpgrade - * @see TurtleUpgradeDataProvider - * @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller - */ -public interface TurtleUpgradeSerialiser extends UpgradeSerialiser { - /** - * The ID for the associated registry. - * - * @return The registry key. - */ - static ResourceKey>> registryId() { - return ComputerCraftAPIService.get().turtleUpgradeRegistryId(); - } - - /** - * Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer}, - * but for upgrades. - *

- * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead. - * - * @param factory Generate a new upgrade with a specific ID. - * @param The type of the generated upgrade. - * @return The serialiser for this upgrade - */ - static TurtleUpgradeSerialiser simple(Function factory) { - final class Impl extends SimpleSerialiser implements TurtleUpgradeSerialiser { - private Impl(Function constructor) { - super(constructor); - } - } - - return new Impl(factory); - } - - /** - * Create an upgrade serialiser for a simple upgrade whose crafting item can be specified. - * - * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's - * {@link UpgradeBase#getCraftingItem()} MUST equal the provided item. - * @param The type of the generated upgrade. - * @return The serialiser for this upgrade. - * @see #simple(Function) For upgrades whose crafting stack should not vary. - */ - static TurtleUpgradeSerialiser simpleWithCustomItem(BiFunction factory) { - final class Impl extends SerialiserWithCraftingItem implements TurtleUpgradeSerialiser { - private Impl(BiFunction factory) { - super(factory); - } - } - - return new Impl(factory); - } -} diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java index e5e48fd4f..603e1c7ae 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java @@ -9,7 +9,6 @@ import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.impl.PlatformHelper; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; @@ -100,7 +99,7 @@ public interface UpgradeBase { * The default check requires that any non-capability NBT is exactly the same as the * crafting item, but this may be relaxed for your upgrade. *

- * This is based on {@code net.minecraftforge.common.crafting.StrictNBTIngredient}'s check. + * This is based on {@code net.neoforged.common.crafting.StrictNBTIngredient}'s check. * * @param stack The stack to check. This is guaranteed to be non-empty and have the same item as * {@link #getCraftingItem()}. @@ -111,12 +110,12 @@ public interface UpgradeBase { // A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a // null one. - var shareTag = PlatformHelper.get().getShareTag(stack); - var craftingShareTag = PlatformHelper.get().getShareTag(crafting); - if (shareTag == craftingShareTag) return true; - if (shareTag == null) return Objects.requireNonNull(craftingShareTag).isEmpty(); - if (craftingShareTag == null) return shareTag.isEmpty(); - return shareTag.equals(craftingShareTag); + var tag = stack.getTag(); + var craftingTag = crafting.getTag(); + if (tag == craftingTag) return true; + if (tag == null) return Objects.requireNonNull(craftingTag).isEmpty(); + if (craftingTag == null) return tag.isEmpty(); + return tag.equals(craftingTag); } /** diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java index eae2be3d5..0d8804837 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java @@ -6,19 +6,20 @@ package dan200.computercraft.api.upgrades; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.impl.PlatformHelper; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; import dan200.computercraft.impl.upgrades.SimpleSerialiser; import net.minecraft.Util; import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; +import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nullable; import java.util.*; @@ -31,31 +32,31 @@ import java.util.function.Function; * the other subclasses. * * @param The base class of upgrades. - * @param The upgrade serialiser to register for. */ -public abstract class UpgradeDataProvider> implements DataProvider { +public abstract class UpgradeDataProvider implements DataProvider { private final PackOutput output; private final String name; private final String folder; - private final ResourceKey> registry; + private final Registry> registry; private @Nullable List upgrades; - protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey> registry) { + @ApiStatus.Internal + protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey>> registry) { this.output = output; this.name = name; this.folder = folder; - this.registry = registry; + this.registry = RegistryHelper.getRegistry(registry); } /** - * Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}). + * Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) "simple" serialiser}. * * @param id The ID of the upgrade to create. * @param serialiser The simple serialiser. * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer. */ - public final Upgrade simple(ResourceLocation id, R serialiser) { + public final Upgrade> simple(ResourceLocation id, UpgradeSerialiser serialiser) { if (!(serialiser instanceof SimpleSerialiser)) { throw new IllegalStateException(serialiser + " must be a simple() seriaiser."); } @@ -65,20 +66,20 @@ public abstract class UpgradeDataProvider simpleWithCustomItem(ResourceLocation id, R serialiser, Item item) { + public final Upgrade> simpleWithCustomItem(ResourceLocation id, UpgradeSerialiser serialiser, Item item) { if (!(serialiser instanceof SerialiserWithCraftingItem)) { throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser."); } return new Upgrade<>(id, serialiser, s -> - s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, item).toString()) + s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).toString()) ); } @@ -94,7 +95,7 @@ public abstract class UpgradeDataProvider> addUpgrade); + protected abstract void addUpgrades(Consumer>> addUpgrade); @Override public CompletableFuture run(CachedOutput cache) { @@ -107,7 +108,7 @@ public abstract class UpgradeDataProvider existingSerialiser(ResourceLocation id) { + var result = registry.get(id); + if (result == null) throw new IllegalArgumentException("No such serialiser " + id); return result; } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java index 813f423fc..65be99127 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java @@ -5,21 +5,40 @@ package dan200.computercraft.api.upgrades; import com.google.gson.JsonObject; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; +import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; +import dan200.computercraft.impl.upgrades.SimpleSerialiser; +import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; +import java.util.function.BiFunction; +import java.util.function.Function; /** - * Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one - * of {@link TurtleUpgradeSerialiser} or {@link PocketUpgradeSerialiser}. + * A serialiser for {@link ITurtleUpgrade} or {@link IPocketUpgrade}s. *

- * However, it may sometimes be useful to implement this if you have some shared logic between upgrade types. + * These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s. + *

+ * This interface is very similar to {@link RecipeSerializer}; each serialiser should correspond to a specific upgrade + * class. Upgrades are then read from JSON files in datapacks, allowing multiple instances of the upgrade to be + * registered. + *

+ * If your upgrade doesn't have any associated configurable parameters (like most upgrades), you can use + * {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser. + *

+ * Upgrades may be data generated via a {@link UpgradeDataProvider} (see {@link TurtleUpgradeDataProvider} and + * {@link PocketUpgradeDataProvider}). * * @param The upgrade that this class can serialise and deserialise. - * @see TurtleUpgradeSerialiser - * @see PocketUpgradeSerialiser + * @see ITurtleUpgrade + * @see IPocketUpgrade */ public interface UpgradeSerialiser { /** @@ -49,4 +68,30 @@ public interface UpgradeSerialiser { */ void toNetwork(FriendlyByteBuf buffer, T upgrade); + /** + * Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer}, + * but for upgrades. + *

+ * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead. + * + * @param factory Generate a new upgrade with a specific ID. + * @param The type of the generated upgrade. + * @return The serialiser for this upgrade + */ + static UpgradeSerialiser simple(Function factory) { + return new SimpleSerialiser<>(factory); + } + + /** + * Create an upgrade serialiser for a simple upgrade whose crafting item can be specified. + * + * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's + * {@link UpgradeBase#getCraftingItem()} MUST equal the provided item. + * @param The type of the generated upgrade. + * @return The serialiser for this upgrade. + * @see #simple(Function) For upgrades whose crafting stack should not vary. + */ + static UpgradeSerialiser simpleWithCustomItem(BiFunction factory) { + return new SerialiserWithCraftingItem<>(factory); + } } diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java b/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java index b45a8596a..597bb51c7 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java @@ -15,10 +15,11 @@ import dan200.computercraft.api.media.MediaProvider; import dan200.computercraft.api.network.PacketNetwork; import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredNode; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.BundledRedstoneProvider; +import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleRefuelHandler; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; @@ -67,9 +68,9 @@ public interface ComputerCraftAPIService { void registerRefuelHandler(TurtleRefuelHandler handler); - ResourceKey>> turtleUpgradeRegistryId(); + ResourceKey>> turtleUpgradeRegistryId(); - ResourceKey>> pocketUpgradeRegistryId(); + ResourceKey>> pocketUpgradeRegistryId(); DetailRegistry getItemStackDetailRegistry(); diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/PlatformHelper.java b/projects/common-api/src/main/java/dan200/computercraft/impl/PlatformHelper.java index a7ab333d4..675008a56 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/PlatformHelper.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/PlatformHelper.java @@ -6,11 +6,6 @@ package dan200.computercraft.impl; import com.google.gson.JsonObject; import dan200.computercraft.api.upgrades.UpgradeDataProvider; -import net.minecraft.core.Registry; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nullable; @@ -32,39 +27,6 @@ public interface PlatformHelper { return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance; } - /** - * Get the unique ID for a registered object. - * - * @param registry The registry to look up this object in. - * @param object The object to look up. - * @param The type of object the registry stores. - * @return The registered object's ID. - * @throws IllegalArgumentException If the registry or object are not registered. - */ - ResourceLocation getRegistryKey(ResourceKey> registry, T object); - - /** - * Look up an ID in a registry, returning the registered object. - * - * @param registry The registry to look up this object in. - * @param id The ID to look up. - * @param The type of object the registry stores. - * @return The resolved registry object. - * @throws IllegalArgumentException If the registry or object are not registered. - */ - T getRegistryObject(ResourceKey> registry, ResourceLocation id); - - /** - * Get the subset of an {@link ItemStack}'s {@linkplain ItemStack#getTag() tag} which is synced to the client. - * - * @param item The stack. - * @return The item's tag. - */ - @Nullable - default CompoundTag getShareTag(ItemStack item) { - return item.getTag(); - } - /** * Add a resource condition which requires a mod to be loaded. This should be used by data providers such as * {@link UpgradeDataProvider}. diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java b/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java new file mode 100644 index 000000000..0c1c02ae7 --- /dev/null +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.impl; + +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.ApiStatus; + +/** + * Additioanl functions for working with {@linkplain Registry registries}. + */ +@ApiStatus.Internal +public final class RegistryHelper { + private RegistryHelper() { + } + + /** + * Find a registry from a {@link ResourceKey}, throwing if it does not exist. + * + * @param id The id of the registry. + * @param The contents of the registry + * @return The associated registry. + */ + @SuppressWarnings("unchecked") + public static Registry getRegistry(ResourceKey> id) { + var registry = (Registry) BuiltInRegistries.REGISTRY.get(id.location()); + if (registry == null) throw new IllegalArgumentException("Unknown registry " + id); + return registry; + } + + /** + * Get the key of a registry entry, throwing if it is not registered. + * + * @param registry The registry to look up in. + * @param object The object to look up. + * @param The type of this registry. + * @return The ID of this object + * @see Registry#getResourceKey(Object) + */ + public static ResourceLocation getKeyOrThrow(Registry registry, T object) { + var key = registry.getResourceKey(object); + if (key.isEmpty()) throw new IllegalArgumentException(object + " was not registered in " + registry.key()); + return key.get().location(); + } +} diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java index c44c45b4f..49dc31729 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java @@ -23,27 +23,27 @@ import java.util.function.BiFunction; * @param The upgrade that this class can serialise and deserialise. */ @ApiStatus.Internal -public abstract class SerialiserWithCraftingItem implements UpgradeSerialiser { +public final class SerialiserWithCraftingItem implements UpgradeSerialiser { private final BiFunction factory; - protected SerialiserWithCraftingItem(BiFunction factory) { + public SerialiserWithCraftingItem(BiFunction factory) { this.factory = factory; } @Override - public final T fromJson(ResourceLocation id, JsonObject object) { + public T fromJson(ResourceLocation id, JsonObject object) { var item = GsonHelper.getAsItem(object, "item"); return factory.apply(id, new ItemStack(item)); } @Override - public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { + public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { var item = buffer.readItem(); return factory.apply(id, item); } @Override - public final void toNetwork(FriendlyByteBuf buffer, T upgrade) { + public void toNetwork(FriendlyByteBuf buffer, T upgrade) { buffer.writeItem(upgrade.getCraftingItem()); } } diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java index c8adccd5f..ffc6e49fe 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java @@ -21,7 +21,7 @@ import java.util.function.Function; * @param The upgrade that this class can serialise and deserialise. */ @ApiStatus.Internal -public abstract class SimpleSerialiser implements UpgradeSerialiser { +public final class SimpleSerialiser implements UpgradeSerialiser { private final Function constructor; public SimpleSerialiser(Function constructor) { @@ -29,16 +29,16 @@ public abstract class SimpleSerialiser implements Upgrade } @Override - public final T fromJson(ResourceLocation id, JsonObject object) { + public T fromJson(ResourceLocation id, JsonObject object) { return constructor.apply(id); } @Override - public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { + public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { return constructor.apply(id); } @Override - public final void toNetwork(FriendlyByteBuf buffer, T upgrade) { + public void toNetwork(FriendlyByteBuf buffer, T upgrade) { } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java b/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java index 36b331939..ea1575ab1 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java +++ b/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java @@ -108,7 +108,7 @@ public final class ClientHooks { */ public static void addBlockDebugInfo(Consumer addText) { var minecraft = Minecraft.getInstance(); - if (!minecraft.options.renderDebug || minecraft.level == null) return; + if (!minecraft.getDebugOverlay().showDebugScreen() || minecraft.level == null) return; if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return; var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos()); @@ -138,7 +138,7 @@ public final class ClientHooks { * @param addText A callback which adds a single line of text. */ public static void addGameDebugInfo(Consumer addText) { - if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().options.renderDebug) { + if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().getDebugOverlay().showDebugScreen()) { addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer()); } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java index d319f2e90..aa5d9ddc9 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java +++ b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java @@ -31,6 +31,8 @@ import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.color.item.ItemColor; import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.MenuAccess; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; @@ -42,6 +44,8 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.ResourceProvider; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ItemLike; @@ -79,17 +83,6 @@ public final class ClientRegistry { * Register any client-side objects which must be done on the main thread. */ public static void registerMainThread() { - MenuScreens.>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new); - MenuScreens.>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new); - MenuScreens.>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new); - MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new); - - MenuScreens.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new); - MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new); - MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new); - - MenuScreens.>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new); - registerItemProperty("state", new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()), ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED @@ -100,6 +93,23 @@ public final class ClientRegistry { ); } + public static void registerMenuScreens(RegisterMenuScreen register) { + register.>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new); + register.>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new); + register.>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new); + register.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new); + + register.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new); + register.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new); + register.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new); + + register.>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new); + } + + public interface RegisterMenuScreen { + > void register(MenuType type, MenuScreens.ScreenConstructor factory); + } + public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) { register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided( new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"), diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java index 50f12c15e..21660081c 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java @@ -125,7 +125,6 @@ public abstract class AbstractComputerScreen ext @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - renderBackground(graphics); super.render(graphics, mouseX, mouseY, partialTicks); renderTooltip(graphics, mouseX, mouseY); } diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/DiskDriveScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/DiskDriveScreen.java index 5fe8fe15b..86fcbba52 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/DiskDriveScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/DiskDriveScreen.java @@ -28,7 +28,6 @@ public class DiskDriveScreen extends AbstractContainerScreen { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - renderBackground(graphics); super.render(graphics, mouseX, mouseY, partialTicks); renderTooltip(graphics, mouseX, mouseY); } diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/ItemToast.java b/projects/common/src/client/java/dan200/computercraft/client/gui/ItemToast.java index 9b08ca664..850db86ee 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/ItemToast.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/ItemToast.java @@ -9,6 +9,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.toasts.Toast; import net.minecraft.client.gui.components.toasts.ToastComponent; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.FormattedCharSequence; import net.minecraft.world.item.ItemStack; @@ -18,6 +19,7 @@ import java.util.List; * A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}. */ public class ItemToast implements Toast { + private static final ResourceLocation TEXTURE = new ResourceLocation("toast/recipe"); public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object(); private static final long DISPLAY_TIME = 7000L; @@ -79,7 +81,7 @@ public class ItemToast implements Toast { } if (width == 160 && message.size() <= 1) { - graphics.blit(TEXTURE, 0, 0, 0, 64, width, height()); + graphics.blitSprite(TEXTURE, 0, 0, width, height()); } else { var height = height(); @@ -109,14 +111,14 @@ public class ItemToast implements Toast { } private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) { - var leftOffset = 5; + var leftOffset = u == 0 ? 20 : 5; var rightOffset = Math.min(60, x - leftOffset); - graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height); + graphics.blitSprite(TEXTURE, 160, 32, 0, u, 0, y, leftOffset, height); for (var k = leftOffset; k < x - rightOffset; k += 64) { - graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height); + graphics.blitSprite(TEXTURE, 160, 32, 32, u, k, y, Math.min(64, x - k - rightOffset), height); } - graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height); + graphics.blitSprite(TEXTURE, 160, 32, 160 - rightOffset, u, x - rightOffset, y, rightOffset, height); } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java index 67ad95ba5..ca7cc8ab8 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java @@ -63,9 +63,9 @@ public class NoTermComputerScreen extends Screen } @Override - public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) { - minecraft.player.getInventory().swapPaint(pDelta); - return super.mouseScrolled(pMouseX, pMouseY, pDelta); + public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) { + minecraft.player.getInventory().swapPaint(scrollX); + return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY); } @Override diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/OptionScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/OptionScreen.java index 1dae0bcea..36e74a698 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/OptionScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/OptionScreen.java @@ -86,8 +86,6 @@ public final class OptionScreen extends Screen { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - renderBackground(graphics); - // Render the actual texture. graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING); graphics.blit(BACKGROUND, diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/PrinterScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/PrinterScreen.java index ff2b9899d..acc380536 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/PrinterScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/PrinterScreen.java @@ -30,7 +30,6 @@ public class PrinterScreen extends AbstractContainerScreen { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - renderBackground(graphics); super.render(graphics, mouseX, mouseY, partialTicks); renderTooltip(graphics, mouseX, mouseY); } diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/PrintoutScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/PrintoutScreen.java index 8ed3b8f64..83b285243 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/PrintoutScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/PrintoutScreen.java @@ -64,15 +64,15 @@ public class PrintoutScreen extends AbstractContainerScreen { } @Override - public boolean mouseScrolled(double x, double y, double delta) { - if (super.mouseScrolled(x, y, delta)) return true; - if (delta < 0) { + public boolean mouseScrolled(double x, double y, double deltaX, double deltaY) { + if (super.mouseScrolled(x, y, deltaX, deltaY)) return true; + if (deltaX < 0) { // Scroll up goes to the next page if (page < pages - 1) page++; return true; } - if (delta > 0) { + if (deltaX > 0) { // Scroll down goes to the previous page if (page > 0) page--; return true; @@ -91,14 +91,12 @@ public class PrintoutScreen extends AbstractContainerScreen { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { // We must take the background further back in order to not overlap with our printed pages. graphics.pose().pushPose(); graphics.pose().translate(0, 0, -1); - renderBackground(graphics); + super.renderBackground(graphics, mouseX, mouseY, partialTicks); graphics.pose().popPose(); - - super.render(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java b/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java index 2434e277d..88f5f02d7 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java @@ -43,6 +43,10 @@ public class DynamicImageButton extends Button { @Override public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + var message = this.message.get(); + setMessage(message.message()); + setTooltip(message.tooltip()); + var texture = this.texture.get(isHoveredOrFocused()); RenderSystem.disableDepthTest(); @@ -50,14 +54,6 @@ public class DynamicImageButton extends Button { RenderSystem.enableDepthTest(); } - @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - var message = this.message.get(); - setMessage(message.message()); - setTooltip(message.tooltip()); - super.render(graphics, mouseX, mouseY, partialTicks); - } - public record HintedMessage(Component message, Tooltip tooltip) { public HintedMessage(Component message, @Nullable Component hint) { this( diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java b/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java index f19221382..a526a6228 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java @@ -195,7 +195,7 @@ public class TerminalWidget extends AbstractWidget { } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + public boolean mouseScrolled(double mouseX, double mouseY, double delta, double deltaY) { if (!inTermRegion(mouseX, mouseY)) return false; if (!hasMouseSupport() || delta == 0) return false; diff --git a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java index 4200f9545..3a489fee2 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java +++ b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java @@ -11,7 +11,7 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.network.protocol.common.ServerCommonPacketListener; import net.minecraft.sounds.SoundEvent; import javax.annotation.Nullable; @@ -27,7 +27,7 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C * @param message The messsge to convert. * @return The converted message. */ - Packet createPacket(NetworkMessage message); + Packet createPacket(NetworkMessage message); /** * Render a {@link BakedModel}, using any loader-specific hooks. diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/SpriteRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/SpriteRenderer.java index b7d0817c9..e9cb768f5 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/SpriteRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/SpriteRenderer.java @@ -125,10 +125,10 @@ public class SpriteRenderer { } public static float u(TextureAtlasSprite sprite, int x, int width) { - return sprite.getU((double) x / width * 16); + return sprite.getU((float) x / width); } public static float v(TextureAtlasSprite sprite, int y, int height) { - return sprite.getV((double) y / height * 16); + return sprite.getV((float) y / height); } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java index 7b1df0034..98b1c37d2 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java @@ -9,6 +9,7 @@ import com.mojang.blaze3d.platform.MemoryTracker; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import com.mojang.math.Axis; +import dan200.computercraft.annotations.ForgeOverride; import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.integration.ShaderMod; import dan200.computercraft.client.render.RenderTypes; @@ -25,6 +26,7 @@ import dan200.computercraft.shared.util.DirectionUtil; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.world.phys.AABB; import org.joml.Matrix3f; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; @@ -255,6 +257,11 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer { } @Override - public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { + public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) { var active = false; if (turtle != null) { var turtleNBT = turtle.getUpgradeNBTData(side); diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java index 7bff349a2..b27b32277 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java +++ b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java @@ -10,15 +10,13 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; -import dan200.computercraft.impl.PlatformHelper; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.impl.UpgradeManager; import net.minecraft.client.Minecraft; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Map; import java.util.WeakHashMap; @@ -29,12 +27,10 @@ import java.util.stream.Stream; * A registry of {@link TurtleUpgradeModeller}s. */ public final class TurtleUpgradeModellers { - private static final Logger LOG = LoggerFactory.getLogger(TurtleUpgradeModellers.class); - - private static final TurtleUpgradeModeller NULL_TURTLE_MODELLER = (upgrade, turtle, side) -> + private static final TurtleUpgradeModeller NULL_TURTLE_MODELLER = (upgrade, turtle, side, data) -> new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity()); - private static final Map, TurtleUpgradeModeller> turtleModels = new ConcurrentHashMap<>(); + private static final Map, TurtleUpgradeModeller> turtleModels = new ConcurrentHashMap<>(); private static volatile boolean fetchedModels; /** @@ -48,15 +44,12 @@ public final class TurtleUpgradeModellers { private TurtleUpgradeModellers() { } - public static void register(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { + public static void register(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { if (fetchedModels) { - // TODO(1.20.4): Replace with an error. - LOG.warn( - "Turtle upgrade serialiser {} was registered too late, its models may not be loaded correctly. If you are " + - "the mod author, you may be using a deprecated API - see https://github.com/cc-tweaked/CC-Tweaked/pull/1684 " + - "for further information.", - PlatformHelper.get().getRegistryKey(TurtleUpgradeSerialiser.registryId(), serialiser) - ); + throw new IllegalStateException(String.format( + "Turtle upgrade serialiser %s must be registered before models are baked.", + RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.serialiserRegistryKey()), serialiser) + )); } if (turtleModels.putIfAbsent(serialiser, modeller) != null) { @@ -67,13 +60,13 @@ public final class TurtleUpgradeModellers { public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) { @SuppressWarnings("unchecked") var modeller = (TurtleUpgradeModeller) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller); - return modeller.getModel(upgrade, access, side); + return modeller.getModel(upgrade, access, side, access.getUpgradeNBTData(side)); } public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) { @SuppressWarnings("unchecked") var modeller = (TurtleUpgradeModeller) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller); - return modeller.getModel(upgrade, data, side); + return modeller.getModel(upgrade, null, side, data); } private static TurtleUpgradeModeller getModeller(ITurtleUpgrade upgradeA) { diff --git a/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java b/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java index 67568247b..999acca27 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java @@ -17,13 +17,12 @@ import dan200.computercraft.shared.computer.metrics.basic.Aggregate; import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric; import dan200.computercraft.shared.config.ConfigFile; import dan200.computercraft.shared.config.ConfigSpec; -import dan200.computercraft.shared.platform.RegistryWrappers; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; import java.util.HashMap; import java.util.Map; @@ -272,12 +271,12 @@ public final class LanguageProvider implements DataProvider { private Stream getExpectedKeys() { return Stream.of( - RegistryWrappers.BLOCKS.stream() - .filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID)) - .map(Block::getDescriptionId), - RegistryWrappers.ITEMS.stream() - .filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID)) - .map(Item::getDescriptionId), + BuiltInRegistries.BLOCK.holders() + .filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID)) + .map(x -> x.value().getDescriptionId()), + BuiltInRegistries.ITEM.holders() + .filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID)) + .map(x -> x.value().getDescriptionId()), turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective), pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective), Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"), diff --git a/projects/common/src/main/java/dan200/computercraft/data/ModelProvider.java b/projects/common/src/main/java/dan200/computercraft/data/ModelProvider.java index 0e0fd8381..357fdfe40 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/ModelProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/ModelProvider.java @@ -5,8 +5,9 @@ package dan200.computercraft.data; import com.google.gson.JsonElement; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.impl.RegistryHelper; import net.minecraft.Util; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; @@ -67,7 +68,7 @@ public class ModelProvider implements DataProvider { blocks.accept(new BlockModelGenerators(addBlockState, addModel, explicitItems::add)); items.accept(new ItemModelGenerators(addModel)); - for (var block : RegistryWrappers.BLOCKS) { + for (var block : BuiltInRegistries.BLOCK) { if (!blockStates.containsKey(block)) continue; var item = Item.BY_BLOCK.get(block); @@ -80,7 +81,7 @@ public class ModelProvider implements DataProvider { } List> futures = new ArrayList<>(); - saveCollection(output, futures, blockStates, x -> blockStatePath.json(RegistryWrappers.BLOCKS.getKey(x))); + saveCollection(output, futures, blockStates, x -> blockStatePath.json(RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK, x))); saveCollection(output, futures, models, modelPath::json); return Util.sequenceFailFast(futures); } diff --git a/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java index d9c4f27ca..4c0f61cd0 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java @@ -5,8 +5,9 @@ package dan200.computercraft.data; import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; @@ -21,7 +22,7 @@ class PocketUpgradeProvider extends PocketUpgradeDataProvider { } @Override - protected void addUpgrades(Consumer>> addUpgrade) { + protected void addUpgrades(Consumer>> addUpgrade) { addUpgrade.accept(simpleWithCustomItem(id("speaker"), PocketUpgradeSerialisers.SPEAKER.get(), Items.SPEAKER.get())); simpleWithCustomItem(id("wireless_modem_normal"), PocketUpgradeSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade); simpleWithCustomItem(id("wireless_modem_advanced"), PocketUpgradeSerialisers.WIRELESS_MODEM_ADVANCED.get(), Items.WIRELESS_MODEM_ADVANCED.get()).add(addUpgrade); diff --git a/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java index 23b91d6ef..5c4d09612 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java @@ -5,34 +5,56 @@ package dan200.computercraft.data; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.mojang.authlib.GameProfile; +import com.mojang.serialization.JsonOps; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeData; import dan200.computercraft.core.util.Colour; +import dan200.computercraft.data.recipe.ShapedSpecBuilder; +import dan200.computercraft.data.recipe.ShapelessSpecBuilder; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.common.IColouredItem; +import dan200.computercraft.shared.common.ClearColourRecipe; +import dan200.computercraft.shared.common.ColourableRecipe; +import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe; +import dan200.computercraft.shared.media.items.DiskItem; +import dan200.computercraft.shared.media.recipes.DiskRecipe; +import dan200.computercraft.shared.media.recipes.PrintoutRecipe; import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.platform.RecipeIngredients; -import dan200.computercraft.shared.platform.RegistryWrappers; import dan200.computercraft.shared.pocket.items.PocketComputerItem; +import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe; +import dan200.computercraft.shared.recipe.CustomShapelessRecipe; +import dan200.computercraft.shared.recipe.ImpostorShapedRecipe; +import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe; import dan200.computercraft.shared.turtle.items.TurtleItem; +import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe; +import dan200.computercraft.shared.turtle.recipes.TurtleRecipe; +import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe; import dan200.computercraft.shared.util.ColourUtils; +import net.minecraft.Util; +import net.minecraft.advancements.Criterion; import net.minecraft.advancements.critereon.InventoryChangeTrigger; import net.minecraft.advancements.critereon.ItemPredicate; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.data.PackOutput; -import net.minecraft.data.recipes.*; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.data.recipes.ShapedRecipeBuilder; +import net.minecraft.data.recipes.ShapelessRecipeBuilder; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import net.minecraft.util.GsonHelper; import net.minecraft.world.item.*; +import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.ShapedRecipe; -import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; +import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Blocks; @@ -43,7 +65,7 @@ import java.util.function.Consumer; import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER; import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM; -class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { +final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { private final RecipeIngredients ingredients = PlatformHelper.get().getRecipeIngredients(); private final TurtleUpgradeDataProvider turtleUpgrades; private final PocketUpgradeDataProvider pocketUpgrades; @@ -55,40 +77,37 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { } @Override - public void buildRecipes(Consumer add) { + public void buildRecipes(RecipeOutput add) { basicRecipes(add); diskColours(add); pocketUpgrades(add); turtleUpgrades(add); turtleOverlays(add); - addSpecial(add, ModRegistry.RecipeSerializers.PRINTOUT.get()); - addSpecial(add, ModRegistry.RecipeSerializers.DISK.get()); - addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM.get()); - addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM_CLEAR.get()); - addSpecial(add, ModRegistry.RecipeSerializers.TURTLE_UPGRADE.get()); - addSpecial(add, ModRegistry.RecipeSerializers.POCKET_COMPUTER_UPGRADE.get()); + addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC)); + addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC)); + addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC)); + addSpecial(add, new ClearColourRecipe(CraftingBookCategory.MISC)); + addSpecial(add, new TurtleUpgradeRecipe(CraftingBookCategory.MISC)); + addSpecial(add, new PocketComputerUpgradeRecipe(CraftingBookCategory.MISC)); } /** * Register a crafting recipe for a disk of every dye colour. * - * @param add The callback to add recipes. + * @param output The callback to add recipes. */ - private void diskColours(Consumer add) { + private void diskColours(RecipeOutput output) { for (var colour : Colour.VALUES) { - ShapelessRecipeBuilder - .shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.DISK.get()) + ShapelessSpecBuilder + .shapeless(RecipeCategory.REDSTONE, DiskItem.createFromIDAndColour(-1, null, colour.getHex())) .requires(ingredients.redstone()) .requires(Items.PAPER) .requires(DyeItem.byColor(ofColour(colour))) .group("computercraft:disk") .unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get())) - .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add) - .withResultTag(x -> x.putInt(IColouredItem.NBT_COLOUR, colour.getHex())), - new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1)) - ); + .build(ImpostorShapelessRecipe::new) + .save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1))); } } @@ -101,23 +120,22 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { * * @param add The callback to add recipes. */ - private void turtleUpgrades(Consumer add) { + private void turtleUpgrades(RecipeOutput add) { for (var turtleItem : turtleItems()) { var base = turtleItem.create(-1, null, -1, null, null, 0, null); - var name = RegistryWrappers.ITEMS.getKey(turtleItem); + var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem); for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) { - var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null); - ShapedRecipeBuilder - .shaped(RecipeCategory.REDSTONE, result.getItem()) + ShapedSpecBuilder + .shaped(RecipeCategory.REDSTONE, turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null)) .group(name.toString()) .pattern("#T") .define('T', base.getItem()) .define('#', upgrade.getCraftingItem().getItem()) - .unlockedBy("has_items", - inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem())) + .unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem())) + .build(ImpostorShapedRecipe::new) .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()), + add, name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath())) ); } @@ -133,31 +151,30 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { * * @param add The callback to add recipes. */ - private void pocketUpgrades(Consumer add) { + private void pocketUpgrades(RecipeOutput add) { for (var pocket : pocketComputerItems()) { var base = pocket.create(-1, null, -1, null); - var name = RegistryWrappers.ITEMS.getKey(pocket).withPath(x -> x.replace("pocket_computer_", "pocket_")); + var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_")); for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) { - var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade)); - ShapedRecipeBuilder - .shaped(RecipeCategory.REDSTONE, result.getItem()) + ShapedSpecBuilder + .shaped(RecipeCategory.REDSTONE, pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade))) .group(name.toString()) .pattern("#") .pattern("P") .define('P', base.getItem()) .define('#', upgrade.getCraftingItem().getItem()) - .unlockedBy("has_items", - inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem())) + .unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem())) + .build(ImpostorShapedRecipe::new) .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()), + add, name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath())) ); } } } - private void turtleOverlays(Consumer add) { + private void turtleOverlays(RecipeOutput add) { turtleOverlay(add, "turtle_trans_overlay", x -> x .unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye()))) .requires(ColourUtils.getDyeTag(DyeColor.LIGHT_BLUE)) @@ -178,28 +195,24 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { ); } - private void turtleOverlay(Consumer add, String overlay, Consumer build) { + private void turtleOverlay(RecipeOutput add, String overlay, Consumer build) { for (var turtleItem : turtleItems()) { var base = turtleItem.create(-1, null, -1, null, null, 0, null); - var name = RegistryWrappers.ITEMS.getKey(turtleItem); + var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem); - var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem()) + var builder = ShapelessSpecBuilder.shapeless(RecipeCategory.REDSTONE, base) .group(name.withSuffix("_overlay").toString()) .unlockedBy("has_turtle", inventoryChange(base.getItem())); build.accept(builder); builder .requires(base.getItem()) - .save( - RecipeWrapper - .wrap(ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get(), add) - .withExtraData(x -> x.addProperty("overlay", new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay).toString())), - name.withSuffix("_overlays/" + overlay) - ); + .build(s -> new TurtleOverlayRecipe(s, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay))) + .save(add, name.withSuffix("_overlays/" + overlay)); } } - private void basicRecipes(Consumer add) { + private void basicRecipes(RecipeOutput add) { ShapedRecipeBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.CABLE.get(), 6) .pattern(" # ") @@ -233,7 +246,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .unlockedBy("has_components", inventoryChange(itemPredicate(ingredients.redstone()), itemPredicate(ingredients.goldIngot()))) .save(add); - ShapedRecipeBuilder + ShapedSpecBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get()) .pattern("###") .pattern("#C#") @@ -241,10 +254,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .define('#', ingredients.goldIngot()) .define('C', ModRegistry.Items.COMPUTER_NORMAL.get()) .unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot()))) - .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add), - new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade") - ); + .build(ComputerUpgradeRecipe::new) + .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")); ShapedRecipeBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get()) @@ -257,7 +268,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK)) .save(add); - ShapedRecipeBuilder + ShapedSpecBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get()) .pattern("###") .pattern("#C#") @@ -266,9 +277,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .define('C', ModRegistry.Items.COMPUTER_NORMAL.get()) .define('I', ingredients.woodenChest()) .unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get())) - .save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add)); + .buildOrThrow(TurtleRecipe::of) + .save(add); - ShapedRecipeBuilder + ShapedSpecBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get()) .pattern("###") .pattern("#C#") @@ -277,9 +289,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .define('C', ModRegistry.Items.COMPUTER_ADVANCED.get()) .define('I', ingredients.woodenChest()) .unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get())) - .save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add)); + .buildOrThrow(TurtleRecipe::of) + .save(add); - ShapedRecipeBuilder + ShapedSpecBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get()) .pattern("###") .pattern("#C#") @@ -288,10 +301,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .define('C', ModRegistry.Items.TURTLE_NORMAL.get()) .define('B', ingredients.goldBlock()) .unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot()))) - .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add), - new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade") - ); + .build(ComputerUpgradeRecipe::new) + .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")); ShapedRecipeBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get()) @@ -347,7 +358,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .unlockedBy("has_apple", inventoryChange(Items.GOLDEN_APPLE)) .save(add); - ShapedRecipeBuilder + ShapedSpecBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get()) .pattern("###") .pattern("#C#") @@ -355,10 +366,8 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .define('#', ingredients.goldIngot()) .define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()) .unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot()))) - .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add), - new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade") - ); + .build(ComputerUpgradeRecipe::new) + .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")); ShapedRecipeBuilder .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get()) @@ -425,57 +434,53 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())) .save(add); - ShapelessRecipeBuilder - .shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD) + ShapelessSpecBuilder + .shapeless(RecipeCategory.DECORATIONS, playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c")) .requires(ingredients.head()) .requires(ModRegistry.Items.MONITOR_NORMAL.get()) .unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get())) - .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add) - .withResultTag(playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c")), - new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy") - ); + .build(CustomShapelessRecipe::new) + .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy")); - ShapelessRecipeBuilder - .shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD) + ShapelessSpecBuilder + .shapeless(RecipeCategory.DECORATIONS, playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb")) .requires(ingredients.head()) .requires(ModRegistry.Items.COMPUTER_ADVANCED.get()) .unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get())) - .save( - RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add) - .withResultTag(playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb")), - new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200") - ); + .build(CustomShapelessRecipe::new) + .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200")); - ShapelessRecipeBuilder + ShapelessSpecBuilder .shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get()) .requires(ModRegistry.Items.PRINTED_PAGE.get(), 2) .requires(ingredients.string()) .unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get())) - .save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add)); + .build(ImpostorShapelessRecipe::new) + .save(add); - ShapelessRecipeBuilder + ShapelessSpecBuilder .shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get()) .requires(ingredients.leather()) .requires(ModRegistry.Items.PRINTED_PAGE.get(), 1) .requires(ingredients.string()) .unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get())) - .save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add)); + .build(ImpostorShapelessRecipe::new) + .save(add); } private static DyeColor ofColour(Colour colour) { return DyeColor.byId(15 - colour.ordinal()); } - private static InventoryChangeTrigger.TriggerInstance inventoryChange(TagKey stack) { + private static Criterion inventoryChange(TagKey stack) { return InventoryChangeTrigger.TriggerInstance.hasItems(itemPredicate(stack)); } - private static InventoryChangeTrigger.TriggerInstance inventoryChange(ItemLike... stack) { + private static Criterion inventoryChange(ItemLike... stack) { return InventoryChangeTrigger.TriggerInstance.hasItems(stack); } - private static InventoryChangeTrigger.TriggerInstance inventoryChange(ItemPredicate... items) { + private static Criterion inventoryChange(ItemPredicate... items) { return InventoryChangeTrigger.TriggerInstance.hasItems(items); } @@ -488,11 +493,12 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { } private static ItemPredicate itemPredicate(Ingredient ingredient) { - var json = ingredient.toJson(); + var json = Util.getOrThrow(Ingredient.CODEC_NONEMPTY.encodeStart(JsonOps.INSTANCE, ingredient), JsonParseException::new); if (!(json instanceof JsonObject object)) throw new IllegalStateException("Unknown ingredient " + json); if (object.has("item")) { - return itemPredicate(ShapedRecipe.itemFromJson(object)); + var item = Util.getOrThrow(ItemStack.ITEM_WITH_COUNT_CODEC.parse(JsonOps.INSTANCE, object), JsonParseException::new); + return itemPredicate(item.getItem()); } else if (object.has("tag")) { return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag")))); } else { @@ -500,15 +506,14 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { } } - private static CompoundTag playerHead(String name, String uuid) { + private static ItemStack playerHead(String name, String uuid) { + var item = new ItemStack(Items.PLAYER_HEAD); var owner = NbtUtils.writeGameProfile(new CompoundTag(), new GameProfile(UUID.fromString(uuid), name)); - - var tag = new CompoundTag(); - tag.put(PlayerHeadItem.TAG_SKULL_OWNER, owner); - return tag; + item.getOrCreateTag().put(PlayerHeadItem.TAG_SKULL_OWNER, owner); + return item; } - private static void addSpecial(Consumer add, SimpleCraftingRecipeSerializer special) { - SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString()); + private static void addSpecial(RecipeOutput add, Recipe recipe) { + add.accept(RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, recipe.getSerializer()), recipe, null); } } diff --git a/projects/common/src/main/java/dan200/computercraft/data/RecipeWrapper.java b/projects/common/src/main/java/dan200/computercraft/data/RecipeWrapper.java deleted file mode 100644 index 3d31c98f0..000000000 --- a/projects/common/src/main/java/dan200/computercraft/data/RecipeWrapper.java +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.data; - -import com.google.gson.JsonObject; -import net.minecraft.data.recipes.FinishedRecipe; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.item.crafting.RecipeSerializer; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -/** - * Adapter for recipes which overrides the serializer and adds custom item NBT. - */ -final class RecipeWrapper implements Consumer { - private final Consumer add; - private final RecipeSerializer serializer; - private final List> extend = new ArrayList<>(0); - - RecipeWrapper(Consumer add, RecipeSerializer serializer) { - this.add = add; - this.serializer = serializer; - } - - public static RecipeWrapper wrap(RecipeSerializer serializer, Consumer original) { - return new RecipeWrapper(original, serializer); - } - - public RecipeWrapper withExtraData(Consumer extra) { - extend.add(extra); - return this; - } - - public RecipeWrapper withResultTag(@Nullable CompoundTag resultTag) { - if (resultTag == null) return this; - - extend.add(json -> { - var object = GsonHelper.getAsJsonObject(json, "result"); - object.addProperty("nbt", resultTag.toString()); - }); - return this; - } - - public RecipeWrapper withResultTag(Consumer resultTag) { - var tag = new CompoundTag(); - resultTag.accept(tag); - return withResultTag(tag); - } - - @Override - public void accept(FinishedRecipe finishedRecipe) { - add.accept(new RecipeImpl(finishedRecipe, serializer, extend)); - } - - private record RecipeImpl( - FinishedRecipe recipe, RecipeSerializer serializer, List> extend - ) implements FinishedRecipe { - @Override - public void serializeRecipeData(JsonObject jsonObject) { - recipe.serializeRecipeData(jsonObject); - for (var extender : extend) extender.accept(jsonObject); - } - - @Override - public ResourceLocation getId() { - return recipe.getId(); - } - - @Override - public RecipeSerializer getType() { - return serializer; - } - - @Nullable - @Override - public JsonObject serializeAdvancement() { - return recipe.serializeAdvancement(); - } - - @Nullable - @Override - public ResourceLocation getAdvancementId() { - return recipe.getAdvancementId(); - } - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java b/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java index 9b207e09d..a43ae58e1 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java @@ -5,8 +5,9 @@ package dan200.computercraft.data; import dan200.computercraft.api.ComputerCraftTags; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.platform.RegistryWrappers; +import net.minecraft.core.Registry; import net.minecraft.data.tags.ItemTagsProvider; import net.minecraft.data.tags.TagsProvider; import net.minecraft.tags.BlockTags; @@ -111,9 +112,9 @@ class TagProvider { TagAppender tag(TagKey tag); } - public record TagAppender(RegistryWrappers.RegistryWrapper registry, TagBuilder builder) { + public record TagAppender(Registry registry, TagBuilder builder) { public TagAppender add(T object) { - builder.addElement(registry.getKey(object)); + builder.addElement(RegistryHelper.getKeyOrThrow(registry, object)); return this; } diff --git a/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java index 535b901f7..fac1cb72f 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java @@ -6,8 +6,9 @@ package dan200.computercraft.data; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftTags.Blocks; +import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; @@ -22,7 +23,7 @@ class TurtleUpgradeProvider extends TurtleUpgradeDataProvider { } @Override - protected void addUpgrades(Consumer>> addUpgrade) { + protected void addUpgrades(Consumer>> addUpgrade) { simpleWithCustomItem(id("speaker"), TurtleSerialisers.SPEAKER.get(), Items.SPEAKER.get()).add(addUpgrade); simpleWithCustomItem(vanilla("crafting_table"), TurtleSerialisers.WORKBENCH.get(), net.minecraft.world.item.Items.CRAFTING_TABLE).add(addUpgrade); simpleWithCustomItem(id("wireless_modem_normal"), TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade); diff --git a/projects/common/src/main/java/dan200/computercraft/data/recipe/AbstractRecipeBuilder.java b/projects/common/src/main/java/dan200/computercraft/data/recipe/AbstractRecipeBuilder.java new file mode 100644 index 000000000..fb9492df3 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/data/recipe/AbstractRecipeBuilder.java @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.data.recipe; + +import com.mojang.serialization.DataResult; +import dan200.computercraft.shared.recipe.RecipeProperties; +import net.minecraft.Util; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; +import net.minecraft.data.recipes.RecipeBuilder; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * An abstract base class for creating recipes, in the style of {@link RecipeBuilder}. + * + * @param The type of this class. + * @param The output of this builder. + * @see ShapelessSpecBuilder + */ +public abstract class AbstractRecipeBuilder, O> { + private final RecipeCategory category; + protected final ItemStack result; + private String group = ""; + private final Map> criteria = new LinkedHashMap<>(); + + protected AbstractRecipeBuilder(RecipeCategory category, ItemStack result) { + this.category = category; + this.result = result; + } + + /** + * Set the group for this recipe. + * + * @param group The new group. + * @return This object, for chaining. + */ + public final S group(String group) { + this.group = group; + return self(); + } + + /** + * Add a criterion to this recipe. + * + * @param name The name of the criterion. + * @param criterion The criterion to add. + * @return This object, for chaining. + */ + public final S unlockedBy(String name, Criterion criterion) { + criteria.put(name, criterion); + return self(); + } + + /** + * Convert this builder into the output ({@link O}) object. + * + * @param properties The properties for this recipe. + * @return The built object. + */ + protected abstract O build(RecipeProperties properties); + + /** + * Convert this builder into a concrete recipe. + * + * @param factory The recipe's constructor. + * @return The "built" recipe. + */ + public final FinishedRecipe build(Function> factory) { + var properties = new RecipeProperties(group, RecipeBuilder.determineBookCategory(category), true); + return new FinishedRecipe(factory.apply(build(properties)), result.getItem(), category, criteria); + } + + /** + * Convert this builder into a concrete recipe. + * + * @param factory The recipe's constructor. + * @return The "built" recipe. + */ + public final FinishedRecipe buildOrThrow(Function>> factory) { + return build(s -> Util.getOrThrow(factory.apply(s), IllegalStateException::new)); + } + + @SuppressWarnings("unchecked") + private S self() { + return (S) this; + } + + public static final class FinishedRecipe { + private final Recipe recipe; + private final Item result; + private final RecipeCategory category; + private final Map> criteria; + + private FinishedRecipe(Recipe recipe, Item result, RecipeCategory category, Map> criteria) { + this.recipe = recipe; + this.result = result; + this.category = category; + this.criteria = criteria; + } + + public void save(RecipeOutput output, ResourceLocation id) { + if (criteria.isEmpty()) throw new IllegalStateException("No way of obtaining recipe " + id); + var advancement = output.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(id)) + .rewards(AdvancementRewards.Builder.recipe(id)) + .requirements(AdvancementRequirements.Strategy.OR); + for (var entry : criteria.entrySet()) advancement.addCriterion(entry.getKey(), entry.getValue()); + + output.accept(id, recipe, advancement.build(id.withPrefix("recipes/" + category.getFolderName() + "/"))); + } + + public void save(RecipeOutput output) { + save(output, RecipeBuilder.getDefaultRecipeId(result)); + } + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/data/recipe/ShapedSpecBuilder.java b/projects/common/src/main/java/dan200/computercraft/data/recipe/ShapedSpecBuilder.java new file mode 100644 index 000000000..fbb2e97f5 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/data/recipe/ShapedSpecBuilder.java @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.data.recipe; + +import dan200.computercraft.shared.recipe.RecipeProperties; +import dan200.computercraft.shared.recipe.ShapedRecipeSpec; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.ShapedRecipeBuilder; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.ShapedRecipePattern; +import net.minecraft.world.level.ItemLike; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * A builder for {@link ShapedRecipeSpec}s, much like {@link ShapedRecipeBuilder}. + */ +public final class ShapedSpecBuilder extends AbstractRecipeBuilder { + private final List rows = new ArrayList<>(); + private final Map key = new LinkedHashMap<>(); + + private ShapedSpecBuilder(RecipeCategory category, ItemStack result) { + super(category, result); + } + + public static ShapedSpecBuilder shaped(RecipeCategory category, ItemStack result) { + return new ShapedSpecBuilder(category, result); + } + + public static ShapedSpecBuilder shaped(RecipeCategory category, ItemLike result) { + return new ShapedSpecBuilder(category, new ItemStack(result)); + } + + public ShapedSpecBuilder define(char key, Ingredient ingredient) { + if (this.key.containsKey(key)) throw new IllegalArgumentException("Symbol '" + key + "' is already defined!"); + if (key == ' ') throw new IllegalArgumentException("Symbol ' ' (whitespace) is reserved and cannot be defined"); + + this.key.put(key, ingredient); + return this; + } + + public ShapedSpecBuilder define(char key, TagKey tag) { + return this.define(key, Ingredient.of(tag)); + } + + public ShapedSpecBuilder define(char key, ItemLike item) { + return this.define(key, Ingredient.of(item)); + } + + public ShapedSpecBuilder pattern(String pattern) { + if (!this.rows.isEmpty() && pattern.length() != this.rows.get(0).length()) { + throw new IllegalArgumentException("Pattern must be the same width on every line!"); + } else { + this.rows.add(pattern); + return this; + } + } + + @Override + protected ShapedRecipeSpec build(RecipeProperties properties) { + return new ShapedRecipeSpec(properties, ShapedRecipePattern.of(key, rows), result); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/data/recipe/ShapelessSpecBuilder.java b/projects/common/src/main/java/dan200/computercraft/data/recipe/ShapelessSpecBuilder.java new file mode 100644 index 000000000..ae103e9dd --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/data/recipe/ShapelessSpecBuilder.java @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.data.recipe; + +import dan200.computercraft.shared.recipe.RecipeProperties; +import dan200.computercraft.shared.recipe.ShapelessRecipeSpec; +import net.minecraft.core.NonNullList; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.ShapelessRecipeBuilder; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; + +/** + * A builder for {@link ShapelessRecipeSpec}s, much like {@link ShapelessRecipeBuilder}. + */ +public final class ShapelessSpecBuilder extends AbstractRecipeBuilder { + private final NonNullList ingredients = NonNullList.create(); + + private ShapelessSpecBuilder(RecipeCategory category, ItemStack result) { + super(category, result); + } + + public static ShapelessSpecBuilder shapeless(RecipeCategory category, ItemStack result) { + return new ShapelessSpecBuilder(category, result); + } + + public static ShapelessSpecBuilder shapeless(RecipeCategory category, ItemLike result) { + return new ShapelessSpecBuilder(category, new ItemStack(result)); + } + + public ShapelessSpecBuilder requires(Ingredient ingredient, int count) { + for (int i = 0; i < count; i++) ingredients.add(ingredient); + return this; + } + + public ShapelessSpecBuilder requires(Ingredient ingredient) { + return requires(ingredient, 1); + } + + public ShapelessSpecBuilder requires(ItemLike item) { + return requires(Ingredient.of(new ItemStack(item))); + } + + public ShapelessSpecBuilder requires(ItemLike item, int count) { + return requires(Ingredient.of(new ItemStack(item)), count); + } + + public ShapelessSpecBuilder requires(TagKey item) { + return requires(Ingredient.of(item)); + } + + @Override + protected ShapelessRecipeSpec build(RecipeProperties properties) { + return new ShapelessRecipeSpec(properties, ingredients, result); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java b/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java index dc4ca4f7a..42e8a0f7b 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java @@ -15,10 +15,11 @@ import dan200.computercraft.api.media.MediaProvider; import dan200.computercraft.api.network.PacketNetwork; import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredNode; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.BundledRedstoneProvider; +import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleRefuelHandler; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.core.filesystem.WritableFileMount; import dan200.computercraft.impl.detail.DetailRegistryImpl; import dan200.computercraft.impl.network.wired.WiredNodeImpl; @@ -44,8 +45,8 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic private final DetailRegistry itemStackDetails = new DetailRegistryImpl<>(ItemDetails::fillBasic); private final DetailRegistry blockDetails = new DetailRegistryImpl<>(BlockDetails::fillBasic); - protected static final ResourceKey>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser")); - protected static final ResourceKey>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser")); + protected static final ResourceKey>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser")); + protected static final ResourceKey>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser")); public static @Nullable InputStream getResourceFile(MinecraftServer server, String domain, String subPath) { var manager = server.getResourceManager(); @@ -116,12 +117,12 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic } @Override - public final ResourceKey>> turtleUpgradeRegistryId() { + public final ResourceKey>> turtleUpgradeRegistryId() { return turtleUpgradeRegistryId; } @Override - public final ResourceKey>> pocketUpgradeRegistryId() { + public final ResourceKey>> pocketUpgradeRegistryId() { return pocketUpgradeRegistryId; } diff --git a/projects/fabric/src/main/java/dan200/computercraft/impl/Peripherals.java b/projects/common/src/main/java/dan200/computercraft/impl/Peripherals.java similarity index 70% rename from projects/fabric/src/main/java/dan200/computercraft/impl/Peripherals.java rename to projects/common/src/main/java/dan200/computercraft/impl/Peripherals.java index ed3f2a6aa..d06efa538 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/impl/Peripherals.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/Peripherals.java @@ -16,20 +16,18 @@ import javax.annotation.Nullable; /** * The registry for peripheral providers. - *

- * This lives in the {@code impl} package despite it not being part of the public API, in order to mirror Forge's class. */ public final class Peripherals { - private static final GenericPeripheralProvider genericProvider = new GenericPeripheralProvider<>(); + private static final GenericPeripheralProvider genericProvider = new GenericPeripheralProvider(); private Peripherals() { } - public static void addGenericLookup(ComponentLookup lookup) { + public static void addGenericLookup(ComponentLookup lookup) { genericProvider.registerLookup(lookup); } - public static @Nullable IPeripheral getGenericPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, Runnable invalidate) { - return genericProvider.getPeripheral(level, pos, side, blockEntity, invalidate); + public static @Nullable IPeripheral getGenericPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) { + return genericProvider.getPeripheral(level, pos, side, blockEntity); } } diff --git a/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java b/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java index feaf30182..93030adc9 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java @@ -6,19 +6,18 @@ package dan200.computercraft.impl; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; import java.util.stream.Stream; public final class PocketUpgrades { - private static final UpgradeManager, IPocketUpgrade> registry = new UpgradeManager<>( - "pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId() + private static final UpgradeManager registry = new UpgradeManager<>( + "pocket computer upgrade", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey() ); private PocketUpgrades() { } - public static UpgradeManager, IPocketUpgrade> instance() { + public static UpgradeManager instance() { return registry; } diff --git a/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java b/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java index 78c91f623..dafdc2089 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java @@ -6,19 +6,18 @@ package dan200.computercraft.impl; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import java.util.stream.Stream; public final class TurtleUpgrades { - private static final UpgradeManager, ITurtleUpgrade> registry = new UpgradeManager<>( - "turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId() + private static final UpgradeManager registry = new UpgradeManager<>( + "turtle upgrade", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey() ); private TurtleUpgrades() { } - public static UpgradeManager, ITurtleUpgrade> instance() { + public static UpgradeManager instance() { return registry; } diff --git a/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java b/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java index 3b3598c82..2a6561bb6 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java @@ -31,27 +31,26 @@ import java.util.stream.Collectors; /** * Manages turtle and pocket computer upgrades. * - * @param The type of upgrade serialisers. * @param The type of upgrade. * @see TurtleUpgrades * @see PocketUpgrades */ -public class UpgradeManager, T extends UpgradeBase> extends SimpleJsonResourceReloadListener { +public class UpgradeManager extends SimpleJsonResourceReloadListener { private static final Logger LOG = LoggerFactory.getLogger(UpgradeManager.class); private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - public record UpgradeWrapper, T extends UpgradeBase>( - String id, T upgrade, R serialiser, String modId + public record UpgradeWrapper( + String id, T upgrade, UpgradeSerialiser serialiser, String modId ) { } private final String kind; - private final ResourceKey> registry; + private final ResourceKey>> registry; - private Map> current = Map.of(); - private Map> currentWrappers = Map.of(); + private Map> current = Map.of(); + private Map> currentWrappers = Map.of(); - public UpgradeManager(String kind, String path, ResourceKey> registry) { + public UpgradeManager(String kind, String path, ResourceKey>> registry) { super(GSON, path); this.kind = kind; this.registry = registry; @@ -64,7 +63,7 @@ public class UpgradeManager, T extends } @Nullable - public UpgradeWrapper getWrapper(T upgrade) { + public UpgradeWrapper getWrapper(T upgrade) { return currentWrappers.get(upgrade); } @@ -92,16 +91,17 @@ public class UpgradeManager, T extends return currentWrappers.keySet(); } - public Map> getUpgradeWrappers() { + public Map> getUpgradeWrappers() { return current; } @Override protected void apply(Map upgrades, ResourceManager manager, ProfilerFiller profiler) { - Map> newUpgrades = new HashMap<>(); + var registry = RegistryHelper.getRegistry(this.registry); + Map> newUpgrades = new HashMap<>(); for (var element : upgrades.entrySet()) { try { - loadUpgrade(newUpgrades, element.getKey(), element.getValue()); + loadUpgrade(registry, newUpgrades, element.getKey(), element.getValue()); } catch (IllegalArgumentException | JsonParseException e) { LOG.error("Error loading {} {} from JSON file", kind, element.getKey(), e); } @@ -112,12 +112,12 @@ public class UpgradeManager, T extends LOG.info("Loaded {} {}s", current.size(), kind); } - private void loadUpgrade(Map> current, ResourceLocation id, JsonElement json) { + private void loadUpgrade(Registry> registry, Map> current, ResourceLocation id, JsonElement json) { var root = GsonHelper.convertToJsonObject(json, "top element"); if (!PlatformHelper.get().shouldLoadResource(root)) return; var serialiserId = new ResourceLocation(GsonHelper.getAsString(root, "type")); - var serialiser = PlatformHelper.get().tryGetRegistryObject(registry, serialiserId); + var serialiser = registry.get(serialiserId); if (serialiser == null) throw new JsonSyntaxException("Unknown upgrade type '" + serialiserId + "'"); // TODO: Can we track which mod this resource came from and use that instead? It's theoretically possible, @@ -130,11 +130,11 @@ public class UpgradeManager, T extends throw new IllegalArgumentException("Upgrade " + id + " from " + serialiser + " was incorrectly given id " + upgrade.getUpgradeID()); } - var result = new UpgradeWrapper(id.toString(), upgrade, serialiser, modId); + var result = new UpgradeWrapper(id.toString(), upgrade, serialiser, modId); current.put(result.id(), result); } - public void loadFromNetwork(Map> newUpgrades) { + public void loadFromNetwork(Map> newUpgrades) { current = Collections.unmodifiableMap(newUpgrades); currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x)); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java b/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java index 33b214ef8..8e1e96c59 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java @@ -63,6 +63,12 @@ public final class CommonHooks { ComputerMBean.start(server); } + public static void onServerStarted(MinecraftServer server) { + // ItemDetails requires creative tabs to be populated, however by default this is done lazily on the client and + // not at all on the server! We instead do this once on server startup. + CreativeModeTabs.tryRebuildTabContents(server.getWorldData().enabledFeatures(), false, server.registryAccess()); + } + public static void onServerStopped() { resetState(); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java index fab124915..3ca2e73e0 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java @@ -5,13 +5,15 @@ package dan200.computercraft.shared; import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.serialization.Codec; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.detail.DetailProvider; import dan200.computercraft.api.detail.VanillaDetailRegistries; import dan200.computercraft.api.media.IMedia; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.upgrades.UpgradeData; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.core.util.Colour; import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.impl.TurtleUpgrades; @@ -36,7 +38,6 @@ 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; import dan200.computercraft.shared.data.PlayerCreativeLootCondition; import dan200.computercraft.shared.details.BlockDetails; @@ -263,29 +264,29 @@ public final class ModRegistry { } public static class TurtleSerialisers { - static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(TurtleUpgradeSerialiser.registryId()); + static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(ITurtleUpgrade.serialiserRegistryKey()); - public static final RegistryEntry> SPEAKER = - REGISTRY.register("speaker", () -> TurtleUpgradeSerialiser.simpleWithCustomItem(TurtleSpeaker::new)); - public static final RegistryEntry> WORKBENCH = - REGISTRY.register("workbench", () -> TurtleUpgradeSerialiser.simpleWithCustomItem(TurtleCraftingTable::new)); - public static final RegistryEntry> WIRELESS_MODEM_NORMAL = - REGISTRY.register("wireless_modem_normal", () -> TurtleUpgradeSerialiser.simpleWithCustomItem((id, item) -> new TurtleModem(id, item, false))); - public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = - REGISTRY.register("wireless_modem_advanced", () -> TurtleUpgradeSerialiser.simpleWithCustomItem((id, item) -> new TurtleModem(id, item, true))); + public static final RegistryEntry> SPEAKER = + REGISTRY.register("speaker", () -> UpgradeSerialiser.simpleWithCustomItem(TurtleSpeaker::new)); + public static final RegistryEntry> WORKBENCH = + REGISTRY.register("workbench", () -> UpgradeSerialiser.simpleWithCustomItem(TurtleCraftingTable::new)); + public static final RegistryEntry> WIRELESS_MODEM_NORMAL = + REGISTRY.register("wireless_modem_normal", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new TurtleModem(id, item, false))); + public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = + REGISTRY.register("wireless_modem_advanced", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new TurtleModem(id, item, true))); - public static final RegistryEntry> TOOL = REGISTRY.register("tool", () -> TurtleToolSerialiser.INSTANCE); + public static final RegistryEntry> TOOL = REGISTRY.register("tool", () -> TurtleToolSerialiser.INSTANCE); } public static class PocketUpgradeSerialisers { - static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(PocketUpgradeSerialiser.registryId()); + static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(IPocketUpgrade.serialiserRegistryKey()); - public static final RegistryEntry> SPEAKER = - REGISTRY.register("speaker", () -> PocketUpgradeSerialiser.simpleWithCustomItem(PocketSpeaker::new)); - public static final RegistryEntry> WIRELESS_MODEM_NORMAL = - REGISTRY.register("wireless_modem_normal", () -> PocketUpgradeSerialiser.simpleWithCustomItem((id, item) -> new PocketModem(id, item, false))); - public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = - REGISTRY.register("wireless_modem_advanced", () -> PocketUpgradeSerialiser.simpleWithCustomItem((id, item) -> new PocketModem(id, item, true))); + public static final RegistryEntry> SPEAKER = + REGISTRY.register("speaker", () -> UpgradeSerialiser.simpleWithCustomItem(PocketSpeaker::new)); + public static final RegistryEntry> WIRELESS_MODEM_NORMAL = + REGISTRY.register("wireless_modem_normal", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new PocketModem(id, item, false))); + public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = + REGISTRY.register("wireless_modem_advanced", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new PocketModem(id, item, true))); } public static class Menus { @@ -347,13 +348,13 @@ public final class ModRegistry { static final RegistrationHelper REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.LOOT_CONDITION_TYPE); public static final RegistryEntry BLOCK_NAMED = REGISTRY.register("block_named", - () -> ConstantLootConditionSerializer.type(BlockNamedEntityLootCondition.INSTANCE)); + () -> new LootItemConditionType(Codec.unit(BlockNamedEntityLootCondition.INSTANCE))); public static final RegistryEntry PLAYER_CREATIVE = REGISTRY.register("player_creative", - () -> ConstantLootConditionSerializer.type(PlayerCreativeLootCondition.INSTANCE)); + () -> new LootItemConditionType(Codec.unit(PlayerCreativeLootCondition.INSTANCE))); public static final RegistryEntry HAS_ID = REGISTRY.register("has_id", - () -> ConstantLootConditionSerializer.type(HasComputerIdLootCondition.INSTANCE)); + () -> new LootItemConditionType(Codec.unit(HasComputerIdLootCondition.INSTANCE))); } public static class RecipeSerializers { @@ -466,8 +467,8 @@ public final class ModRegistry { * Register any objects which must be done on the main thread. */ public static void registerMainThread() { - CauldronInteraction.WATER.put(Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION); - CauldronInteraction.WATER.put(Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION); + CauldronInteraction.WATER.map().put(Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION); + CauldronInteraction.WATER.map().put(Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION); } private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentUtils.java b/projects/common/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentUtils.java index 2bd97ad28..5eb7ca110 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentUtils.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentUtils.java @@ -8,8 +8,9 @@ import com.google.gson.JsonObject; import com.mojang.brigadier.Message; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.impl.RegistryHelper; import net.minecraft.commands.synchronization.ArgumentTypeInfo; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; @@ -24,7 +25,7 @@ public class ArgumentUtils { public static > JsonObject serializeToJson(ArgumentTypeInfo.Template template) { var object = new JsonObject(); object.addProperty("type", "argument"); - object.addProperty("parser", RegistryWrappers.COMMAND_ARGUMENT_TYPES.getKey(template.type()).toString()); + object.addProperty("parser", RegistryHelper.getKeyOrThrow(BuiltInRegistries.COMMAND_ARGUMENT_TYPE, template.type()).toString()); var properties = new JsonObject(); serializeToJson(properties, template.type(), template); @@ -44,12 +45,12 @@ public class ArgumentUtils { @SuppressWarnings("unchecked") private static , T extends ArgumentTypeInfo.Template> void serializeToNetwork(FriendlyByteBuf buffer, ArgumentTypeInfo type, ArgumentTypeInfo.Template template) { - buffer.writeId(RegistryWrappers.COMMAND_ARGUMENT_TYPES, type); + buffer.writeId(BuiltInRegistries.COMMAND_ARGUMENT_TYPE, type); type.serializeToNetwork((T) template, buffer); } public static ArgumentTypeInfo.Template deserialize(FriendlyByteBuf buffer) { - var type = buffer.readById(RegistryWrappers.COMMAND_ARGUMENT_TYPES); + var type = buffer.readById(BuiltInRegistries.COMMAND_ARGUMENT_TYPE); Objects.requireNonNull(type, "Unknown argument type"); return type.deserializeFromNetwork(buffer); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java index cfcec37bc..956c409c4 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java @@ -7,7 +7,6 @@ package dan200.computercraft.shared.common; import dan200.computercraft.shared.ModRegistry; import net.minecraft.core.NonNullList; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -20,8 +19,8 @@ import net.minecraft.world.level.Level; * Craft a wet sponge with a {@linkplain IColouredItem dyable item} to remove its dye. */ public final class ClearColourRecipe extends CustomRecipe { - public ClearColourRecipe(ResourceLocation id, CraftingBookCategory category) { - super(id, category); + public ClearColourRecipe(CraftingBookCategory category) { + super(category); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java index 3fc926ed2..ab8d9f1f7 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java @@ -8,7 +8,6 @@ import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.util.ColourTracker; import dan200.computercraft.shared.util.ColourUtils; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingBookCategory; @@ -17,8 +16,8 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.Level; public final class ColourableRecipe extends CustomRecipe { - public ColourableRecipe(ResourceLocation id, CraftingBookCategory category) { - super(id, category); + public ColourableRecipe(CraftingBookCategory category) { + super(category); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index 3fcb162c2..5baab1df5 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -56,14 +56,22 @@ public class CommandAPI implements ILuaAPI { var receiver = computer.getReceiver(); try { receiver.clearOutput(); - var result = commandManager.performPrefixedCommand(computer.getSource(), command); - return new Object[]{ result > 0, receiver.copyOutput(), result }; + var state = new CommandState(); + var source = computer.getSource().withCallback((success, x) -> { + if (success) state.successes++; + }); + commandManager.performPrefixedCommand(source, command); + return new Object[]{ state.successes > 0, receiver.copyOutput(), state.successes }; } catch (Throwable t) { LOG.error(Logging.JAVA_ERROR, "Error running command.", t); return new Object[]{ false, createOutput("Java Exception Thrown: " + t) }; } } + private static final class CommandState { + int successes; + } + private static Map getBlockInfo(Level world, BlockPos pos) { // Get the details of the block var block = new BlockReference(world, pos); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java index 2bd6f00f4..15ea4e26f 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java @@ -99,7 +99,7 @@ public abstract class AbstractComputerBlock extends ComputerBlock implements GameMasterBlock { + private static final MapCodec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockCodecs.propertiesCodec(), + BlockCodecs.blockEntityCodec(x -> x.type) + ).apply(instance, CommandComputerBlock::new)); + public CommandComputerBlock(Properties settings, RegistryEntry> type) { super(settings, type); } + + @Override + protected MapCodec> codec() { + return CODEC; + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlock.java index 44a3e85f4..6d594c6b3 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlock.java @@ -4,9 +4,12 @@ package dan200.computercraft.shared.computer.blocks; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.items.ComputerItem; import dan200.computercraft.shared.platform.RegistryEntry; +import dan200.computercraft.shared.util.BlockCodecs; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; @@ -21,6 +24,11 @@ import net.minecraft.world.level.block.state.properties.EnumProperty; import javax.annotation.Nullable; public class ComputerBlock extends AbstractComputerBlock { + private static final MapCodec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockCodecs.propertiesCodec(), + BlockCodecs.blockEntityCodec(x -> x.type) + ).apply(instance, ComputerBlock::new)); + public static final EnumProperty STATE = EnumProperty.create("state", ComputerState.class); public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; @@ -37,6 +45,11 @@ public class ComputerBlock extends AbstractComput builder.add(FACING, STATE); } + @Override + protected MapCodec> codec() { + return CODEC; + } + @Nullable @Override public BlockState getStateForPlacement(BlockPlaceContext placement) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java index ab8fa2a20..e2cc16aec 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java @@ -8,7 +8,6 @@ import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.recipe.CustomShapedRecipe; import dan200.computercraft.shared.recipe.ShapedRecipeSpec; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; @@ -17,8 +16,8 @@ import net.minecraft.world.level.Level; * A recipe which converts a computer from one form into another. */ public abstract class ComputerConvertRecipe extends CustomShapedRecipe { - public ComputerConvertRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) { - super(identifier, recipe); + public ComputerConvertRecipe(ShapedRecipeSpec recipe) { + super(recipe); } protected abstract ItemStack convert(IComputerItem item, ItemStack stack); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java index cec37135e..4ae1a1f32 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java @@ -8,7 +8,6 @@ import com.mojang.serialization.DataResult; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.recipe.ShapedRecipeSpec; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeSerializer; @@ -22,17 +21,17 @@ import net.minecraft.world.item.crafting.RecipeSerializer; public final class ComputerUpgradeRecipe extends ComputerConvertRecipe { private final Item result; - private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) { - super(identifier, recipe); + public ComputerUpgradeRecipe(ShapedRecipeSpec recipe) { + super(recipe); this.result = recipe.result().getItem(); } - public static DataResult of(ResourceLocation id, ShapedRecipeSpec recipe) { + public static DataResult of(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)); + return DataResult.success(new ComputerUpgradeRecipe(recipe)); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/data/ConstantLootConditionSerializer.java b/projects/common/src/main/java/dan200/computercraft/shared/data/ConstantLootConditionSerializer.java deleted file mode 100644 index 75e6b3f3a..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/data/ConstantLootConditionSerializer.java +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.data; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.google.gson.JsonSerializationContext; -import net.minecraft.world.level.storage.loot.Serializer; -import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; -import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; - -public final class ConstantLootConditionSerializer implements Serializer { - private final T instance; - - public ConstantLootConditionSerializer(T instance) { - this.instance = instance; - } - - public static LootItemConditionType type(T condition) { - return new LootItemConditionType(new ConstantLootConditionSerializer<>(condition)); - } - - @Override - public void serialize(JsonObject json, T object, JsonSerializationContext context) { - } - - @Override - public T deserialize(JsonObject json, JsonDeserializationContext context) { - return instance; - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/details/BlockDetails.java b/projects/common/src/main/java/dan200/computercraft/shared/details/BlockDetails.java index b32ea9ecb..0940234c7 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/details/BlockDetails.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/details/BlockDetails.java @@ -5,7 +5,7 @@ package dan200.computercraft.shared.details; import dan200.computercraft.api.detail.BlockReference; -import dan200.computercraft.shared.platform.RegistryWrappers; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.state.properties.Property; import java.util.HashMap; @@ -15,7 +15,7 @@ public class BlockDetails { public static void fillBasic(Map data, BlockReference block) { var state = block.state(); - data.put("name", DetailHelpers.getId(RegistryWrappers.BLOCKS, state.getBlock())); + data.put("name", DetailHelpers.getId(BuiltInRegistries.BLOCK, state.getBlock())); Map stateTable = new HashMap<>(); for (Map.Entry, ? extends Comparable> entry : state.getValues().entrySet()) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/details/DetailHelpers.java b/projects/common/src/main/java/dan200/computercraft/shared/details/DetailHelpers.java index e39385ee8..1fc3f28c1 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/details/DetailHelpers.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/details/DetailHelpers.java @@ -4,8 +4,9 @@ package dan200.computercraft.shared.details; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.impl.RegistryHelper; import net.minecraft.core.Holder; +import net.minecraft.core.Registry; import net.minecraft.tags.TagKey; import java.util.Map; @@ -24,7 +25,7 @@ public final class DetailHelpers { return tags.collect(Collectors.toMap(x -> x.location().toString(), x -> true)); } - public static String getId(RegistryWrappers.RegistryWrapper registry, T entry) { - return registry.getKey(entry).toString(); + public static String getId(Registry registry, T entry) { + return RegistryHelper.getKeyOrThrow(registry, entry).toString(); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java b/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java index 650ad3bb9..ca5b1ad2f 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java @@ -5,11 +5,13 @@ package dan200.computercraft.shared.details; import com.google.gson.JsonParseException; -import dan200.computercraft.shared.platform.RegistryWrappers; import dan200.computercraft.shared.util.NBTUtil; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.EnchantedBookItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.EnchantmentHelper; @@ -22,7 +24,7 @@ import java.util.*; */ public class ItemDetails { public static void fillBasic(Map data, ItemStack stack) { - data.put("name", DetailHelpers.getId(RegistryWrappers.ITEMS, stack.getItem())); + data.put("name", DetailHelpers.getId(BuiltInRegistries.ITEM, stack.getItem())); data.put("count", stack.getCount()); var hash = NBTUtil.getNBTHash(stack.getTag()); if (hash != null) data.put("nbt", hash); @@ -42,9 +44,7 @@ public class ItemDetails { } data.put("tags", DetailHelpers.getTags(stack.getTags())); - - // Include deprecated itemGroups field - data.put("itemGroups", List.of()); + data.put("itemGroups", getItemGroups(stack)); var tag = stack.getTag(); if (tag != null && tag.contains("display", Tag.TAG_COMPOUND)) { @@ -83,6 +83,27 @@ public class ItemDetails { } } + /** + * Retrieve all item groups an item stack pertains to. + * + * @param stack Stack to analyse + * @return A filled list that contains pairs of item group IDs and their display names. + */ + private static List> getItemGroups(ItemStack stack) { + return CreativeModeTabs.allTabs().stream() + .filter(x -> x.shouldDisplay() && x.getType() == CreativeModeTab.Type.CATEGORY && x.contains(stack)) + .map(group -> { + Map groupData = new HashMap<>(2); + + var id = BuiltInRegistries.CREATIVE_MODE_TAB.getKey(group); + if (id != null) groupData.put("id", id.toString()); + + groupData.put("displayName", group.getDisplayName().getString()); + return groupData; + }) + .toList(); + } + /** * Retrieve all visible enchantments from given stack. Try to follow all tooltip rules : order and visibility. * @@ -126,7 +147,7 @@ public class ItemDetails { var enchantment = entry.getKey(); var level = entry.getValue(); var enchant = new HashMap(3); - enchant.put("name", DetailHelpers.getId(RegistryWrappers.ENCHANTMENTS, enchantment)); + enchant.put("name", DetailHelpers.getId(BuiltInRegistries.ENCHANTMENT, enchantment)); enchant.put("level", level); enchant.put("displayName", enchantment.getFullname(level).getString()); enchants.add(enchant); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java index 95a7700d9..925e5e344 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java @@ -4,7 +4,6 @@ package dan200.computercraft.shared.integration; -import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; @@ -15,13 +14,12 @@ import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.turtle.items.TurtleItem; import net.minecraft.core.NonNullList; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingBookCategory; -import net.minecraft.world.item.crafting.CraftingRecipe; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.ShapedRecipe; +import net.minecraft.world.item.crafting.ShapedRecipePattern; import javax.annotation.Nullable; import java.util.*; @@ -38,17 +36,14 @@ import static dan200.computercraft.shared.integration.RecipeModHelpers.TURTLES; * @see RecipeModHelpers */ public class UpgradeRecipeGenerator { - private static final ResourceLocation TURTLE_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade"); - private static final ResourceLocation POCKET_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"); - - private final Function wrap; + private final Function wrap; private final Map> upgradeItemLookup = new HashMap<>(); private final List pocketUpgrades = new ArrayList<>(); private final List turtleUpgrades = new ArrayList<>(); private boolean initialised = false; - public UpgradeRecipeGenerator(Function wrap) { + public UpgradeRecipeGenerator(Function wrap) { this.wrap = wrap; } @@ -235,11 +230,19 @@ public class UpgradeRecipeGenerator { } private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) { - return wrap.apply(new ShapedRecipe(POCKET_UPGRADE, "", CraftingBookCategory.MISC, 1, 2, NonNullList.of(Ingredient.EMPTY, upgrade, pocketComputer), result)); + return wrap.apply(new ShapedRecipe( + "", CraftingBookCategory.MISC, + new ShapedRecipePattern(1, 2, NonNullList.of(Ingredient.EMPTY, upgrade, pocketComputer), Optional.empty()), + result + )); } private T turtle(Ingredient left, Ingredient right, ItemStack result) { - return wrap.apply(new ShapedRecipe(TURTLE_UPGRADE, "", CraftingBookCategory.MISC, 2, 1, NonNullList.of(Ingredient.EMPTY, left, right), result)); + return wrap.apply(new ShapedRecipe( + "", CraftingBookCategory.MISC, + new ShapedRecipePattern(2, 1, NonNullList.of(Ingredient.EMPTY, left, right), Optional.empty()), + result + )); } private class UpgradeInfo { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java index 6893b7d1a..3642491a1 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java @@ -60,7 +60,7 @@ public class JEIComputerCraft implements IModPlugin { // Hide all upgrade recipes var category = registry.createRecipeLookup(RecipeTypes.CRAFTING); category.get().forEach(wrapper -> { - if (RecipeModHelpers.shouldRemoveRecipe(wrapper.getId())) { + if (RecipeModHelpers.shouldRemoveRecipe(wrapper.id())) { registry.hideRecipes(RecipeTypes.CRAFTING, List.of(wrapper)); } }); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java index fdaa75d85..eaf2f5981 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java @@ -11,7 +11,6 @@ import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.util.ColourTracker; import dan200.computercraft.shared.util.ColourUtils; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -24,8 +23,8 @@ import net.minecraft.world.level.Level; public class DiskRecipe extends CustomRecipe { private final Ingredient redstone; - public DiskRecipe(ResourceLocation id, CraftingBookCategory category) { - super(id, category); + public DiskRecipe(CraftingBookCategory category) { + super(category); redstone = PlatformHelper.get().getRecipeIngredients().redstone(); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java index 1f59b5e4f..11376cef2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java @@ -8,7 +8,6 @@ import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.media.items.PrintoutItem; import dan200.computercraft.shared.platform.PlatformHelper; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -22,8 +21,8 @@ public final class PrintoutRecipe extends CustomRecipe { private final Ingredient leather; private final Ingredient string; - public PrintoutRecipe(ResourceLocation id, CraftingBookCategory category) { - super(id, category); + public PrintoutRecipe(CraftingBookCategory category) { + super(category); var ingredients = PlatformHelper.get().getRecipeIngredients(); leather = ingredients.leather(); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/MessageType.java b/projects/common/src/main/java/dan200/computercraft/shared/network/MessageType.java index b0d436f2a..93f673850 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/MessageType.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/MessageType.java @@ -4,6 +4,9 @@ package dan200.computercraft.shared.network; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + /** * A type of message to send over the network. *

@@ -15,4 +18,11 @@ package dan200.computercraft.shared.network; * @see NetworkMessage#type() */ public interface MessageType> { + /** + * Get the id of this message type. This will be used as the custom packet channel name. + * + * @return The id of this message type. + * @see CustomPacketPayload#id() + */ + ResourceLocation id(); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java b/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java index bf4c1927c..75dedc2a3 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java @@ -8,8 +8,6 @@ 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; @@ -21,50 +19,48 @@ import java.util.*; * @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 seenChannel = new HashSet<>(); private static final List>> serverMessages = new ArrayList<>(); private static final List>> clientMessages = new ArrayList<>(); - public static final MessageType COMPUTER_ACTION = registerServerbound(0, "computer_action", ComputerActionServerMessage.class, ComputerActionServerMessage::new); - public static final MessageType QUEUE_EVENT = registerServerbound(1, "queue_event", QueueEventServerMessage.class, QueueEventServerMessage::new); - public static final MessageType KEY_EVENT = registerServerbound(2, "key_event", KeyEventServerMessage.class, KeyEventServerMessage::new); - public static final MessageType MOUSE_EVENT = registerServerbound(3, "mouse_event", MouseEventServerMessage.class, MouseEventServerMessage::new); - public static final MessageType UPLOAD_FILE = registerServerbound(4, "upload_file", UploadFileMessage.class, UploadFileMessage::new); + public static final MessageType COMPUTER_ACTION = registerServerbound("computer_action", ComputerActionServerMessage::new); + public static final MessageType QUEUE_EVENT = registerServerbound("queue_event", QueueEventServerMessage::new); + public static final MessageType KEY_EVENT = registerServerbound("key_event", KeyEventServerMessage::new); + public static final MessageType MOUSE_EVENT = registerServerbound("mouse_event", MouseEventServerMessage::new); + public static final MessageType UPLOAD_FILE = registerServerbound("upload_file", UploadFileMessage::new); - public static final MessageType CHAT_TABLE = registerClientbound(10, "chat_table", ChatTableClientMessage.class, ChatTableClientMessage::new); - public static final MessageType POCKET_COMPUTER_DATA = registerClientbound(11, "pocket_computer_data", PocketComputerDataMessage.class, PocketComputerDataMessage::new); - public static final MessageType POCKET_COMPUTER_DELETED = registerClientbound(12, "pocket_computer_deleted", PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new); - public static final MessageType COMPUTER_TERMINAL = registerClientbound(13, "computer_terminal", ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new); - public static final MessageType PLAY_RECORD = registerClientbound(14, "play_record", PlayRecordClientMessage.class, PlayRecordClientMessage::new); - public static final MessageType MONITOR_CLIENT = registerClientbound(15, "monitor_client", MonitorClientMessage.class, MonitorClientMessage::new); - public static final MessageType SPEAKER_AUDIO = registerClientbound(16, "speaker_audio", SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new); - public static final MessageType SPEAKER_MOVE = registerClientbound(17, "speaker_move", SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new); - public static final MessageType SPEAKER_PLAY = registerClientbound(18, "speaker_play", SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new); - public static final MessageType SPEAKER_STOP = registerClientbound(19, "speaker_stop", SpeakerStopClientMessage.class, SpeakerStopClientMessage::new); - public static final MessageType UPLOAD_RESULT = registerClientbound(20, "upload_result", UploadResultMessage.class, UploadResultMessage::new); - public static final MessageType UPGRADES_LOADED = registerClientbound(21, "upgrades_loaded", UpgradesLoadedMessage.class, UpgradesLoadedMessage::new); + public static final MessageType CHAT_TABLE = registerClientbound("chat_table", ChatTableClientMessage::new); + public static final MessageType POCKET_COMPUTER_DATA = registerClientbound("pocket_computer_data", PocketComputerDataMessage::new); + public static final MessageType POCKET_COMPUTER_DELETED = registerClientbound("pocket_computer_deleted", PocketComputerDeletedClientMessage::new); + public static final MessageType COMPUTER_TERMINAL = registerClientbound("computer_terminal", ComputerTerminalClientMessage::new); + public static final MessageType PLAY_RECORD = registerClientbound("play_record", PlayRecordClientMessage::new); + public static final MessageType MONITOR_CLIENT = registerClientbound("monitor_client", MonitorClientMessage::new); + public static final MessageType SPEAKER_AUDIO = registerClientbound("speaker_audio", SpeakerAudioClientMessage::new); + public static final MessageType SPEAKER_MOVE = registerClientbound("speaker_move", SpeakerMoveClientMessage::new); + public static final MessageType SPEAKER_PLAY = registerClientbound("speaker_play", SpeakerPlayClientMessage::new); + public static final MessageType SPEAKER_STOP = registerClientbound("speaker_stop", SpeakerStopClientMessage::new); + public static final MessageType UPLOAD_RESULT = registerClientbound("upload_result", UploadResultMessage::new); + public static final MessageType UPGRADES_LOADED = registerClientbound("upgrades_loaded", UpgradesLoadedMessage::new); private NetworkMessages() { } private static > MessageType register( List>> messages, - int id, String channel, Class klass, FriendlyByteBuf.Reader reader + String channel, FriendlyByteBuf.Reader 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); + var type = PlatformHelper.get().createMessageType(new ResourceLocation(ComputerCraftAPI.MOD_ID, channel), reader); messages.add(type); return type; } - private static > MessageType registerServerbound(int id, String channel, Class klass, FriendlyByteBuf.Reader reader) { - return register(serverMessages, id, channel, klass, reader); + private static > MessageType registerServerbound(String id, FriendlyByteBuf.Reader reader) { + return register(serverMessages, id, reader); } - private static > MessageType registerClientbound(int id, String channel, Class klass, FriendlyByteBuf.Reader reader) { - return register(clientMessages, id, channel, klass, reader); + private static > MessageType registerClientbound(String id, FriendlyByteBuf.Reader reader) { + return register(clientMessages, id, reader); } /** diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java b/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java index 98f9e2ff7..1b1d414ec 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java @@ -5,18 +5,16 @@ package dan200.computercraft.shared.network.client; import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.impl.PocketUpgrades; +import dan200.computercraft.impl.RegistryHelper; 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; import net.minecraft.resources.ResourceKey; @@ -24,14 +22,13 @@ import net.minecraft.resources.ResourceLocation; import java.util.HashMap; import java.util.Map; -import java.util.Objects; /** * Syncs turtle and pocket upgrades to the client. */ public final class UpgradesLoadedMessage implements NetworkMessage { - private final Map, ITurtleUpgrade>> turtleUpgrades; - private final Map, IPocketUpgrade>> pocketUpgrades; + private final Map> turtleUpgrades; + private final Map> pocketUpgrades; public UpgradesLoadedMessage() { turtleUpgrades = TurtleUpgrades.instance().getUpgradeWrappers(); @@ -39,28 +36,28 @@ public final class UpgradesLoadedMessage implements NetworkMessage, T extends UpgradeBase> Map> fromBytes( - FriendlyByteBuf buf, ResourceKey> registryKey + private Map> fromBytes( + FriendlyByteBuf buf, ResourceKey>> registryKey ) { - var registry = PlatformHelper.get().wrap(registryKey); + var registry = RegistryHelper.getRegistry(registryKey); var size = buf.readVarInt(); - Map> upgrades = new HashMap<>(size); + Map> upgrades = new HashMap<>(size); for (var i = 0; i < size; i++) { var id = buf.readUtf(); var serialiserId = buf.readResourceLocation(); - var serialiser = registry.tryGet(serialiserId); + var serialiser = registry.get(serialiserId); if (serialiser == null) throw new IllegalStateException("Unknown serialiser " + serialiserId); var upgrade = serialiser.fromNetwork(new ResourceLocation(id), buf); var modId = buf.readUtf(); - upgrades.put(id, new UpgradeManager.UpgradeWrapper(id, upgrade, serialiser, modId)); + upgrades.put(id, new UpgradeManager.UpgradeWrapper(id, upgrade, serialiser, modId)); } return upgrades; @@ -68,14 +65,14 @@ public final class UpgradesLoadedMessage implements NetworkMessage, T extends UpgradeBase> void toBytes( - FriendlyByteBuf buf, ResourceKey> registryKey, Map> upgrades + private void toBytes( + FriendlyByteBuf buf, ResourceKey>> registryKey, Map> upgrades ) { - var registry = PlatformHelper.get().wrap(registryKey); + var registry = RegistryHelper.getRegistry(registryKey); buf.writeVarInt(upgrades.size()); for (var entry : upgrades.entrySet()) { @@ -85,7 +82,7 @@ public final class UpgradesLoadedMessage implements NetworkMessage) serialiser; - buf.writeResourceLocation(Objects.requireNonNull(registry.getKey(serialiser), "Serialiser is not registered!")); + buf.writeResourceLocation(RegistryHelper.getKeyOrThrow(registry, serialiser)); unwrappedSerialiser.toNetwork(buf, entry.getValue().upgrade()); buf.writeUtf(entry.getValue().modId()); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlock.java index 2edca1700..276322f01 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlock.java @@ -4,6 +4,7 @@ package dan200.computercraft.shared.peripheral.diskdrive; +import com.mojang.serialization.MapCodec; import dan200.computercraft.impl.MediaProviders; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.HorizontalContainerBlock; @@ -26,6 +27,8 @@ import net.minecraft.world.phys.BlockHitResult; import javax.annotation.Nullable; public class DiskDriveBlock extends HorizontalContainerBlock { + private static final MapCodec CODEC = simpleCodec(DiskDriveBlock::new); + public static final EnumProperty STATE = EnumProperty.create("state", DiskDriveState.class); private static final BlockEntityTicker serverTicker = (level, pos, state, drive) -> drive.serverTick(); @@ -43,6 +46,11 @@ public class DiskDriveBlock extends HorizontalContainerBlock { properties.add(FACING, STATE); } + @Override + protected MapCodec codec() { + return CODEC; + } + @Override @Deprecated public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/ComponentLookup.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/ComponentLookup.java index 082391e14..774faf8d1 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/ComponentLookup.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/ComponentLookup.java @@ -15,10 +15,8 @@ import javax.annotation.Nullable; /** * Extract some component (for instance a capability on Forge, or a {@code BlockApiLookup} on Fabric) from a block and * block entity. - * - * @param A platform-specific type, used for the invalidation callback. */ -public interface ComponentLookup { +public interface ComponentLookup { /** * Extract some component from a block in the world. * @@ -28,9 +26,8 @@ public interface ComponentLookup { * @param blockEntity The block entity at that position. * @param side The side of the block to extract the component from. Implementations should try to use a * sideless lookup first, but may fall back to a sided lookup if needed. - * @param invalidate An invalidation function to call if this component changes. * @return The found component, or {@code null} if not present. */ @Nullable - Object find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, C invalidate); + Object find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java index 8874a12b9..338b9a91a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java @@ -11,8 +11,9 @@ import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IDynamicPeripheral; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.impl.RegistryHelper; import net.minecraft.core.Direction; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.entity.BlockEntity; import javax.annotation.Nullable; @@ -29,7 +30,7 @@ public final class GenericPeripheral implements IDynamicPeripheral { GenericPeripheral(BlockEntity tile, Direction side, @Nullable String name, Set additionalTypes, List methods) { this.side = side; - var type = RegistryWrappers.BLOCK_ENTITY_TYPES.getKey(tile.getType()); + var type = RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK_ENTITY_TYPE, tile.getType()); this.tile = tile; this.type = name != null ? name : type.toString(); this.additionalTypes = additionalTypes; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java index 09670ac20..1a9d92626 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java @@ -13,8 +13,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.entity.BlockEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.util.ArrayList; @@ -25,39 +23,35 @@ import java.util.Objects; * A peripheral provider which finds methods from various {@linkplain GenericSource generic sources}. *

* Methods are found using the original block entity itself and a registered list of {@link ComponentLookup}s. - * - * @param A platform-specific type, used for the invalidation callback. */ -public final class GenericPeripheralProvider { - private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class); - - private final List> lookups = new ArrayList<>(); +public final class GenericPeripheralProvider { + private final List lookups = new ArrayList<>(); /** * Register a component lookup function. * * @param lookup The component lookup function. */ - public synchronized void registerLookup(ComponentLookup lookup) { + public synchronized void registerLookup(ComponentLookup lookup) { Objects.requireNonNull(lookup); if (!lookups.contains(lookup)) lookups.add(lookup); } - public void forEachMethod(MethodSupplier methods, ServerLevel level, BlockPos pos, Direction side, BlockEntity blockEntity, C invalidate, MethodSupplier.TargetedConsumer consumer) { + public void forEachMethod(MethodSupplier methods, ServerLevel level, BlockPos pos, Direction side, BlockEntity blockEntity, MethodSupplier.TargetedConsumer consumer) { methods.forEachMethod(blockEntity, consumer); for (var lookup : lookups) { - var contents = lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, side, invalidate); + var contents = lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, side); if (contents != null) methods.forEachMethod(contents, consumer); } } @Nullable - public IPeripheral getPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, C invalidate) { + public IPeripheral getPeripheral(ServerLevel level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) { if (blockEntity == null) return null; var builder = new GenericPeripheralBuilder(); - forEachMethod(ServerContext.get(level.getServer()).peripheralMethods(), level, pos, side, blockEntity, invalidate, builder::addMethod); + forEachMethod(ServerContext.get(level.getServer()).peripheralMethods(), level, pos, side, blockEntity, builder::addMethod); return builder.toPeripheral(blockEntity, side); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/AbstractInventoryMethods.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/AbstractInventoryMethods.java index 1115a46a8..c83d4922f 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/AbstractInventoryMethods.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/AbstractInventoryMethods.java @@ -75,17 +75,14 @@ public abstract class AbstractInventoryMethods implements GenericPeripheral { *

* The returned information contains the same information as each item in * {@link #list}, as well as additional details like the display name - * (`displayName`), and item and item durability (`damage`, `maxDamage`, `durability`). + * (`displayName`), item groups (`itemGroups`), which are the creative tabs + * an item will appear under, and item and item durability (`damage`, + * `maxDamage`, `durability`). *

* Some items include more information (such as enchantments) - it is * recommended to print it out using [`textutils.serialize`] or in the Lua * REPL, to explore what is available. *

- * > [Deprecated fields][!INFO] - * > Older versions of CC: Tweaked exposed an {@code itemGroups} field, listing the - * > creative tabs an item was available under. This information is no longer available on - * > more recent versions of the game, and so this field will always be empty. Do not use this - * > field in new code! * * @param inventory The current inventory. * @param slot The slot to get information about. diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java index fe6a67453..06b874739 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java @@ -140,12 +140,12 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB @Override @Deprecated - public ItemStack getCloneItemStack(BlockGetter world, BlockPos pos, BlockState state) { + public ItemStack getCloneItemStack(LevelReader world, BlockPos pos, BlockState state) { return state.getValue(CABLE) ? new ItemStack(ModRegistry.Items.CABLE.get()) : new ItemStack(ModRegistry.Items.WIRED_MODEM.get()); } @ForgeOverride - public ItemStack getCloneItemStack(BlockState state, @Nullable HitResult hit, BlockGetter world, BlockPos pos, Player player) { + public ItemStack getCloneItemStack(BlockState state, @Nullable HitResult hit, LevelReader world, BlockPos pos, Player player) { var modem = state.getValue(MODEM).getFacing(); boolean cable = state.getValue(CABLE); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java index ef6257b52..dca279888 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java @@ -60,7 +60,6 @@ public class CableBlockEntity extends BlockEntity { private boolean invalidPeripheral; private boolean peripheralAccessAllowed; private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral(PlatformHelper.get().createPeripheralAccess(this, x -> queueRefreshPeripheral())); - private @Nullable Runnable modemChanged; private boolean connectionsFormed = false; private boolean connectionsChanged = false; @@ -121,7 +120,7 @@ public class CableBlockEntity extends BlockEntity { super.setBlockState(state); // We invalidate both the modem and element if the modem's direction is different. - if (getMaybeDirection() != direction && modemChanged != null) modemChanged.run(); + if (getMaybeDirection() != direction) PlatformHelper.get().invalidateComponent(this); } @Nullable @@ -273,7 +272,7 @@ public class CableBlockEntity extends BlockEntity { void modemChanged() { // Tell anyone who cares that the connection state has changed - if (modemChanged != null) modemChanged.run(); + PlatformHelper.get().invalidateComponent(this); if (getLevel().isClientSide) return; @@ -326,10 +325,6 @@ public class CableBlockEntity extends BlockEntity { return direction == null || getMaybeDirection() == direction ? modem : null; } - public void onModemChanged(Runnable callback) { - modemChanged = callback; - } - boolean hasCable() { return getBlockState().getValue(CableBlock.CABLE); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java index 50a5d9cea..27ea33c6c 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java @@ -4,10 +4,11 @@ package dan200.computercraft.shared.peripheral.modem.wired; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.platform.RegistryWrappers; import net.minecraft.Util; import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.sounds.SoundSource; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.BlockItem; @@ -50,7 +51,7 @@ public abstract class CableBlockItem extends BlockItem { @Override public String getDescriptionId() { if (translationKey == null) { - translationKey = Util.makeDescriptionId("block", RegistryWrappers.ITEMS.getKey(this)); + translationKey = Util.makeDescriptionId("block", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, this)); } return translationKey; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlock.java index c2cb274ed..aa5cdb57a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlock.java @@ -4,8 +4,10 @@ package dan200.computercraft.shared.peripheral.modem.wireless; +import com.mojang.serialization.MapCodec; import dan200.computercraft.shared.peripheral.modem.ModemShapes; import dan200.computercraft.shared.platform.RegistryEntry; +import dan200.computercraft.shared.util.BlockCodecs; import dan200.computercraft.shared.util.WaterloggableHelpers; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -31,6 +33,8 @@ import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED; import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStateForPlacement; public class WirelessModemBlock extends DirectionalBlock implements SimpleWaterloggedBlock, EntityBlock { + private static final MapCodec CODEC = BlockCodecs.blockWithBlockEntityCodec(WirelessModemBlock::new, x -> x.type); + public static final BooleanProperty ON = BooleanProperty.create("on"); private final RegistryEntry> type; @@ -50,6 +54,11 @@ public class WirelessModemBlock extends DirectionalBlock implements SimpleWaterl builder.add(FACING, ON, WATERLOGGED); } + @Override + protected MapCodec codec() { + return CODEC; + } + @Override @Deprecated public VoxelShape getShape(BlockState blockState, BlockGetter blockView, BlockPos blockPos, CollisionContext context) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlockEntity.java index 10fe36371..3e7e67e26 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemBlockEntity.java @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.modem.wireless; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.peripheral.modem.ModemPeripheral; import dan200.computercraft.shared.peripheral.modem.ModemState; +import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.util.TickScheduler; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -51,7 +52,6 @@ public class WirelessModemBlockEntity extends BlockEntity { private final boolean advanced; private final ModemPeripheral modem; - private @Nullable Runnable modemChanged; private final TickScheduler.Token tickToken = new TickScheduler.Token(this); public WirelessModemBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, boolean advanced) { @@ -77,7 +77,7 @@ public class WirelessModemBlockEntity extends BlockEntity { public void setBlockState(BlockState state) { var direction = getDirection(); super.setBlockState(state); - if (getDirection() != direction && modemChanged != null) modemChanged.run(); + if (getDirection() != direction) PlatformHelper.get().invalidateComponent(this); } void blockTick() { @@ -100,8 +100,4 @@ public class WirelessModemBlockEntity extends BlockEntity { public IPeripheral getPeripheral(@Nullable Direction direction) { return direction == null || getDirection() == direction ? modem : null; } - - public void onModemChanged(Runnable callback) { - modemChanged = callback; - } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java index 41d0b6182..b7d0e4392 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java @@ -4,8 +4,10 @@ package dan200.computercraft.shared.peripheral.monitor; +import com.mojang.serialization.MapCodec; import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.platform.RegistryEntry; +import dan200.computercraft.shared.util.BlockCodecs; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; @@ -33,6 +35,8 @@ import net.minecraft.world.phys.BlockHitResult; import javax.annotation.Nullable; public class MonitorBlock extends HorizontalDirectionalBlock implements EntityBlock { + private static final MapCodec CODEC = BlockCodecs.blockWithBlockEntityCodec(MonitorBlock::new, x -> x.type); + public static final DirectionProperty ORIENTATION = DirectionProperty.create("orientation", Direction.UP, Direction.DOWN, Direction.NORTH); @@ -57,6 +61,11 @@ public class MonitorBlock extends HorizontalDirectionalBlock implements EntityBl builder.add(ORIENTATION, FACING, STATE); } + @Override + protected MapCodec codec() { + return CODEC; + } + @Override @Nullable public BlockState getStateForPlacement(BlockPlaceContext context) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java index a08feac5a..4e3c59cf6 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java @@ -5,7 +5,6 @@ package dan200.computercraft.shared.peripheral.monitor; import com.google.common.annotations.VisibleForTesting; -import dan200.computercraft.annotations.ForgeOverride; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.terminal.Terminal; @@ -507,7 +506,6 @@ public class MonitorBlockEntity extends BlockEntity { computers.remove(computer); } - @ForgeOverride public AABB getRenderBoundingBox() { // We attempt to cache the bounding box to save having to do property lookups (and allocations!) on every frame. // Unfortunately the AABB does depend on quite a lot of state, so we need to add a bunch of extra fields - diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlock.java index b0bede7cf..b3b17366d 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlock.java @@ -4,10 +4,12 @@ package dan200.computercraft.shared.peripheral.printer; +import com.mojang.serialization.MapCodec; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.HorizontalContainerBlock; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -17,6 +19,8 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty; import javax.annotation.Nullable; public class PrinterBlock extends HorizontalContainerBlock { + private static final MapCodec CODEC = simpleCodec(PrinterBlock::new); + public static final BooleanProperty TOP = BooleanProperty.create("top"); public static final BooleanProperty BOTTOM = BooleanProperty.create("bottom"); @@ -33,6 +37,11 @@ public class PrinterBlock extends HorizontalContainerBlock { properties.add(FACING, TOP, BOTTOM); } + @Override + protected MapCodec codec() { + return CODEC; + } + @Nullable @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlock.java index c19b9522e..112060cb3 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlock.java @@ -4,6 +4,7 @@ package dan200.computercraft.shared.peripheral.speaker; +import com.mojang.serialization.MapCodec; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.util.BlockEntityHelpers; import net.minecraft.core.BlockPos; @@ -22,6 +23,7 @@ import net.minecraft.world.level.block.state.StateDefinition; import javax.annotation.Nullable; public class SpeakerBlock extends HorizontalDirectionalBlock implements EntityBlock { + private static final MapCodec CODEC = simpleCodec(SpeakerBlock::new); private static final BlockEntityTicker serverTicker = (level, pos, state, drive) -> drive.serverTick(); public SpeakerBlock(Properties settings) { @@ -35,6 +37,11 @@ public class SpeakerBlock extends HorizontalDirectionalBlock implements EntityBl properties.add(FACING); } + @Override + protected MapCodec codec() { + return CODEC; + } + @Nullable @Override public BlockState getStateForPlacement(BlockPlaceContext placement) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java b/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java index 8ed71f6da..b93b56dfe 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java @@ -21,7 +21,7 @@ import net.minecraft.core.Direction; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.common.ClientCommonPacketListener; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; @@ -83,15 +83,6 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper */ ConfigFile.Builder createConfigBuilder(); - /** - * Wrap a Minecraft registry in our own abstraction layer. - * - * @param registry The registry to wrap. - * @param The type of object stored in this registry. - * @return The wrapped registry. - */ - RegistryWrappers.RegistryWrapper wrap(ResourceKey> registry); - /** * Create a registration helper for a specific registry. * @@ -101,17 +92,6 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper */ RegistrationHelper createRegistrationHelper(ResourceKey> registry); - /** - * A version of {@link #getRegistryObject(ResourceKey, ResourceLocation)} which allows missing entries. - * - * @param registry The registry to look up this object in. - * @param id The ID to look up. - * @param The type of object the registry stores. - * @return The registered object or {@code null}. - */ - @Nullable - T tryGetRegistryObject(ResourceKey> registry, ResourceLocation id); - /** * Determine if this resource should be loaded, based on platform-specific loot conditions. *

@@ -167,14 +147,12 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper /** * 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 The type of this message. + * @param The type of this message. + * @param id The id for this message type. + * @param reader The function which reads the packet from a buffer. Should be the inverse to {@link NetworkMessage#write(FriendlyByteBuf)}. * @return The new {@link MessageType} instance. */ - > MessageType createMessageType(int id, ResourceLocation channel, Class klass, FriendlyByteBuf.Reader reader); + > MessageType createMessageType(ResourceLocation id, FriendlyByteBuf.Reader reader); /** * Convert a clientbound {@link NetworkMessage} to a Minecraft {@link Packet}. @@ -182,7 +160,15 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper * @param message The messsge to convert. * @return The converted message. */ - Packet createPacket(NetworkMessage message); + Packet createPacket(NetworkMessage message); + + /** + * Invalidate components on a block enitty. + * + * @param owner The block entity whose components should be invalidated. + */ + default void invalidateComponent(BlockEntity owner) { + } /** * Create a {@link ComponentAccess} for surrounding peripherals. @@ -334,7 +320,7 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper * @return The distance (in blocks) that a player can reach. */ default double getReachDistance(Player player) { - return player.isCreative() ? 5 : 4.5; + return Player.getPickRange(player.isCreative()); } /** diff --git a/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryEntry.java b/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryEntry.java index ecf0eb7c2..8b699fddf 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryEntry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryEntry.java @@ -4,8 +4,13 @@ package dan200.computercraft.shared.platform; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; import java.util.function.Supplier; @@ -22,4 +27,22 @@ public interface RegistryEntry extends Supplier { * @return This registered item. */ ResourceLocation id(); + + static Codec> codec(Registry registry) { + record HolderEntry(ResourceLocation id, Holder holder) implements RegistryEntry { + @Override + public T get() { + return holder().value(); + } + } + Codec> codec = ResourceLocation.CODEC.flatXmap( + id -> registry + .getHolder(ResourceKey.create(registry.key(), id)) + .map(x -> DataResult.success(new HolderEntry<>(id, x))) + .orElseGet(() -> DataResult.error(() -> "Unknown registry key in " + registry.key() + ": " + id)), + holder -> DataResult.success(holder.id()) + ); + + return ExtraCodecs.overrideLifecycle(codec, x -> registry.lifecycle(x.get()), x -> registry.lifecycle(x.get())); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryWrappers.java b/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryWrappers.java deleted file mode 100644 index 76c289a5b..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/platform/RegistryWrappers.java +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.platform; - -import net.minecraft.commands.synchronization.ArgumentTypeInfo; -import net.minecraft.core.IdMap; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.enchantment.Enchantment; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.material.Fluid; - -import javax.annotation.Nullable; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -/** - * Mimics {@link Registry} but using {@link PlatformHelper}'s recipe abstractions. - */ -public final class RegistryWrappers { - public static final RegistryWrapper ITEMS = PlatformHelper.get().wrap(Registries.ITEM); - public static final RegistryWrapper BLOCKS = PlatformHelper.get().wrap(Registries.BLOCK); - public static final RegistryWrapper> BLOCK_ENTITY_TYPES = PlatformHelper.get().wrap(Registries.BLOCK_ENTITY_TYPE); - public static final RegistryWrapper FLUIDS = PlatformHelper.get().wrap(Registries.FLUID); - public static final RegistryWrapper ENCHANTMENTS = PlatformHelper.get().wrap(Registries.ENCHANTMENT); - public static final RegistryWrapper> COMMAND_ARGUMENT_TYPES = PlatformHelper.get().wrap(Registries.COMMAND_ARGUMENT_TYPE); - public static final RegistryWrapper> RECIPE_SERIALIZERS = PlatformHelper.get().wrap(Registries.RECIPE_SERIALIZER); - public static final RegistryWrapper> MENU = PlatformHelper.get().wrap(Registries.MENU); - - public interface RegistryWrapper extends IdMap { - ResourceLocation getKey(T object); - - T get(ResourceLocation location); - - @Nullable - T tryGet(ResourceLocation location); - - default Stream stream() { - return StreamSupport.stream(spliterator(), false); - } - } - - private RegistryWrappers() { - } - - public static void writeKey(FriendlyByteBuf buf, RegistryWrapper registry, K object) { - buf.writeResourceLocation(registry.getKey(object)); - } - - public static K readKey(FriendlyByteBuf buf, RegistryWrapper registry) { - var id = buf.readResourceLocation(); - return registry.get(id); - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java index 97a7f9c6a..9207dd45f 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -4,7 +4,6 @@ package dan200.computercraft.shared.pocket.core; -import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.pocket.IPocketAccess; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.upgrades.UpgradeData; @@ -19,7 +18,6 @@ import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -29,7 +27,10 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class PocketServerComputer extends ServerComputer implements IPocketAccess { private @Nullable IPocketUpgrade upgrade; @@ -104,12 +105,6 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces setPeripheral(ComputerSide.BACK, peripheral); } - @Override - @Deprecated(forRemoval = true) - public Map getUpgrades() { - return upgrade == null ? Map.of() : Collections.singletonMap(upgrade.getUpgradeID(), getPeripheral(ComputerSide.BACK)); - } - public @Nullable UpgradeData getUpgrade() { return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData()); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java index bf55800a2..263935903 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java @@ -10,7 +10,6 @@ import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingBookCategory; @@ -19,8 +18,8 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.Level; public final class PocketComputerUpgradeRecipe extends CustomRecipe { - public PocketComputerUpgradeRecipe(ResourceLocation identifier, CraftingBookCategory category) { - super(identifier, category); + public PocketComputerUpgradeRecipe(CraftingBookCategory category) { + super(category); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapedRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapedRecipe.java index c1a7b27a0..e966be63d 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapedRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapedRecipe.java @@ -4,39 +4,39 @@ package dan200.computercraft.shared.recipe; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.ModRegistry; import net.minecraft.Util; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.ShapedRecipe; +import net.minecraft.world.item.crafting.ShapedRecipePattern; + +import java.util.function.Function; /** * A custom version of {@link ShapedRecipe}, which can be converted to and from a {@link ShapedRecipeSpec}. *

* This recipe may both be used as a normal recipe (behaving mostly the same as {@link ShapedRecipe}, with - * {@linkplain RecipeUtil#itemStackFromJson(JsonObject) support for putting nbt on the result}), or subclassed to + * {@linkplain MoreCodecs#ITEM_STACK_WITH_NBT support for putting nbt on the result}), or subclassed to * customise the crafting behaviour. */ public class CustomShapedRecipe extends ShapedRecipe { + private final ShapedRecipePattern pattern; private final ItemStack result; - public CustomShapedRecipe(ResourceLocation id, ShapedRecipeSpec recipe) { - super( - id, - recipe.properties().group(), recipe.properties().category(), - recipe.template().width(), recipe.template().height(), recipe.template().ingredients(), - recipe.result() - ); + public CustomShapedRecipe(ShapedRecipeSpec recipe) { + super(recipe.properties().group(), recipe.properties().category(), recipe.pattern(), recipe.result(), recipe.properties().showNotification()); + this.pattern = recipe.pattern(); this.result = recipe.result(); } public final ShapedRecipeSpec toSpec() { - return new ShapedRecipeSpec(RecipeProperties.of(this), ShapedTemplate.of(this), result); + return new ShapedRecipeSpec(RecipeProperties.of(this), pattern, result); } @Override @@ -44,38 +44,37 @@ public class CustomShapedRecipe extends ShapedRecipe { return ModRegistry.RecipeSerializers.SHAPED.get(); } - public interface Factory { - R create(ResourceLocation id, ShapedRecipeSpec recipe); + public static RecipeSerializer serialiser(Function factory) { + return new Serialiser<>(r -> DataResult.success(factory.apply(r))); } - public static RecipeSerializer serialiser(CustomShapedRecipe.Factory factory) { - return new Serialiser<>((id, r) -> DataResult.success(factory.create(id, r))); - } - - public static RecipeSerializer validatingSerialiser(CustomShapedRecipe.Factory> factory) { + public static RecipeSerializer validatingSerialiser(Function> factory) { return new Serialiser<>(factory); } - private record Serialiser( - Factory> factory - ) implements RecipeSerializer { - private Serialiser(Factory> factory) { - this.factory = (id, r) -> factory.create(id, r).flatMap(x -> { + private static final class Serialiser implements RecipeSerializer { + private final Function> factory; + private final Codec codec; + + private Serialiser(Function> factory) { + this.factory = r -> factory.apply(r).flatMap(x -> { if (x.getSerializer() != this) { - return DataResult.error(() -> "Expected serialiser to be " + this + ", but was " + x.getSerializer()); + return DataResult.error(() -> "Expected serialiser to be " + RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, this) + + ", but was " + RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, x.getSerializer())); } return DataResult.success(x); }); + this.codec = ShapedRecipeSpec.CODEC.flatXmap(factory, x -> DataResult.success(x.toSpec())).codec(); } @Override - public T fromJson(ResourceLocation id, JsonObject json) { - return Util.getOrThrow(factory.create(id, ShapedRecipeSpec.fromJson(json)), JsonParseException::new); + public Codec codec() { + return codec; } @Override - public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { - return Util.getOrThrow(factory.create(id, ShapedRecipeSpec.fromNetwork(buffer)), IllegalStateException::new); + public T fromNetwork(FriendlyByteBuf buffer) { + return Util.getOrThrow(factory.apply(ShapedRecipeSpec.fromNetwork(buffer)), IllegalStateException::new); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapelessRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapelessRecipe.java index e948d411d..c9a4749a4 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapelessRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/CustomShapelessRecipe.java @@ -4,73 +4,81 @@ package dan200.computercraft.shared.recipe; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.ModRegistry; import net.minecraft.Util; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.ShapelessRecipe; +import java.util.function.Function; + /** * A custom version of {@link ShapelessRecipe}, which can be converted to and from a {@link ShapelessRecipeSpec}. *

* This recipe may both be used as a normal recipe (behaving mostly the same as {@link ShapelessRecipe}, with - * {@linkplain RecipeUtil#itemStackFromJson(JsonObject) support for putting nbt on the result}), or subclassed to + * {@linkplain MoreCodecs#ITEM_STACK_WITH_NBT support for putting nbt on the result}), or subclassed to * customise the crafting behaviour. */ public class CustomShapelessRecipe extends ShapelessRecipe { private final ItemStack result; + private final boolean showNotification; - public CustomShapelessRecipe(ResourceLocation id, ShapelessRecipeSpec recipe) { - super(id, recipe.properties().group(), recipe.properties().category(), recipe.result(), recipe.ingredients()); + public CustomShapelessRecipe(ShapelessRecipeSpec recipe) { + super(recipe.properties().group(), recipe.properties().category(), recipe.result(), recipe.ingredients()); this.result = recipe.result(); + this.showNotification = recipe.properties().showNotification(); } public final ShapelessRecipeSpec toSpec() { return new ShapelessRecipeSpec(RecipeProperties.of(this), getIngredients(), result); } + @Override + public final boolean showNotification() { + return showNotification; + } + @Override public RecipeSerializer getSerializer() { return ModRegistry.RecipeSerializers.SHAPELESS.get(); } - public interface Factory { - R create(ResourceLocation id, ShapelessRecipeSpec recipe); + public static RecipeSerializer serialiser(Function factory) { + return new CustomShapelessRecipe.Serialiser<>(r -> DataResult.success(factory.apply(r))); } - public static RecipeSerializer serialiser(Factory factory) { - return new CustomShapelessRecipe.Serialiser<>((id, r) -> DataResult.success(factory.create(id, r))); - } - - public static RecipeSerializer validatingSerialiser(Factory> factory) { + public static RecipeSerializer validatingSerialiser(Function> factory) { return new CustomShapelessRecipe.Serialiser<>(factory); } - private record Serialiser( - Factory> factory - ) implements RecipeSerializer { - private Serialiser(Factory> factory) { - this.factory = (id, r) -> factory.create(id, r).flatMap(x -> { + private static final class Serialiser implements RecipeSerializer { + private final Function> factory; + private final Codec codec; + + private Serialiser(Function> factory) { + this.factory = r -> factory.apply(r).flatMap(x -> { if (x.getSerializer() != this) { - return DataResult.error(() -> "Expected serialiser to be " + this + ", but was " + x.getSerializer()); + return DataResult.error(() -> "Expected serialiser to be " + RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, this) + + ", but was " + RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, x.getSerializer())); } return DataResult.success(x); }); + this.codec = ShapelessRecipeSpec.CODEC.flatXmap(factory, x -> DataResult.success(x.toSpec())).codec(); } @Override - public T fromJson(ResourceLocation id, JsonObject json) { - return Util.getOrThrow(factory.create(id, ShapelessRecipeSpec.fromJson(json)), JsonParseException::new); + public Codec codec() { + return codec; } @Override - public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { - return Util.getOrThrow(factory.create(id, ShapelessRecipeSpec.fromNetwork(buffer)), IllegalStateException::new); + public T fromNetwork(FriendlyByteBuf buffer) { + return Util.getOrThrow(factory.apply(ShapelessRecipeSpec.fromNetwork(buffer)), IllegalStateException::new); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapedRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapedRecipe.java index f598cbee8..fa09ba49f 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapedRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapedRecipe.java @@ -6,7 +6,6 @@ package dan200.computercraft.shared.recipe; import dan200.computercraft.shared.ModRegistry; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CustomRecipe; @@ -20,8 +19,8 @@ import net.minecraft.world.level.Level; * This is used to represent examples for our {@link CustomRecipe}s. */ public final class ImpostorShapedRecipe extends CustomShapedRecipe { - public ImpostorShapedRecipe(ResourceLocation id, ShapedRecipeSpec recipe) { - super(id, recipe); + public ImpostorShapedRecipe(ShapedRecipeSpec recipe) { + super(recipe); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapelessRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapelessRecipe.java index 25974cb3d..4bace646c 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapelessRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ImpostorShapelessRecipe.java @@ -6,7 +6,6 @@ package dan200.computercraft.shared.recipe; import dan200.computercraft.shared.ModRegistry; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CustomRecipe; @@ -20,8 +19,8 @@ import net.minecraft.world.level.Level; * This is used to represent examples for our {@link CustomRecipe}s. */ public final class ImpostorShapelessRecipe extends CustomShapelessRecipe { - public ImpostorShapelessRecipe(ResourceLocation id, ShapelessRecipeSpec recipe) { - super(id, recipe); + public ImpostorShapelessRecipe(ShapelessRecipeSpec recipe) { + super(recipe); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/MoreCodecs.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/MoreCodecs.java index 9de9c1eab..99204ed35 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/MoreCodecs.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/MoreCodecs.java @@ -8,13 +8,34 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.NonNullList; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.TagParser; +import net.minecraft.util.ExtraCodecs; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.ShapelessRecipe; +import net.minecraft.world.level.ItemLike; + +import java.util.Optional; + /** * Additional codecs for working with recipes. */ public class MoreCodecs { + /** + * A non-air item. + */ + private static final Codec ITEM_NOT_AIR = ExtraCodecs.validate( + BuiltInRegistries.ITEM.byNameCodec(), + item -> item == Items.AIR ? DataResult.error(() -> "Crafting result must not be minecraft:air") : DataResult.success(item) + ); + /** * A codec for {@link CompoundTag}s, which either accepts a NBT-string or a JSON object. */ @@ -23,6 +44,26 @@ public class MoreCodecs { nbtCompound -> DataResult.success(Either.left(nbtCompound.getAsString())) ); + /** + * A {@link ItemStack}, similar to {@link ItemStack#ITEM_WITH_COUNT_CODEC} or {@link ItemStack#CODEC}, + * but using {@link #TAG} to parse the stack's NBT. + */ + public static final Codec ITEM_STACK_WITH_NBT = RecordCodecBuilder.create(instance -> instance.group( + ITEM_NOT_AIR.fieldOf("item").forGetter(ItemStack::getItem), + ExtraCodecs.strictOptionalField(ExtraCodecs.POSITIVE_INT, "count", 1).forGetter(ItemStack::getCount), + ExtraCodecs.strictOptionalField(TAG, "nbt").forGetter(x -> Optional.ofNullable(x.getTag())) + ).apply(instance, MoreCodecs::createTag)); + + /** + * A list of {@link Ingredient}s, usable in a {@linkplain ShapelessRecipe shapeless recipe}. + */ + public static final Codec> SHAPELESS_INGREDIENTS = Ingredient.CODEC_NONEMPTY.listOf().flatXmap(list -> { + var ingredients = list.stream().filter(ingredient -> !ingredient.isEmpty()).toArray(Ingredient[]::new); + if (ingredients.length == 0) return DataResult.error(() -> "No ingredients for shapeless recipe"); + if (ingredients.length > 9) return DataResult.error(() -> "Too many ingredients for shapeless recipe"); + return DataResult.success(NonNullList.of(Ingredient.EMPTY, ingredients)); + }, DataResult::success); + private static DataResult parseTag(String contents) { try { return DataResult.success(TagParser.parseTag(contents)); @@ -30,4 +71,10 @@ public class MoreCodecs { return DataResult.error(e::getMessage); } } + + private static ItemStack createTag(ItemLike item, int count, Optional tag) { + var stack = new ItemStack(item, count); + tag.ifPresent(stack::setTag); + return stack; + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeProperties.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeProperties.java index 9048bc219..725409ead 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeProperties.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeProperties.java @@ -4,37 +4,42 @@ package dan200.computercraft.shared.recipe; -import com.google.gson.JsonObject; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; +import net.minecraft.util.ExtraCodecs; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.CraftingRecipe; /** * Common properties that appear in all {@link CraftingRecipe}s. * - * @param group The (optional) group of the recipe, see {@link CraftingRecipe#getGroup()}. - * @param category The category the recipe appears in, see {@link CraftingRecipe#category()}. + * @param group The (optional) group of the recipe, see {@link CraftingRecipe#getGroup()}. + * @param category The category the recipe appears in, see {@link CraftingRecipe#category()}. + * @param showNotification Show notifications when the recipe is unlocked, see {@link CraftingRecipe#showNotification()}. */ -public record RecipeProperties(String group, CraftingBookCategory category) { - public static RecipeProperties of(CraftingRecipe recipe) { - return new RecipeProperties(recipe.getGroup(), recipe.category()); - } +public record RecipeProperties(String group, CraftingBookCategory category, boolean showNotification) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + ExtraCodecs.strictOptionalField(Codec.STRING, "group", "").forGetter(RecipeProperties::group), + CraftingBookCategory.CODEC.fieldOf("category").orElse(CraftingBookCategory.MISC).forGetter(RecipeProperties::category), + ExtraCodecs.strictOptionalField(Codec.BOOL, "show_notification", true).forGetter(RecipeProperties::showNotification) + ).apply(instance, RecipeProperties::new)); - public static RecipeProperties fromJson(JsonObject json) { - var group = GsonHelper.getAsString(json, "group", ""); - var category = CraftingBookCategory.CODEC.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC); - return new RecipeProperties(group, category); + public static RecipeProperties of(CraftingRecipe recipe) { + return new RecipeProperties(recipe.getGroup(), recipe.category(), recipe.showNotification()); } public static RecipeProperties fromNetwork(FriendlyByteBuf buffer) { var group = buffer.readUtf(); var category = buffer.readEnum(CraftingBookCategory.class); - return new RecipeProperties(group, category); + var showNotification = buffer.readBoolean(); + return new RecipeProperties(group, category, showNotification); } public void toNetwork(FriendlyByteBuf buffer) { buffer.writeUtf(group()); buffer.writeEnum(category()); + buffer.writeBoolean(showNotification()); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeUtil.java index 83ad4c44b..f22141262 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeUtil.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/RecipeUtil.java @@ -4,17 +4,9 @@ package dan200.computercraft.shared.recipe; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSyntaxException; -import com.mojang.serialization.JsonOps; -import net.minecraft.Util; import net.minecraft.core.NonNullList; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.ShapedRecipe; public final class RecipeUtil { private RecipeUtil() { @@ -30,44 +22,4 @@ public final class RecipeUtil { public static void writeIngredients(FriendlyByteBuf buffer, NonNullList ingredients) { buffer.writeCollection(ingredients, (a, b) -> b.toNetwork(a)); } - - public static NonNullList readShapelessIngredients(JsonObject json) { - NonNullList ingredients = NonNullList.create(); - - var ingredientsList = GsonHelper.getAsJsonArray(json, "ingredients"); - for (var i = 0; i < ingredientsList.size(); ++i) { - var ingredient = Ingredient.fromJson(ingredientsList.get(i)); - if (!ingredient.isEmpty()) ingredients.add(ingredient); - } - - if (ingredients.isEmpty()) throw new JsonParseException("No ingredients for shapeless recipe"); - if (ingredients.size() > 9) { - throw new JsonParseException("Too many ingredients for shapeless recipe the max is 9"); - } - - return ingredients; - } - - /** - * Extends {@link ShapedRecipe#itemStackFromJson(JsonObject)} with support for the {@code nbt} field. - * - * @param json The json to extract the item from. - * @return The parsed item stack. - */ - public static ItemStack itemStackFromJson(JsonObject json) { - var item = ShapedRecipe.itemFromJson(json); - if (json.has("data")) throw new JsonParseException("Disallowed data tag found"); - - var count = GsonHelper.getAsInt(json, "count", 1); - if (count < 1) throw new JsonSyntaxException("Invalid output count: " + count); - - var stack = new ItemStack(item, count); - - var nbt = json.get("nbt"); - if (nbt != null) { - stack.setTag(Util.getOrThrow(MoreCodecs.TAG.parse(JsonOps.INSTANCE, nbt), JsonParseException::new)); - } - return stack; - } - } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedRecipeSpec.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedRecipeSpec.java index a93115ba0..fd61842e0 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedRecipeSpec.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedRecipeSpec.java @@ -4,11 +4,12 @@ package dan200.computercraft.shared.recipe; -import com.google.gson.JsonObject; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.ShapedRecipe; +import net.minecraft.world.item.crafting.ShapedRecipePattern; /** * A description of a {@link ShapedRecipe}. @@ -17,27 +18,26 @@ import net.minecraft.world.item.crafting.ShapedRecipe; * deserialisation of {@link ShapedRecipe}-like recipes. * * @param properties The common properties of this recipe. - * @param template The shaped template of the recipe. + * @param pattern The shaped template of the recipe. * @param result The result of the recipe. */ -public record ShapedRecipeSpec(RecipeProperties properties, ShapedTemplate template, ItemStack result) { - public static ShapedRecipeSpec fromJson(JsonObject json) { - var properties = RecipeProperties.fromJson(json); - var template = ShapedTemplate.fromJson(json); - var result = RecipeUtil.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result")); - return new ShapedRecipeSpec(properties, template, result); - } +public record ShapedRecipeSpec(RecipeProperties properties, ShapedRecipePattern pattern, ItemStack result) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + RecipeProperties.CODEC.forGetter(ShapedRecipeSpec::properties), + ShapedRecipePattern.MAP_CODEC.forGetter(ShapedRecipeSpec::pattern), + MoreCodecs.ITEM_STACK_WITH_NBT.fieldOf("result").forGetter(ShapedRecipeSpec::result) + ).apply(instance, ShapedRecipeSpec::new)); public static ShapedRecipeSpec fromNetwork(FriendlyByteBuf buffer) { var properties = RecipeProperties.fromNetwork(buffer); - var template = ShapedTemplate.fromNetwork(buffer); + var template = ShapedRecipePattern.fromNetwork(buffer); var result = buffer.readItem(); return new ShapedRecipeSpec(properties, template, result); } public void toNetwork(FriendlyByteBuf buffer) { properties().toNetwork(buffer); - template().toNetwork(buffer); + pattern().toNetwork(buffer); buffer.writeItem(result()); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedTemplate.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedTemplate.java deleted file mode 100644 index ef84079f9..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapedTemplate.java +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.recipe; - -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; -import net.minecraft.core.NonNullList; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.ShapedRecipe; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * The template for {@linkplain ShapedRecipe shaped recipes}. This largely exists for parsing shaped recipes from JSON. - * - * @param width The width of the recipe, see {@link ShapedRecipe#getWidth()}. - * @param height The height of the recipe, see {@link ShapedRecipe#getHeight()}. - * @param ingredients The ingredients in the recipe, see {@link ShapedRecipe#getIngredients()} - */ -public record ShapedTemplate(int width, int height, NonNullList ingredients) { - public static ShapedTemplate of(ShapedRecipe recipe) { - return new ShapedTemplate(recipe.getWidth(), recipe.getHeight(), recipe.getIngredients()); - } - - public static ShapedTemplate fromJson(JsonObject json) { - Map key = new HashMap<>(); - for (var entry : GsonHelper.getAsJsonObject(json, "key").entrySet()) { - if (entry.getKey().length() != 1) { - throw new JsonSyntaxException("Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only)."); - } - if (" ".equals(entry.getKey())) { - throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol."); - } - - key.put(entry.getKey().charAt(0), Ingredient.fromJson(entry.getValue())); - } - - var patternList = GsonHelper.getAsJsonArray(json, "pattern"); - if (patternList.size() == 0) { - throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed"); - } - - var pattern = new String[patternList.size()]; - for (var x = 0; x < pattern.length; x++) { - var line = GsonHelper.convertToString(patternList.get(x), "pattern[" + x + "]"); - if (x > 0 && pattern[0].length() != line.length()) { - throw new JsonSyntaxException("Invalid pattern: each row must be the same width"); - } - pattern[x] = line; - } - - var width = pattern[0].length(); - var height = pattern.length; - var ingredients = NonNullList.withSize(width * height, Ingredient.EMPTY); - - Set missingKeys = new HashSet<>(key.keySet()); - - var ingredientIdx = 0; - for (var line : pattern) { - for (var x = 0; x < line.length(); x++) { - var chr = line.charAt(x); - var ing = chr == ' ' ? Ingredient.EMPTY : key.get(chr); - if (ing == null) { - throw new JsonSyntaxException("Pattern references symbol '" + chr + "' but it's not defined in the key"); - } - ingredients.set(ingredientIdx++, ing); - missingKeys.remove(chr); - } - } - - if (!missingKeys.isEmpty()) { - throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + missingKeys); - } - - return new ShapedTemplate(width, height, ingredients); - } - - public static ShapedTemplate fromNetwork(FriendlyByteBuf buffer) { - var width = buffer.readVarInt(); - var height = buffer.readVarInt(); - var ingredients = NonNullList.withSize(width * height, Ingredient.EMPTY); - for (var i = 0; i < ingredients.size(); ++i) ingredients.set(i, Ingredient.fromNetwork(buffer)); - return new ShapedTemplate(width, height, ingredients); - } - - public void toNetwork(FriendlyByteBuf buffer) { - buffer.writeVarInt(width()); - buffer.writeVarInt(height()); - for (var ingredient : ingredients) ingredient.toNetwork(buffer); - } - -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapelessRecipeSpec.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapelessRecipeSpec.java index 7a3011f5c..3ad95f503 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapelessRecipeSpec.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/ShapelessRecipeSpec.java @@ -4,10 +4,10 @@ package dan200.computercraft.shared.recipe; -import com.google.gson.JsonObject; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.NonNullList; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.ShapelessRecipe; @@ -23,12 +23,11 @@ import net.minecraft.world.item.crafting.ShapelessRecipe; * @param result The result of the recipe. */ public record ShapelessRecipeSpec(RecipeProperties properties, NonNullList ingredients, ItemStack result) { - public static ShapelessRecipeSpec fromJson(JsonObject json) { - var properties = RecipeProperties.fromJson(json); - var ingredients = RecipeUtil.readShapelessIngredients(json); - var result = RecipeUtil.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result")); - return new ShapelessRecipeSpec(properties, ingredients, result); - } + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + RecipeProperties.CODEC.forGetter(ShapelessRecipeSpec::properties), + MoreCodecs.SHAPELESS_INGREDIENTS.fieldOf("ingredients").forGetter(ShapelessRecipeSpec::ingredients), + MoreCodecs.ITEM_STACK_WITH_NBT.fieldOf("result").forGetter(ShapelessRecipeSpec::result) + ).apply(instance, ShapelessRecipeSpec::new)); public static ShapelessRecipeSpec fromNetwork(FriendlyByteBuf buffer) { var properties = RecipeProperties.fromNetwork(buffer); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java index e2b01fa73..b187e41dd 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java @@ -4,6 +4,8 @@ package dan200.computercraft.shared.turtle.blocks; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import dan200.computercraft.annotations.ForgeOverride; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; @@ -13,6 +15,7 @@ import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity; import dan200.computercraft.shared.platform.RegistryEntry; import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.turtle.items.TurtleItem; +import dan200.computercraft.shared.util.BlockCodecs; import dan200.computercraft.shared.util.BlockEntityHelpers; import dan200.computercraft.shared.util.WaterloggableHelpers; import net.minecraft.core.BlockPos; @@ -49,6 +52,11 @@ import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED; import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStateForPlacement; public class TurtleBlock extends AbstractComputerBlock implements SimpleWaterloggedBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockCodecs.propertiesCodec(), + BlockCodecs.blockEntityCodec(x -> x.type) + ).apply(instance, TurtleBlock::new)); + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; /** @@ -81,6 +89,11 @@ public class TurtleBlock extends AbstractComputerBlock implem builder.add(FACING, WATERLOGGED); } + @Override + protected MapCodec codec() { + return CODEC; + } + @Override @Deprecated public RenderShape getRenderShape(BlockState state) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java index 87595824b..68845e450 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java @@ -17,6 +17,7 @@ import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.container.BasicContainer; +import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.turtle.apis.TurtleAPI; import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.turtle.inventory.TurtleMenu; @@ -60,7 +61,6 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba private TurtleBrain brain = new TurtleBrain(this); private MoveState moveState = MoveState.NOT_MOVED; private @Nullable IPeripheral peripheral; - private @Nullable Runnable onMoved; public TurtleBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, IntSupplier fuelLimit, ComputerFamily family) { super(type, pos, state, family); @@ -304,7 +304,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba // Mark the other turtle as having moved, and so its peripheral is dead. copy.moveState = MoveState.MOVED; - if (onMoved != null) onMoved.run(); + PlatformHelper.get().invalidateComponent(this); } @Nullable @@ -314,10 +314,6 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba return peripheral = new ComputerPeripheral("turtle", this); } - public void onMoved(Runnable onMoved) { - this.onMoved = onMoved; - } - @Nullable @Override public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java index 42445b935..8288333fd 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -44,7 +44,7 @@ public final class TurtlePlayer { } private static GameProfile getProfile(@Nullable GameProfile profile) { - return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE; + return profile != null ? profile : DEFAULT_PROFILE; } public static TurtlePlayer get(ITurtleAccess access) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java index 65d6ee1b2..38a7a567b 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java @@ -4,7 +4,8 @@ package dan200.computercraft.shared.turtle.recipes; -import com.google.gson.JsonObject; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.recipe.CustomShapelessRecipe; @@ -13,7 +14,6 @@ import dan200.computercraft.shared.turtle.items.TurtleItem; import net.minecraft.core.RegistryAccess; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.GsonHelper; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeSerializer; @@ -25,13 +25,12 @@ import net.minecraft.world.item.crafting.ShapelessRecipe; public class TurtleOverlayRecipe extends CustomShapelessRecipe { private final ResourceLocation overlay; - public TurtleOverlayRecipe(ResourceLocation id, ShapelessRecipeSpec spec, ResourceLocation overlay) { - super(id, spec); + public TurtleOverlayRecipe(ShapelessRecipeSpec spec, ResourceLocation overlay) { + super(spec); this.overlay = overlay; } - private static ItemStack make(ItemStack stack, ResourceLocation overlay) { - var turtle = (TurtleItem) stack.getItem(); + private static ItemStack make(ItemStack stack, TurtleItem turtle, ResourceLocation overlay) { return turtle.create( turtle.getComputerID(stack), turtle.getLabel(stack), @@ -47,7 +46,7 @@ public class TurtleOverlayRecipe extends CustomShapelessRecipe { public ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAccess) { for (var i = 0; i < inventory.getContainerSize(); i++) { var stack = inventory.getItem(i); - if (stack.getItem() instanceof TurtleItem) return make(stack, overlay); + if (stack.getItem() instanceof TurtleItem turtle) return make(stack, turtle, overlay); } return ItemStack.EMPTY; @@ -59,19 +58,20 @@ public class TurtleOverlayRecipe extends CustomShapelessRecipe { } public static class Serialiser implements RecipeSerializer { + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + ShapelessRecipeSpec.CODEC.forGetter(CustomShapelessRecipe::toSpec), + ResourceLocation.CODEC.fieldOf("overlay").forGetter(x -> x.overlay) + ).apply(instance, TurtleOverlayRecipe::new)); @Override - public TurtleOverlayRecipe fromJson(ResourceLocation id, JsonObject json) { - var recipe = ShapelessRecipeSpec.fromJson(json); - var overlay = new ResourceLocation(GsonHelper.getAsString(json, "overlay")); - - return new TurtleOverlayRecipe(id, recipe, overlay); + public Codec codec() { + return CODEC; } @Override - public TurtleOverlayRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { + public TurtleOverlayRecipe fromNetwork(FriendlyByteBuf buffer) { var recipe = ShapelessRecipeSpec.fromNetwork(buffer); var overlay = buffer.readResourceLocation(); - return new TurtleOverlayRecipe(id, recipe, overlay); + return new TurtleOverlayRecipe(recipe, overlay); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java index d73d29aca..7097d7db0 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java @@ -10,7 +10,6 @@ import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.computer.recipe.ComputerConvertRecipe; import dan200.computercraft.shared.recipe.ShapedRecipeSpec; import dan200.computercraft.shared.turtle.items.TurtleItem; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeSerializer; @@ -20,17 +19,17 @@ import net.minecraft.world.item.crafting.RecipeSerializer; public final class TurtleRecipe extends ComputerConvertRecipe { private final TurtleItem turtle; - private TurtleRecipe(ResourceLocation id, ShapedRecipeSpec recipe, TurtleItem turtle) { - super(id, recipe); + private TurtleRecipe(ShapedRecipeSpec recipe, TurtleItem turtle) { + super(recipe); this.turtle = turtle; } - public static DataResult of(ResourceLocation id, ShapedRecipeSpec recipe) { + public static DataResult of(ShapedRecipeSpec recipe) { if (!(recipe.result().getItem() instanceof TurtleItem turtle)) { return DataResult.error(() -> recipe.result().getItem() + " is not a turtle item"); } - return DataResult.success(new TurtleRecipe(id, recipe, turtle)); + return DataResult.success(new TurtleRecipe(recipe, turtle)); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java index 3ad657e47..868c2a1da 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java @@ -11,7 +11,6 @@ import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.turtle.items.TurtleItem; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingBookCategory; @@ -20,8 +19,8 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.Level; public final class TurtleUpgradeRecipe extends CustomRecipe { - public TurtleUpgradeRecipe(ResourceLocation id, CraftingBookCategory category) { - super(id, category); + public TurtleUpgradeRecipe(CraftingBookCategory category) { + super(category); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java index e428d36fd..1f9de26e7 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java @@ -14,6 +14,7 @@ import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.Level; @@ -55,7 +56,7 @@ public class TurtleInventoryCrafting implements CraftingContainer { } // Check the actual crafting - return turtle.getLevel().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, this, turtle.getLevel()).orElse(null); + return turtle.getLevel().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, this, turtle.getLevel()).map(RecipeHolder::value).orElse(null); } @Nullable diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java index e480b31f2..f859b22e5 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java @@ -6,9 +6,9 @@ package dan200.computercraft.shared.turtle.upgrades; import com.google.gson.JsonObject; import dan200.computercraft.api.turtle.TurtleToolDurability; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; @@ -19,7 +19,7 @@ import net.minecraft.world.level.block.Block; import java.util.Objects; -public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser { +public final class TurtleToolSerialiser implements UpgradeSerialiser { public static final TurtleToolSerialiser INSTANCE = new TurtleToolSerialiser(); private TurtleToolSerialiser() { @@ -29,7 +29,7 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser T getRegistryEntry(String name, String typeName, RegistryWrappers.RegistryWrapper registry) throws LuaException { + public static T getRegistryEntry(String name, String typeName, Registry registry) throws LuaException { ResourceLocation id; try { id = new ResourceLocation(name); @@ -28,7 +28,7 @@ public final class ArgumentHelpers { } T value; - if (id == null || (value = registry.tryGet(id)) == null) { + if (id == null || (value = registry.get(id)) == null) { throw new LuaException(String.format("Unknown %s '%s'", typeName, name)); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/BlockCodecs.java b/projects/common/src/main/java/dan200/computercraft/shared/util/BlockCodecs.java new file mode 100644 index 000000000..d707b5fe2 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/BlockCodecs.java @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.util; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dan200.computercraft.shared.platform.RegistryEntry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Additional codecs for block properties. + */ +public final class BlockCodecs { + private BlockCodecs() { + } + + public static RecordCodecBuilder propertiesCodec() { + return BlockBehaviour.Properties.CODEC.fieldOf("properties").forGetter(BlockBehaviour::properties); + } + + @SuppressWarnings("unchecked") + public static > RecordCodecBuilder> blockEntityCodec(Function> getter) { + return RegistryEntry.codec(BuiltInRegistries.BLOCK_ENTITY_TYPE) + .xmap(x -> (RegistryEntry) x, x -> x) + .fieldOf("block_entity").forGetter(getter); + } + + public static > MapCodec blockWithBlockEntityCodec( + BiFunction, B> factory, + Function> getter + ) { + return RecordCodecBuilder.mapCodec(instance -> instance.group(propertiesCodec(), blockEntityCodec(getter)).apply(instance, factory)); + } +} diff --git a/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json b/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json index 17254d90e..a8518a768 100644 --- a/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json +++ b/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json @@ -22,7 +22,7 @@ "north": { "texture": "#cursor", "cullface": "north", - "forge_data": {"block_light": 15, "sky_light": 15} + "neoforge_data": {"block_light": 15, "sky_light": 15} } } } diff --git a/projects/common/src/main/resources/computercraft-common.accesswidener b/projects/common/src/main/resources/computercraft-common.accesswidener index 71c43c6fe..d6e6e5044 100644 --- a/projects/common/src/main/resources/computercraft-common.accesswidener +++ b/projects/common/src/main/resources/computercraft-common.accesswidener @@ -16,7 +16,6 @@ accessible field net/minecraft/world/item/CreativeModeTabs OP_BLOCKS Lnet/minecr accessible class net/minecraft/world/inventory/MenuType$MenuSupplier accessible method net/minecraft/world/inventory/MenuType (Lnet/minecraft/world/inventory/MenuType$MenuSupplier;Lnet/minecraft/world/flag/FeatureFlagSet;)V accessible class net/minecraft/client/gui/screens/MenuScreens$ScreenConstructor -accessible method net/minecraft/client/gui/screens/MenuScreens register (Lnet/minecraft/world/inventory/MenuType;Lnet/minecraft/client/gui/screens/MenuScreens$ScreenConstructor;)V # Data generators accessible class net/minecraft/data/loot/LootTableProvider$SubProviderEntry diff --git a/projects/common/src/main/resources/pack.mcmeta b/projects/common/src/main/resources/pack.mcmeta index 3eeeeebbf..1f18b939a 100755 --- a/projects/common/src/main/resources/pack.mcmeta +++ b/projects/common/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { - "pack_format": 15, + "pack_format": 18, "description": "CC: Tweaked" } } diff --git a/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java b/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java index 815c716db..e3d6d0ffe 100644 --- a/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java +++ b/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java @@ -18,16 +18,15 @@ import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.container.ContainerData; import dan200.computercraft.shared.platform.*; -import io.netty.buffer.Unpooled; import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientGamePacketListener; -import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.ClientCommonPacketListener; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; @@ -54,7 +53,6 @@ import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import javax.annotation.Nullable; -import java.util.Iterator; import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -78,38 +76,6 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat throw new UnsupportedOperationException("Cannot query registry inside tests"); } - @SuppressWarnings("unchecked") - private static Registry getRegistry(ResourceKey> id) { - var registry = (Registry) BuiltInRegistries.REGISTRY.get(id.location()); - if (registry == null) throw new IllegalArgumentException("Unknown registry " + id); - return registry; - } - - @Override - public ResourceLocation getRegistryKey(ResourceKey> registry, T object) { - var key = getRegistry(registry).getKey(object); - if (key == null) throw new IllegalArgumentException(object + " was not registered in " + registry); - return key; - } - - @Override - public T getRegistryObject(ResourceKey> registry, ResourceLocation id) { - var value = getRegistry(registry).get(id); - if (value == null) throw new IllegalArgumentException(id + " was not registered in " + registry); - return value; - } - - @Override - public RegistryWrappers.RegistryWrapper wrap(ResourceKey> registry) { - return new RegistryWrapperImpl<>(registry.location(), getRegistry(registry)); - } - - @Nullable - @Override - public T tryGetRegistryObject(ResourceKey> registry, ResourceLocation id) { - return getRegistry(registry).get(id); - } - @Override public boolean shouldLoadResource(JsonObject object) { throw new UnsupportedOperationException("Cannot use resource conditions"); @@ -145,21 +111,26 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat throw new UnsupportedOperationException("Cannot open menu inside tests"); } - record TypeImpl>( - ResourceLocation id, Function reader - ) implements MessageType { + @Override + public > MessageType createMessageType(ResourceLocation id, FriendlyByteBuf.Reader reader) { + record TypeImpl>(ResourceLocation id) implements MessageType { + } + return new TypeImpl<>(id); } @Override - public > MessageType createMessageType(int id, ResourceLocation channel, Class klass, FriendlyByteBuf.Reader reader) { - return new TypeImpl<>(channel, reader); - } + public Packet createPacket(NetworkMessage message) { + return new ClientboundCustomPayloadPacket(new CustomPacketPayload() { + @Override + public void write(FriendlyByteBuf friendlyByteBuf) { + message.write(friendlyByteBuf); + } - @Override - public Packet createPacket(NetworkMessage message) { - var buf = new FriendlyByteBuf(Unpooled.buffer()); - message.write(buf); - return new ClientboundCustomPayloadPacket(((TypeImpl) message.type()).id(), buf); + @Override + public ResourceLocation id() { + return message.type().id(); + } + }); } @Override @@ -252,48 +223,4 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat public String getInstalledVersion() { return "1.0"; } - - private record RegistryWrapperImpl( - ResourceLocation name, Registry registry - ) implements RegistryWrappers.RegistryWrapper { - @Override - public int getId(T object) { - return registry.getId(object); - } - - @Override - public ResourceLocation getKey(T object) { - var key = registry.getKey(object); - if (key == null) throw new IllegalArgumentException(object + " was not registered in " + name); - return key; - } - - @Override - public T get(ResourceLocation location) { - var object = registry.get(location); - if (object == null) throw new IllegalArgumentException(location + " was not registered in " + name); - return object; - } - - @Nullable - @Override - public T tryGet(ResourceLocation location) { - return registry.get(location); - } - - @Override - public @Nullable T byId(int id) { - return registry.byId(id); - } - - @Override - public int size() { - return registry.size(); - } - - @Override - public Iterator iterator() { - return registry.iterator(); - } - } } diff --git a/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeArbitraries.java b/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeArbitraries.java index 36235fb0c..759272302 100644 --- a/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeArbitraries.java +++ b/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeArbitraries.java @@ -12,6 +12,9 @@ import net.jqwik.api.Combinators; import net.minecraft.core.NonNullList; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.ShapedRecipePattern; + +import java.util.Optional; /** * {@link Arbitrary} implementations for recipes. @@ -20,7 +23,8 @@ public final class RecipeArbitraries { public static Arbitrary recipeProperties() { return Combinators.combine( Arbitraries.strings().ofMinLength(1).withChars("abcdefghijklmnopqrstuvwxyz_"), - Arbitraries.of(CraftingBookCategory.values()) + Arbitraries.of(CraftingBookCategory.values()), + Arbitraries.of(true, false) ).as(RecipeProperties::new); } @@ -32,18 +36,18 @@ public final class RecipeArbitraries { ).as(ShapelessRecipeSpec::new); } - public static Arbitrary shapedTemplate() { + public static Arbitrary shapedPattern() { return Combinators.combine(Arbitraries.integers().between(1, 3), Arbitraries.integers().between(1, 3)) .as(IntIntImmutablePair::new) .flatMap(x -> MinecraftArbitraries.ingredient().array(Ingredient[].class).ofSize(x.leftInt() * x.rightInt()) - .map(i -> new ShapedTemplate(x.leftInt(), x.rightInt(), NonNullList.of(Ingredient.EMPTY, i))) + .map(i -> new ShapedRecipePattern(x.leftInt(), x.rightInt(), NonNullList.of(Ingredient.EMPTY, i), Optional.empty())) ); } public static Arbitrary shapedRecipeSpec() { return Combinators.combine( recipeProperties(), - shapedTemplate(), + shapedPattern(), MinecraftArbitraries.nonEmptyItemStack() ).as(ShapedRecipeSpec::new); } diff --git a/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeEqualities.java b/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeEqualities.java index c237c8686..1ab5fac23 100644 --- a/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeEqualities.java +++ b/projects/common/src/test/java/dan200/computercraft/shared/recipe/RecipeEqualities.java @@ -6,6 +6,7 @@ package dan200.computercraft.shared.recipe; import dan200.computercraft.test.core.StructuralEquality; import dan200.computercraft.test.shared.MinecraftEqualities; +import net.minecraft.world.item.crafting.ShapedRecipePattern; /** * {@link StructuralEquality} implementations for recipes. @@ -20,15 +21,15 @@ public final class RecipeEqualities { StructuralEquality.at("result", ShapelessRecipeSpec::result, MinecraftEqualities.itemStack) ); - public static final StructuralEquality shapedTemplate = StructuralEquality.all( - StructuralEquality.at("width", ShapedTemplate::width), - StructuralEquality.at("height", ShapedTemplate::height), - StructuralEquality.at("ingredients", ShapedTemplate::ingredients, MinecraftEqualities.ingredient.list()) + public static final StructuralEquality shapedPattern = StructuralEquality.all( + StructuralEquality.at("width", ShapedRecipePattern::width), + StructuralEquality.at("height", ShapedRecipePattern::height), + StructuralEquality.at("ingredients", ShapedRecipePattern::ingredients, MinecraftEqualities.ingredient.list()) ); public static final StructuralEquality shapedRecipeSpec = StructuralEquality.all( StructuralEquality.at("properties", ShapedRecipeSpec::properties), - StructuralEquality.at("ingredients", ShapedRecipeSpec::template, shapedTemplate), + StructuralEquality.at("ingredients", ShapedRecipeSpec::pattern, shapedPattern), StructuralEquality.at("result", ShapedRecipeSpec::result, MinecraftEqualities.itemStack) ); } diff --git a/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftArbitraries.java b/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftArbitraries.java index 176dfe04d..a293f550e 100644 --- a/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftArbitraries.java +++ b/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftArbitraries.java @@ -4,12 +4,12 @@ package dan200.computercraft.test.shared; -import dan200.computercraft.shared.platform.RegistryWrappers; import net.jqwik.api.Arbitraries; import net.jqwik.api.Arbitrary; import net.jqwik.api.Combinators; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; @@ -25,7 +25,7 @@ import java.util.List; * {@link Arbitrary} implementations for Minecraft types. */ public final class MinecraftArbitraries { - public static Arbitrary ofRegistry(RegistryWrappers.RegistryWrapper registry) { + public static Arbitrary ofRegistry(Registry registry) { return Arbitraries.of(registry.stream().toList()); } @@ -34,7 +34,7 @@ public final class MinecraftArbitraries { } public static Arbitrary item() { - return ofRegistry(RegistryWrappers.ITEMS); + return ofRegistry(BuiltInRegistries.ITEM); } public static Arbitrary nonEmptyItemStack() { diff --git a/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftEqualities.java b/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftEqualities.java index 8686bb250..a4bc6e4f4 100644 --- a/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftEqualities.java +++ b/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/MinecraftEqualities.java @@ -4,7 +4,11 @@ package dan200.computercraft.test.shared; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.mojang.serialization.JsonOps; import dan200.computercraft.test.core.StructuralEquality; +import net.minecraft.Util; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import org.hamcrest.Description; @@ -26,14 +30,18 @@ public class MinecraftEqualities { }; public static final StructuralEquality ingredient = new StructuralEquality<>() { + private static JsonElement toJson(Ingredient ingredient) { + return Util.getOrThrow(Ingredient.CODEC.encodeStart(JsonOps.INSTANCE, ingredient), JsonParseException::new); + } + @Override public boolean equals(Ingredient left, Ingredient right) { - return left.toJson().equals(right.toJson()); + return toJson(left).equals(toJson(right)); } @Override public void describe(Description description, Ingredient object) { - description.appendValue(object.toJson()); + description.appendValue(toJson(object)); } }; } diff --git a/projects/common/src/testMod/java/dan200/computercraft/export/Exporter.java b/projects/common/src/testMod/java/dan200/computercraft/export/Exporter.java index 266b6c669..d8e4db7c0 100644 --- a/projects/common/src/testMod/java/dan200/computercraft/export/Exporter.java +++ b/projects/common/src/testMod/java/dan200/computercraft/export/Exporter.java @@ -17,8 +17,9 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.data.PrettyJsonWriter; import dan200.computercraft.gametest.core.TestHooks; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.impl.RegistryHelper; import net.minecraft.client.Minecraft; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -74,23 +75,25 @@ public class Exporter { Set items = new HashSet<>(); // First find all CC items - for (var item : RegistryWrappers.ITEMS) { - if (RegistryWrappers.ITEMS.getKey(item).getNamespace().equals(ComputerCraftAPI.MOD_ID)) items.add(item); + for (var item : BuiltInRegistries.ITEM) { + if (RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).getNamespace().equals(ComputerCraftAPI.MOD_ID)) { + items.add(item); + } } // Now find all CC recipes. var level = Minecraft.getInstance().level; for (var recipe : level.getRecipeManager().getAllRecipesFor(RecipeType.CRAFTING)) { - var result = recipe.getResultItem(level.registryAccess()); - if (!RegistryWrappers.ITEMS.getKey(result.getItem()).getNamespace().equals(ComputerCraftAPI.MOD_ID)) { + var result = recipe.value().getResultItem(level.registryAccess()); + if (!RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, result.getItem()).getNamespace().equals(ComputerCraftAPI.MOD_ID)) { continue; } if (result.hasTag()) { - TestHooks.LOG.warn("Skipping recipe {} as it has NBT", recipe.getId()); + TestHooks.LOG.warn("Skipping recipe {} as it has NBT", recipe.id()); continue; } - if (recipe instanceof ShapedRecipe shaped) { + if (recipe.value() instanceof ShapedRecipe shaped) { var converted = new JsonDump.Recipe(result); for (var x = 0; x < shaped.getWidth(); x++) { @@ -102,8 +105,8 @@ public class Exporter { } } - dump.recipes.put(recipe.getId().toString(), converted); - } else if (recipe instanceof ShapelessRecipe shapeless) { + dump.recipes.put(recipe.id().toString(), converted); + } else if (recipe.value() instanceof ShapelessRecipe shapeless) { var converted = new JsonDump.Recipe(result); var ingredients = shapeless.getIngredients(); @@ -111,7 +114,7 @@ public class Exporter { converted.setInput(i, ingredients.get(i), items); } - dump.recipes.put(recipe.getId().toString(), converted); + dump.recipes.put(recipe.id().toString(), converted); } else { TestHooks.LOG.info("Don't know how to handle recipe {}", recipe); } @@ -126,7 +129,7 @@ public class Exporter { for (var item : items) { var stack = new ItemStack(item); - var location = RegistryWrappers.ITEMS.getKey(item); + var location = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item); dump.itemNames.put(location.toString(), stack.getHoverName().getString()); renderer.captureRender(itemDir.resolve(location.getNamespace()).resolve(location.getPath() + ".png"), diff --git a/projects/common/src/testMod/java/dan200/computercraft/export/JsonDump.java b/projects/common/src/testMod/java/dan200/computercraft/export/JsonDump.java index 474c879bf..aa996c905 100644 --- a/projects/common/src/testMod/java/dan200/computercraft/export/JsonDump.java +++ b/projects/common/src/testMod/java/dan200/computercraft/export/JsonDump.java @@ -4,13 +4,17 @@ package dan200.computercraft.export; -import dan200.computercraft.shared.platform.RegistryWrappers; +import dan200.computercraft.impl.RegistryHelper; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Ingredient; -import java.util.*; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; public class JsonDump { public Map itemNames = new TreeMap<>(); @@ -22,7 +26,7 @@ public class JsonDump { public int count; public Recipe(ItemStack output) { - this.output = RegistryWrappers.ITEMS.getKey(output.getItem()).toString(); + this.output = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, output.getItem()).toString(); count = output.getCount(); } @@ -37,7 +41,7 @@ public class JsonDump { if (!canonicalItem.contains(item)) continue; trackedItems.add(item); - inputs[pos] = new String[]{ RegistryWrappers.ITEMS.getKey(item).toString() }; + inputs[pos] = new String[]{ RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).toString() }; return; } @@ -45,7 +49,7 @@ public class JsonDump { for (var i = 0; i < items.length; i++) { var item = items[i].getItem(); trackedItems.add(item); - itemIds[i] = RegistryWrappers.ITEMS.getKey(item).toString(); + itemIds[i] = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).toString(); } Arrays.sort(itemIds); diff --git a/projects/common/src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java b/projects/common/src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java index 754cbe438..d6ff5e364 100644 --- a/projects/common/src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java +++ b/projects/common/src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java @@ -63,7 +63,7 @@ class CCTestCommand { var structureBlock = (StructureBlockEntity) player.level().getBlockEntity(pos); if (structureBlock == null) return error(context.getSource(), "No nearby structure block"); - var info = GameTestRegistry.getTestFunction(structureBlock.getStructurePath()); + var info = GameTestRegistry.getTestFunction(structureBlock.getMetaData()); // Kill the existing armor stand player @@ -89,14 +89,14 @@ class CCTestCommand { var structureBlock = (StructureBlockEntity) player.level().getBlockEntity(pos); if (structureBlock == null) return error(context.getSource(), "No nearby structure block"); - var info = GameTestRegistry.getTestFunction(structureBlock.getStructurePath()); + var info = GameTestRegistry.getTestFunction(structureBlock.getMetaData()); var item = ModRegistry.Items.COMPUTER_ADVANCED.get().create(1, info.getTestName()); if (!player.getInventory().add(item)) { var itemEntity = player.drop(item, false); if (itemEntity != null) { itemEntity.setNoPickUpDelay(); - itemEntity.setThrower(player.getUUID()); + itemEntity.setThrower(player); } } diff --git a/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/StructureTemplateManagerMixin.java b/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/StructureTemplateManagerMixin.java new file mode 100644 index 000000000..0e6fe611f --- /dev/null +++ b/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/StructureTemplateManagerMixin.java @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.mixin.gametest; + +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(StructureTemplateManager.class) +class StructureTemplateManagerMixin { + /** + * Ensure {@link net.minecraft.SharedConstants#IS_RUNNING_IN_IDE} is always true, meaning the test structure loader + * is always present. + * + * @return A constant {@code true}. + */ + @SuppressWarnings("UnusedMethod") + @Redirect(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;IS_RUNNING_IN_IDE:Z")) + private boolean getRunningInIde() { + return true; + } +} diff --git a/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/MinecraftMixin.java b/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/MinecraftMixin.java index 52ab5c742..bab1b7941 100644 --- a/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/MinecraftMixin.java +++ b/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/MinecraftMixin.java @@ -42,8 +42,8 @@ class MinecraftMixin implements MinecraftExtensions { private void updateStable(boolean render, CallbackInfo ci) { isStable.set( level != null && player != null && - levelRenderer.isChunkCompiled(player.blockPosition()) && levelRenderer.countRenderedChunks() > 10 && - levelRenderer.hasRenderedAllChunks() + levelRenderer.isSectionCompiled(player.blockPosition()) && levelRenderer.countRenderedSections() > 10 && + levelRenderer.hasRenderedAllSections() ); } diff --git a/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/WorldOpenFlowsMixin.java b/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/WorldOpenFlowsMixin.java index ec3c861b7..ecfef1525 100644 --- a/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/WorldOpenFlowsMixin.java +++ b/projects/common/src/testMod/java/dan200/computercraft/mixin/gametest/client/WorldOpenFlowsMixin.java @@ -4,8 +4,8 @@ package dan200.computercraft.mixin.gametest.client; -import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows; +import net.minecraft.world.level.storage.LevelStorageSource; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; @@ -14,16 +14,16 @@ public class WorldOpenFlowsMixin { /** * Never prompt for backup/experimental options when running tests. * - * @param screen The current menu. - * @param level The level to load. + * @param access The current menu. * @param customised Whether this rule uses legacy customised worldgen options. - * @param action The action run to load the world. + * @param load The action run to load the world. + * @param cancel The action run to abort loading the world. * @author SquidDev * @reason Makes it easier to run tests. We can switch to an @Inject if this becomes a problem. */ @Overwrite @SuppressWarnings("UnusedMethod") - private void askForBackup(Screen screen, String level, boolean customised, Runnable action) { - action.run(); + private void askForBackup(LevelStorageSource.LevelStorageAccess access, boolean customised, Runnable load, Runnable cancel) { + load.run(); } } diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Details_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Details_Test.kt new file mode 100644 index 000000000..1e7c2303f --- /dev/null +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Details_Test.kt @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.gametest + +import dan200.computercraft.api.detail.VanillaDetailRegistries +import dan200.computercraft.gametest.api.Structures +import dan200.computercraft.gametest.api.sequence +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import org.junit.jupiter.api.Assertions.assertEquals + +/** + * Test our detail providers ([VanillaDetailRegistries]). + */ +class Details_Test { + /** + * Assert that items have their creative tabs availab.e + */ + @GameTest(template = Structures.DEFAULT) + fun Has_item_groups(helper: GameTestHelper) = helper.sequence { + thenExecute { + val details = VanillaDetailRegistries.ITEM_STACK.getDetails(ItemStack(Items.DIRT)) + assertEquals( + listOf( + mapOf( + "displayName" to "Natural Blocks", + "id" to "minecraft:natural_blocks", + ), + ), + details["itemGroups"], + ) + } + } +} diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt index 2c65cfe92..7b24528db 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt @@ -40,7 +40,7 @@ class Recipe_Test { .getRecipeFor(RecipeType.CRAFTING, container, context.level) .orElseThrow { GameTestAssertException("No recipe matches") } - val result = recipe.assemble(container, context.level.registryAccess()) + val result = recipe.value.assemble(container, context.level.registryAccess()) val profile = GameProfile(UUID.fromString("f3c8d69b-0776-4512-8434-d1b2165909eb"), "dan200") diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/ClientTestExtensions.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/ClientTestExtensions.kt index b7f105612..1df5dcd68 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/ClientTestExtensions.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/ClientTestExtensions.kt @@ -5,12 +5,13 @@ package dan200.computercraft.gametest.api import dan200.computercraft.gametest.core.MinecraftExtensions +import dan200.computercraft.impl.RegistryHelper import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor -import dan200.computercraft.shared.platform.RegistryWrappers import net.minecraft.client.Minecraft import net.minecraft.client.Screenshot import net.minecraft.client.gui.screens.inventory.MenuAccess import net.minecraft.core.BlockPos +import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.gametest.framework.GameTestAssertException import net.minecraft.gametest.framework.GameTestHelper import net.minecraft.gametest.framework.GameTestSequence @@ -121,7 +122,7 @@ class ClientTestHelper { * Get the currently open [AbstractContainerMenu], ensuring it is of a specific type. */ fun getOpenMenu(type: MenuType): T { - fun getName(type: MenuType<*>) = RegistryWrappers.MENU.getKey(type) + fun getName(type: MenuType<*>) = RegistryHelper.getKeyOrThrow(BuiltInRegistries.MENU, type) val screen = minecraft.screen @Suppress("UNCHECKED_CAST") diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt index 2d68bb8aa..60921532a 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt @@ -6,16 +6,17 @@ package dan200.computercraft.gametest.api import dan200.computercraft.api.peripheral.IPeripheral import dan200.computercraft.gametest.core.ManagedComputers +import dan200.computercraft.impl.RegistryHelper import dan200.computercraft.mixin.gametest.GameTestHelperAccessor import dan200.computercraft.mixin.gametest.GameTestInfoAccessor import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor import dan200.computercraft.shared.platform.PlatformHelper -import dan200.computercraft.shared.platform.RegistryWrappers import dan200.computercraft.test.core.computer.LuaTaskContext import dan200.computercraft.test.shared.ItemStackMatcher.isStack import net.minecraft.commands.arguments.blocks.BlockInput import net.minecraft.core.BlockPos import net.minecraft.core.Direction +import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.gametest.framework.* import net.minecraft.resources.ResourceLocation import net.minecraft.world.Container @@ -161,7 +162,7 @@ fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boole fun > GameTestHelper.assertBlockHas(pos: BlockPos, property: Property, value: T, message: String = "") { val state = getBlockState(pos) if (!state.hasProperty(property)) { - val id = RegistryWrappers.BLOCKS.getKey(state.block) + val id = RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK, state.block) fail(message, "block $id does not have property ${property.name}", pos) } else if (state.getValue(property) != value) { fail(message, "${property.name} is ${state.getValue(property)}, expected $value", pos) @@ -248,7 +249,8 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin } } -private fun getName(type: BlockEntityType<*>): ResourceLocation = RegistryWrappers.BLOCK_ENTITY_TYPES.getKey(type)!! +private fun getName(type: BlockEntityType<*>): ResourceLocation = + RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK_ENTITY_TYPE, type) /** * Get a [BlockEntity] of a specific type. diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ClientTestHooks.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ClientTestHooks.kt index 236247a8e..e63c81d5c 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ClientTestHooks.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ClientTestHooks.kt @@ -56,7 +56,7 @@ object ClientTestHooks { fun onOpenScreen(screen: Screen): Boolean = when { enabled && !loadedWorld && (screen is TitleScreen || screen is AccessibilityOnboardingScreen) -> { loadedWorld = true - openWorld() + openWorld(screen) true } @@ -66,7 +66,7 @@ object ClientTestHooks { /** * Open or create our test world immediately on game launch. */ - private fun openWorld() { + private fun openWorld(screen: Screen) { val minecraft = Minecraft.getInstance() // Clear some options before we get any further. @@ -81,7 +81,7 @@ object ClientTestHooks { if (minecraft.levelSource.levelExists(LEVEL_NAME)) { LOG.info("World already exists, opening.") - minecraft.createWorldOpenFlows().loadLevel(minecraft.screen, LEVEL_NAME) + minecraft.createWorldOpenFlows().checkForBackupAndLoad(LEVEL_NAME) { minecraft.setScreen(screen) } } else { LOG.info("World does not exist, creating it.") val rules = GameRules() @@ -93,7 +93,9 @@ object ClientTestHooks { LEVEL_NAME, LevelSettings("Test Level", GameType.CREATIVE, false, Difficulty.EASY, true, rules, WorldDataConfiguration.DEFAULT), WorldOptions(WorldOptions.randomSeed(), false, false), - ) { WorldPresets.createNormalWorldDimensions(it) } + { WorldPresets.createNormalWorldDimensions(it) }, + screen, + ) } } @@ -171,7 +173,7 @@ object ClientTestHooks { minecraft.execute { LOG.info("Stopping client.") minecraft.level!!.disconnect() - minecraft.clearLevel() + minecraft.disconnect() minecraft.stop() exitProcess( diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt index c3a418d30..ea15e3154 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt @@ -77,6 +77,7 @@ object TestHooks { private val testClasses = listOf( Computer_Test::class.java, CraftOs_Test::class.java, + Details_Test::class.java, Disk_Drive_Test::class.java, Inventory_Test::class.java, Loot_Test::class.java, diff --git a/projects/common/src/testMod/resources/computercraft-gametest.mixins.json b/projects/common/src/testMod/resources/computercraft-gametest.mixins.json index 2f2f797a6..a4c662e82 100644 --- a/projects/common/src/testMod/resources/computercraft-gametest.mixins.json +++ b/projects/common/src/testMod/resources/computercraft-gametest.mixins.json @@ -11,6 +11,7 @@ "GameTestInfoAccessor", "GameTestSequenceAccessor", "GameTestSequenceMixin", + "StructureTemplateManagerMixin", "TestCommandAccessor" ], "client": [ diff --git a/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/MountConstants.java b/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/MountConstants.java index 6992fe761..6fc72deeb 100644 --- a/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/MountConstants.java +++ b/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/MountConstants.java @@ -59,7 +59,7 @@ public final class MountConstants { public static final String FILE_EXISTS = "File exists"; /** - * The error message used when trying to {@linkplain WritableMount#openForWrite(String) opening a directory to read}. + * The error message used when trying to open a directory to read. */ public static final String CANNOT_WRITE_TO_DIRECTORY = "Cannot write to directory"; diff --git a/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/WritableMount.java b/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/WritableMount.java index 918ae3402..1cb502910 100644 --- a/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/WritableMount.java +++ b/projects/core-api/src/main/java/dan200/computercraft/api/filesystem/WritableMount.java @@ -55,28 +55,6 @@ public interface WritableMount extends Mount { */ void rename(String source, String dest) throws IOException; - /** - * Opens a file with a given path, and returns an {@link SeekableByteChannel} for writing to it. - * - * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram". - * @return A channel for writing to. - * @throws IOException If the file could not be opened for writing. - * @deprecated Replaced with more the generic {@link #openFile(String, Set)}. - */ - @Deprecated(forRemoval = true) - SeekableByteChannel openForWrite(String path) throws IOException; - - /** - * Opens a file with a given path, and returns an {@link SeekableByteChannel} for appending to it. - * - * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram". - * @return A channel for writing to. - * @throws IOException If the file could not be opened for writing. - * @deprecated Replaced with more the generic {@link #openFile(String, Set)}. - */ - @Deprecated(forRemoval = true) - SeekableByteChannel openForAppend(String path) throws IOException; - /** * Opens a file with a given path, and returns an {@link SeekableByteChannel}. *

@@ -95,12 +73,7 @@ public interface WritableMount extends Mount { * @return A channel for writing to. * @throws IOException If the file could not be opened for writing. */ - default SeekableByteChannel openFile(String path, Set options) throws IOException { - if (options.equals(MountConstants.READ_OPTIONS)) return openForRead(path); - if (options.equals(MountConstants.WRITE_OPTIONS)) return openForWrite(path); - if (options.equals(MountConstants.APPEND_OPTIONS)) return openForAppend(path); - throw new IOException(MountConstants.UNSUPPORTED_MODE); - } + SeekableByteChannel openFile(String path, Set options) throws IOException; /** * Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the diff --git a/projects/core-api/src/main/java/dan200/computercraft/api/lua/IArguments.java b/projects/core-api/src/main/java/dan200/computercraft/api/lua/IArguments.java index fa0018ef7..5acf256c4 100644 --- a/projects/core-api/src/main/java/dan200/computercraft/api/lua/IArguments.java +++ b/projects/core-api/src/main/java/dan200/computercraft/api/lua/IArguments.java @@ -477,7 +477,7 @@ public interface IArguments { } /** - * Create a version of these arguments which escapes the scope of the current function call. + * Mark these arguments as escaping the scope of the current function call. *

* Some {@link IArguments} implementations provide a view over the underlying Lua data structures, allowing for * zero-copy implementations of some methods (such as {@link #getTableUnsafe(int)} or {@link #getBytes(int)}). @@ -491,12 +491,9 @@ public interface IArguments { * {@link ILuaContext#issueMainThreadTask(LuaTask)} (or similar), then you will need to mark arguments as escaping * yourself. * - * @return An {@link IArguments} instance which can escape the current scope. May be {@code this}. * @throws LuaException For the same reasons as {@link #get(int)}. * @throws IllegalStateException If marking these arguments as escaping outside the scope of the original function. */ - default IArguments escapes() throws LuaException { - // TODO(1.21.0): Make this return void, require that it mutates this. - return this; + default void escapes() throws LuaException { } } diff --git a/projects/core/src/main/java/dan200/computercraft/core/asm/LuaMethodSupplier.java b/projects/core/src/main/java/dan200/computercraft/core/asm/LuaMethodSupplier.java index 86a634be6..07ec56fc7 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/asm/LuaMethodSupplier.java +++ b/projects/core/src/main/java/dan200/computercraft/core/asm/LuaMethodSupplier.java @@ -30,8 +30,8 @@ public final class LuaMethodSupplier { } }, m -> (target, context, args) -> { - var escArgs = args.escapes(); - return context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, escArgs))); + args.escapes(); + return context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, args))); } ); private static final IntCache DYNAMIC = new IntCache<>( diff --git a/projects/core/src/main/java/dan200/computercraft/core/asm/PeripheralMethodSupplier.java b/projects/core/src/main/java/dan200/computercraft/core/asm/PeripheralMethodSupplier.java index c282494f1..7b986d5db 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/asm/PeripheralMethodSupplier.java +++ b/projects/core/src/main/java/dan200/computercraft/core/asm/PeripheralMethodSupplier.java @@ -31,8 +31,8 @@ public final class PeripheralMethodSupplier { } }, m -> (target, context, computer, args) -> { - var escArgs = args.escapes(); - return context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, computer, escArgs))); + args.escapes(); + return context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, computer, args))); } ); private static final IntCache DYNAMIC = new IntCache<>( diff --git a/projects/core/src/main/java/dan200/computercraft/core/filesystem/MemoryMount.java b/projects/core/src/main/java/dan200/computercraft/core/filesystem/MemoryMount.java index c8e09a586..4df8b4b55 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/filesystem/MemoryMount.java +++ b/projects/core/src/main/java/dan200/computercraft/core/filesystem/MemoryMount.java @@ -148,18 +148,6 @@ public final class MemoryMount extends AbstractInMemoryMount options) throws IOException { var flags = FileFlags.of(options); diff --git a/projects/core/src/main/java/dan200/computercraft/core/filesystem/WritableFileMount.java b/projects/core/src/main/java/dan200/computercraft/core/filesystem/WritableFileMount.java index 390e7899e..69f6c5d62 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/filesystem/WritableFileMount.java +++ b/projects/core/src/main/java/dan200/computercraft/core/filesystem/WritableFileMount.java @@ -155,18 +155,6 @@ public class WritableFileMount extends FileMount implements WritableMount { } } - @Override - @Deprecated(forRemoval = true) - public SeekableByteChannel openForWrite(String path) throws IOException { - return openFile(path, WRITE_OPTIONS); - } - - @Override - @Deprecated(forRemoval = true) - public SeekableByteChannel openForAppend(String path) throws IOException { - return openFile(path, APPEND_OPTIONS); - } - @Override public SeekableByteChannel openFile(String path, Set options) throws IOException { var flags = FileFlags.of(options); diff --git a/projects/core/src/main/java/dan200/computercraft/core/lua/VarargArguments.java b/projects/core/src/main/java/dan200/computercraft/core/lua/VarargArguments.java index f8ecee139..d49d8d490 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/lua/VarargArguments.java +++ b/projects/core/src/main/java/dan200/computercraft/core/lua/VarargArguments.java @@ -196,8 +196,8 @@ final class VarargArguments implements IArguments { } @Override - public IArguments escapes() { - if (escapes) return this; + public void escapes() { + if (escapes) return; if (isClosed()) throw new IllegalStateException("Cannot call escapes after IArguments has been closed."); var cache = this.cache; @@ -223,7 +223,6 @@ final class VarargArguments implements IArguments { escapes = true; this.cache = cache; this.typeNames = typeNames; - return this; } void close() { diff --git a/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java b/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java index 0082aae5a..b38b532f1 100644 --- a/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java +++ b/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java @@ -6,14 +6,13 @@ package dan200.computercraft.api.client; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; -import dan200.computercraft.impl.client.ComputerCraftAPIClientService; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.impl.client.FabricComputerCraftAPIClientService; /** - * The Fabric-specific entrypoint for ComputerCraft's API. + * The Fabric-specific entrypoint for ComputerCraft's client-side API. * * @see dan200.computercraft.api.ComputerCraftAPI The main API - * @see dan200.computercraft.api.client.ComputerCraftAPIClient The main client-side API */ public final class FabricComputerCraftAPIClient { private FabricComputerCraftAPIClient() { @@ -32,11 +31,11 @@ public final class FabricComputerCraftAPIClient { * @param modeller The upgrade modeller. * @param The type of the turtle upgrade. */ - public static void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { + public static void registerTurtleUpgradeModeller(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { getInstance().registerTurtleUpgradeModeller(serialiser, modeller); } - private static ComputerCraftAPIClientService getInstance() { - return ComputerCraftAPIClientService.get(); + private static FabricComputerCraftAPIClientService getInstance() { + return FabricComputerCraftAPIClientService.get(); } } diff --git a/projects/common-api/src/client/java/dan200/computercraft/impl/client/ComputerCraftAPIClientService.java b/projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java similarity index 58% rename from projects/common-api/src/client/java/dan200/computercraft/impl/client/ComputerCraftAPIClientService.java rename to projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java index a56e0a044..280c30762 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/impl/client/ComputerCraftAPIClientService.java +++ b/projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java @@ -4,35 +4,34 @@ package dan200.computercraft.impl.client; -import dan200.computercraft.api.client.ComputerCraftAPIClient; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.impl.Services; import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nullable; /** - * Backing interface for {@link ComputerCraftAPIClient} + * Backing interface for CC's client-side API. *

* Do NOT directly reference this class. It exists for internal use by the API. */ @ApiStatus.Internal -public interface ComputerCraftAPIClientService { - static ComputerCraftAPIClientService get() { +public interface FabricComputerCraftAPIClientService { + static FabricComputerCraftAPIClientService get() { var instance = Instance.INSTANCE; - return instance == null ? Services.raise(ComputerCraftAPIClientService.class, Instance.ERROR) : instance; + return instance == null ? Services.raise(FabricComputerCraftAPIClientService.class, Instance.ERROR) : instance; } - void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller); + void registerTurtleUpgradeModeller(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller); final class Instance { - static final @Nullable ComputerCraftAPIClientService INSTANCE; + static final @Nullable FabricComputerCraftAPIClientService INSTANCE; static final @Nullable Throwable ERROR; static { - var helper = Services.tryLoad(ComputerCraftAPIClientService.class); + var helper = Services.tryLoad(FabricComputerCraftAPIClientService.class); INSTANCE = helper.instance(); ERROR = helper.error(); } diff --git a/projects/fabric-api/src/main/java/dan200/computercraft/api/node/wired/WiredElementLookup.java b/projects/fabric-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementLookup.java similarity index 89% rename from projects/fabric-api/src/main/java/dan200/computercraft/api/node/wired/WiredElementLookup.java rename to projects/fabric-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementLookup.java index a02d086db..73cde4d01 100644 --- a/projects/fabric-api/src/main/java/dan200/computercraft/api/node/wired/WiredElementLookup.java +++ b/projects/fabric-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementLookup.java @@ -2,10 +2,9 @@ // // SPDX-License-Identifier: MPL-2.0 -package dan200.computercraft.api.node.wired; +package dan200.computercraft.api.network.wired; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.network.wired.WiredElement; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java b/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java index ec16c4ecb..e8a1bdaa2 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java @@ -26,6 +26,7 @@ import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.event.client.player.ClientPickBlockGatherCallback; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.MenuScreens; import net.minecraft.client.renderer.RenderType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.BlockHitResult; @@ -45,6 +46,7 @@ public class ComputerCraftClient { ClientRegistry.register(); ClientRegistry.registerTurtleModellers(FabricComputerCraftAPIClient::registerTurtleUpgradeModeller); ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register); + ClientRegistry.registerMenuScreens(MenuScreens::register); ClientRegistry.registerMainThread(); PreparableModelLoadingPlugin.register(CustomModelLoader::prepare, (state, context) -> { diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java b/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java index 09d278bf4..5727e1c19 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java @@ -6,10 +6,16 @@ package dan200.computercraft.client.integration.rei; import dan200.computercraft.shared.integration.UpgradeRecipeGenerator; import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; +import me.shedaniel.rei.api.common.display.basic.BasicDisplay; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; +import me.shedaniel.rei.api.common.util.EntryIngredients; import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay; +import me.shedaniel.rei.plugin.common.displays.crafting.DefaultShapedDisplay; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.ShapedRecipe; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -17,7 +23,7 @@ import java.util.Optional; * Provides custom recipe and usage hints for pocket/turtle upgrades. */ class UpgradeDisplayGenerator implements DynamicDisplayGenerator> { - private final UpgradeRecipeGenerator> resolver = new UpgradeRecipeGenerator<>(DefaultCraftingDisplay::of); + private final UpgradeRecipeGenerator> resolver = new UpgradeRecipeGenerator<>(GeneratedShapedDisplay::new); @Override public Optional>> getRecipeFor(EntryStack entry) { @@ -28,4 +34,31 @@ class UpgradeDisplayGenerator implements DynamicDisplayGenerator>> getUsageFor(EntryStack entry) { return entry.getType() == VanillaEntryTypes.ITEM ? Optional.of(resolver.findRecipesWithInput(entry.castValue())) : Optional.empty(); } + + /** + * Similar to {@link DefaultShapedDisplay}, but does not require a {@link RecipeHolder}. + */ + private static class GeneratedShapedDisplay extends DefaultCraftingDisplay { + private final int width, height; + + GeneratedShapedDisplay(ShapedRecipe recipe) { + super( + EntryIngredients.ofIngredients(recipe.getIngredients()), + Collections.singletonList(EntryIngredients.of(recipe.getResultItem(BasicDisplay.registryAccess()))), + Optional.empty() + ); + width = recipe.getWidth(); + height = recipe.getHeight(); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + } } diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java index 8452d9e58..4d01e7256 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java @@ -12,7 +12,6 @@ import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.platform.FabricMessageType; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; @@ -22,7 +21,7 @@ import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.network.protocol.common.ServerCommonPacketListener; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.RandomSource; @@ -34,10 +33,8 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper { private static final RandomSource random = RandomSource.create(0); @Override - public Packet createPacket(NetworkMessage message) { - var buf = PacketByteBufs.create(); - message.write(buf); - return ClientPlayNetworking.createC2SPacket(FabricMessageType.toFabricType(message.type()).getId(), buf); + public Packet createPacket(NetworkMessage message) { + return ClientPlayNetworking.createC2SPacket(FabricMessageType.toFabricPacket(message)); } @Override diff --git a/projects/common/src/client/java/dan200/computercraft/client/ComputerCraftAPIClientImpl.java b/projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java similarity index 55% rename from projects/common/src/client/java/dan200/computercraft/client/ComputerCraftAPIClientImpl.java rename to projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java index 667f034bc..83202e7f7 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/ComputerCraftAPIClientImpl.java +++ b/projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java @@ -2,19 +2,18 @@ // // SPDX-License-Identifier: MPL-2.0 -package dan200.computercraft.client; +package dan200.computercraft.impl.client; import com.google.auto.service.AutoService; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.client.turtle.TurtleUpgradeModellers; -import dan200.computercraft.impl.client.ComputerCraftAPIClientService; -@AutoService(ComputerCraftAPIClientService.class) -public final class ComputerCraftAPIClientImpl implements ComputerCraftAPIClientService { +@AutoService(FabricComputerCraftAPIClientService.class) +public final class FabricComputerCraftAPIClientImpl implements FabricComputerCraftAPIClientService { @Override - public void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { + public void registerTurtleUpgradeModeller(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { TurtleUpgradeModellers.register(serialiser, modeller); } } diff --git a/projects/fabric/src/client/java/dan200/computercraft/mixin/client/MinecraftMixin.java b/projects/fabric/src/client/java/dan200/computercraft/mixin/client/MinecraftMixin.java index 262439220..a6697f705 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/mixin/client/MinecraftMixin.java +++ b/projects/fabric/src/client/java/dan200/computercraft/mixin/client/MinecraftMixin.java @@ -7,7 +7,6 @@ package dan200.computercraft.mixin.client; import dan200.computercraft.client.ClientHooks; import dan200.computercraft.client.ClientRegistry; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.main.GameConfig; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.server.packs.resources.ReloadableResourceManager; @@ -24,15 +23,9 @@ class MinecraftMixin { @Final private ReloadableResourceManager resourceManager; - @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At("HEAD")) + @Inject(method = "updateLevelInEngines", at = @At("HEAD")) @SuppressWarnings("UnusedMethod") - private void clearLevel(Screen screen, CallbackInfo ci) { - ClientHooks.onWorldUnload(); - } - - @Inject(method = "setLevel", at = @At("HEAD")) - @SuppressWarnings("UnusedMethod") - private void setLevel(ClientLevel screen, CallbackInfo ci) { + private void updateLevelInEngines(ClientLevel screen, CallbackInfo ci) { ClientHooks.onWorldUnload(); } @@ -44,7 +37,8 @@ class MinecraftMixin { ordinal = 0 ) ) - public void beforeInitialResourceReload(GameConfig gameConfig, CallbackInfo ci) { + @SuppressWarnings("UnusedMethod") + private void beforeInitialResourceReload(GameConfig gameConfig, CallbackInfo ci) { ClientRegistry.registerReloadListeners(resourceManager::registerReloadListener, (Minecraft) (Object) this); } } diff --git a/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java b/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java index 82e4671a5..706dcaa67 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java +++ b/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java @@ -5,7 +5,6 @@ package dan200.computercraft.data; import com.mojang.serialization.Codec; -import dan200.computercraft.shared.platform.RegistryWrappers; import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; @@ -14,6 +13,7 @@ import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider; import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; @@ -93,7 +93,7 @@ public class FabricDataGenerators implements DataGeneratorEntrypoint { return addWithRegistries((out, registries) -> new FabricTagProvider.BlockTagProvider(out, registries) { @Override protected void addTags(HolderLookup.Provider registries) { - tags.accept(x -> new TagProvider.TagAppender<>(RegistryWrappers.BLOCKS, getOrCreateRawBuilder(x))); + tags.accept(x -> new TagProvider.TagAppender<>(BuiltInRegistries.BLOCK, getOrCreateRawBuilder(x))); } }); } @@ -107,7 +107,7 @@ public class FabricDataGenerators implements DataGeneratorEntrypoint { tags.accept(new TagProvider.ItemTagConsumer() { @Override public TagProvider.TagAppender tag(TagKey tag) { - return new TagProvider.TagAppender<>(RegistryWrappers.ITEMS, getOrCreateRawBuilder(tag)); + return new TagProvider.TagAppender<>(BuiltInRegistries.ITEM, getOrCreateRawBuilder(tag)); } @Override diff --git a/projects/fabric/src/main/java/dan200/computercraft/mixin/ChunkMapMixin.java b/projects/fabric/src/main/java/dan200/computercraft/mixin/ChunkMapMixin.java deleted file mode 100644 index 1835ed109..000000000 --- a/projects/fabric/src/main/java/dan200/computercraft/mixin/ChunkMapMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.mixin; - -import dan200.computercraft.shared.CommonHooks; -import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.chunk.LevelChunk; -import org.apache.commons.lang3.mutable.MutableObject; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(ChunkMap.class) -class ChunkMapMixin { - @Inject(method = "playerLoadedChunk", at = @At("TAIL")) - @SuppressWarnings("UnusedMethod") - private void onPlayerLoadedChunk(ServerPlayer player, MutableObject packetCache, LevelChunk chunk, CallbackInfo callback) { - CommonHooks.onChunkWatch(chunk, player); - } -} diff --git a/projects/fabric/src/main/java/dan200/computercraft/mixin/PlayerChunkSenderMixin.java b/projects/fabric/src/main/java/dan200/computercraft/mixin/PlayerChunkSenderMixin.java new file mode 100644 index 000000000..36a41fdd6 --- /dev/null +++ b/projects/fabric/src/main/java/dan200/computercraft/mixin/PlayerChunkSenderMixin.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.mixin; + +import dan200.computercraft.shared.CommonHooks; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.network.PlayerChunkSender; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.chunk.LevelChunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerChunkSender.class) +class PlayerChunkSenderMixin { + @Inject(method = "sendChunk", at = @At("TAIL")) + @SuppressWarnings("UnusedMethod") + private static void onPlayerLoadedChunk(ServerGamePacketListenerImpl connection, ServerLevel server, LevelChunk chunk, CallbackInfo ci) { + CommonHooks.onChunkWatch(chunk, connection.player); + } +} diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java index d0e6f2ab4..5a99d3d1b 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java @@ -6,7 +6,7 @@ package dan200.computercraft.shared; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.detail.FabricDetailRegistries; -import dan200.computercraft.api.node.wired.WiredElementLookup; +import dan200.computercraft.api.network.wired.WiredElementLookup; import dan200.computercraft.api.peripheral.PeripheralLookup; import dan200.computercraft.impl.Peripherals; import dan200.computercraft.shared.command.CommandComputerCraft; @@ -33,6 +33,7 @@ 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.fabricmc.loader.api.FabricLoader; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; @@ -85,13 +86,17 @@ public class ComputerCraft { // Register hooks ServerLifecycleEvents.SERVER_STARTING.register(server -> { - ((FabricConfigFile) ConfigSpec.serverSpec).load(server.getWorldPath(SERVERCONFIG).resolve(ComputerCraftAPI.MOD_ID + "-server.toml")); + ((FabricConfigFile) ConfigSpec.serverSpec).load( + server.getWorldPath(SERVERCONFIG).resolve(ComputerCraftAPI.MOD_ID + "-server.toml"), + FabricLoader.getInstance().getConfigDir().resolve(ComputerCraftAPI.MOD_ID + "-server.toml") + ); CommonHooks.onServerStarting(server); }); ServerLifecycleEvents.SERVER_STOPPED.register(s -> { CommonHooks.onServerStopped(); ((FabricConfigFile) ConfigSpec.serverSpec).unload(); }); + ServerLifecycleEvents.SERVER_STARTED.register(CommonHooks::onServerStarted); ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> ServerNetworking.sendToPlayer(new UpgradesLoadedMessage(), player)); ServerTickEvents.START_SERVER_TICK.register(CommonHooks::onServerTickStart); @@ -116,7 +121,7 @@ public class ComputerCraft { ComputerCraftAPI.registerGenericSource(new InventoryMethods()); - Peripherals.addGenericLookup((world, pos, state, blockEntity, side, invalidate) -> InventoryMethods.extractContainer(world, pos, state, blockEntity, side)); + Peripherals.addGenericLookup(InventoryMethods::extractContainer); } private record ReloadListener(String name, PreparableReloadListener listener) diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/details/FluidDetails.java b/projects/fabric/src/main/java/dan200/computercraft/shared/details/FluidDetails.java index 57318851b..326398227 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/details/FluidDetails.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/details/FluidDetails.java @@ -6,9 +6,9 @@ package dan200.computercraft.shared.details; import dan200.computercraft.api.detail.DetailProvider; import dan200.computercraft.api.detail.FabricDetailRegistries; -import dan200.computercraft.shared.platform.RegistryWrappers; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; +import net.minecraft.core.registries.BuiltInRegistries; import java.util.Map; @@ -19,7 +19,7 @@ import java.util.Map; */ public class FluidDetails { public static void fillBasic(Map data, StorageView fluid) { - data.put("name", DetailHelpers.getId(RegistryWrappers.FLUIDS, fluid.getResource().getFluid())); + data.put("name", DetailHelpers.getId(BuiltInRegistries.FLUID, fluid.getResource().getFluid())); data.put("amount", fluid.getAmount()); } diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java index 0536d8e27..3158bc81a 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java @@ -44,9 +44,20 @@ public class FabricConfigFile implements ConfigFile { this.onChange = onChange; } - public synchronized void load(Path path) { + /** + * Load the config from one or more possible paths. + *

+ * Config files will be loaded from the first path which exists. If none exists, a file at the last location will + * be chosen. + * + * @param paths The list of paths to load from. + */ + public synchronized void load(Path... paths) { + if (paths.length == 0) throw new IllegalArgumentException("Must pass at least one path"); + closeConfig(); + var path = Arrays.stream(paths).filter(Files::exists).findFirst().orElseGet(() -> paths[paths.length - 1]); var config = this.config = CommentedFileConfig.builder(path).sync() .onFileNotFound(FileNotFoundAction.READ_NOTHING) .writingMode(WritingMode.REPLACE) diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricMessageType.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricMessageType.java index d10832cca..885b2d971 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricMessageType.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricMessageType.java @@ -29,6 +29,11 @@ public record FabricMessageType>( this(PacketType.create(id, b -> new PacketWrapper<>(reader.apply(b)))); } + @Override + public ResourceLocation id() { + return type().getId(); + } + public static > PacketType> toFabricType(MessageType type) { return ((FabricMessageType) type).type(); } diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 3ed6f52c0..1187a03e1 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -11,7 +11,7 @@ import com.mojang.authlib.GameProfile; import com.mojang.brigadier.arguments.ArgumentType; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.network.wired.WiredElement; -import dan200.computercraft.api.node.wired.WiredElementLookup; +import dan200.computercraft.api.network.wired.WiredElementLookup; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.PeripheralLookup; import dan200.computercraft.impl.Peripherals; @@ -28,7 +28,6 @@ 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; @@ -48,7 +47,7 @@ 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.network.protocol.common.ClientCommonPacketListener; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; @@ -81,7 +80,6 @@ import net.minecraft.world.phys.Vec3; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.function.*; @@ -105,36 +103,11 @@ public class PlatformHelperImpl implements PlatformHelper { return registry; } - @Override - public ResourceLocation getRegistryKey(ResourceKey> registry, T object) { - var key = getRegistry(registry).getKey(object); - if (key == null) throw new IllegalArgumentException(object + " was not registered in " + registry); - return key; - } - - @Override - public T getRegistryObject(ResourceKey> registry, ResourceLocation id) { - var value = getRegistry(registry).get(id); - if (value == null) throw new IllegalArgumentException(id + " was not registered in " + registry); - return value; - } - - @Override - public RegistryWrappers.RegistryWrapper wrap(ResourceKey> registry) { - return new RegistryWrapperImpl<>(registry.location(), getRegistry(registry)); - } - @Override public RegistrationHelper createRegistrationHelper(ResourceKey> registry) { return new RegistrationHelperImpl<>(getRegistry(registry)); } - @Nullable - @Override - public T tryGetRegistryObject(ResourceKey> registry, ResourceLocation id) { - return getRegistry(registry).get(id); - } - @Override public boolean shouldLoadResource(JsonObject object) { return ResourceConditions.objectMatchesConditions(object); @@ -173,20 +146,18 @@ public class PlatformHelperImpl implements PlatformHelper { } @Override - public > MessageType createMessageType(int id, ResourceLocation channel, Class klass, FriendlyByteBuf.Reader reader) { + public > MessageType createMessageType(ResourceLocation channel, FriendlyByteBuf.Reader reader) { return new FabricMessageType<>(channel, reader); } @Override - public Packet createPacket(NetworkMessage message) { - var buf = PacketByteBufs.create(); - message.write(buf); - return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricType(message.type()).getId(), buf); + public Packet createPacket(NetworkMessage message) { + return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricPacket(message)); } @Override public ComponentAccess createPeripheralAccess(BlockEntity owner, Consumer invalidate) { - return new PeripheralAccessImpl(owner, invalidate); + return new PeripheralAccessImpl(owner); } @Override @@ -322,50 +293,6 @@ public class PlatformHelperImpl implements PlatformHelper { return stack.useOn(new UseOnContext(player, InteractionHand.MAIN_HAND, hit)); } - private record RegistryWrapperImpl( - ResourceLocation name, Registry registry - ) implements RegistryWrappers.RegistryWrapper { - @Override - public int getId(T object) { - return registry.getId(object); - } - - @Override - public ResourceLocation getKey(T object) { - var key = registry.getKey(object); - if (key == null) throw new IllegalArgumentException(object + " was not registered in " + name); - return key; - } - - @Override - public T get(ResourceLocation location) { - var object = registry.get(location); - if (object == null) throw new IllegalArgumentException(location + " was not registered in " + name); - return object; - } - - @Nullable - @Override - public T tryGet(ResourceLocation location) { - return registry.get(location); - } - - @Override - public @Nullable T byId(int id) { - return registry.byId(id); - } - - @Override - public int size() { - return registry.size(); - } - - @Override - public Iterator iterator() { - return registry.iterator(); - } - } - private static final class RegistrationHelperImpl implements RegistrationHelper { private final Registry registry; private final List> entries = new ArrayList<>(); @@ -459,11 +386,8 @@ public class PlatformHelperImpl implements PlatformHelper { } private static final class PeripheralAccessImpl extends ComponentAccessImpl { - private final Runnable[] invalidators = new Runnable[6]; - - private PeripheralAccessImpl(BlockEntity owner, Consumer invalidate) { + private PeripheralAccessImpl(BlockEntity owner) { super(owner, PeripheralLookup.get()); - for (var dir : Direction.values()) invalidators[dir.ordinal()] = () -> invalidate.accept(dir); } @Nullable @@ -473,8 +397,7 @@ public class PlatformHelperImpl implements PlatformHelper { if (result != null) return result; var cache = caches[direction.ordinal()]; - var invalidate = invalidators[direction.ordinal()]; - return Peripherals.getGenericPeripheral(cache.getWorld(), cache.getPos(), direction.getOpposite(), cache.getBlockEntity(), invalidate); + return Peripherals.getGenericPeripheral(cache.getWorld(), cache.getPos(), direction.getOpposite(), cache.getBlockEntity()); } } } diff --git a/projects/fabric/src/main/resources/computercraft.fabric.mixins.json b/projects/fabric/src/main/resources/computercraft.fabric.mixins.json index d52054c29..b572263d1 100644 --- a/projects/fabric/src/main/resources/computercraft.fabric.mixins.json +++ b/projects/fabric/src/main/resources/computercraft.fabric.mixins.json @@ -8,11 +8,11 @@ }, "mixins": [ "ArgumentTypeInfosAccessor", - "ChunkMapMixin", "EntityMixin", "ExplosionDamageCalculatorMixin", "ItemEntityMixin", "ItemMixin", + "PlayerChunkSenderMixin", "ServerLevelMixin", "TagEntryAccessor", "TagsProviderMixin" diff --git a/projects/fabric/src/main/resources/fabric.mod.json b/projects/fabric/src/main/resources/fabric.mod.json index 578385401..0a93dd75c 100644 --- a/projects/fabric/src/main/resources/fabric.mod.json +++ b/projects/fabric/src/main/resources/fabric.mod.json @@ -46,7 +46,7 @@ "depends": { "fabricloader": ">=0.14.21", "fabric-api": ">=0.86.1", - "minecraft": "=1.20.1" + "minecraft": "=1.20.4" }, "accessWidener": "computercraft.accesswidener" } diff --git a/projects/forge-api/build.gradle.kts b/projects/forge-api/build.gradle.kts index 1b169f3f4..388124590 100644 --- a/projects/forge-api/build.gradle.kts +++ b/projects/forge-api/build.gradle.kts @@ -20,11 +20,3 @@ dependencies { tasks.javadoc { include("dan200/computercraft/api/**/*.java") } - -publishing { - publications { - named("maven", MavenPublication::class) { - fg.component(this) - } - } -} diff --git a/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java b/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java index fa306f78a..553a4f577 100644 --- a/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java +++ b/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java @@ -5,13 +5,13 @@ package dan200.computercraft.api.client.turtle; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.turtle.TurtleUpgradeType; -import dan200.computercraft.impl.client.ComputerCraftAPIClientService; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.fml.event.IModBusEvent; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import org.jetbrains.annotations.ApiStatus; /** * This event is fired to register {@link TurtleUpgradeModeller}s for a mod's {@linkplain TurtleUpgradeType turtle @@ -22,11 +22,18 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; * dispatched). Subscribers should be careful not to */ public class RegisterTurtleModellersEvent extends Event implements IModBusEvent, RegisterTurtleUpgradeModeller { + private final RegisterTurtleUpgradeModeller dispatch; + + @ApiStatus.Internal + public RegisterTurtleModellersEvent(RegisterTurtleUpgradeModeller dispatch) { + this.dispatch = dispatch; + } + /** * {@inheritDoc} */ @Override - public void register(TurtleUpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { - ComputerCraftAPIClientService.get().registerTurtleUpgradeModeller(serialiser, modeller); + public void register(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { + dispatch.register(serialiser, modeller); } } diff --git a/projects/forge-api/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java b/projects/forge-api/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java index 2a4b1dc0e..08b0cbfe8 100644 --- a/projects/forge-api/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java +++ b/projects/forge-api/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java @@ -5,15 +5,9 @@ package dan200.computercraft.api; import dan200.computercraft.api.lua.GenericSource; -import dan200.computercraft.api.network.wired.WiredElement; -import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.impl.ComputerCraftAPIForgeService; -import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.world.level.BlockGetter; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; +import net.neoforged.neoforge.capabilities.BlockCapability; /** * The forge-specific entrypoint for ComputerCraft's API. @@ -22,40 +16,16 @@ public final class ForgeComputerCraftAPI { private ForgeComputerCraftAPI() { } - /** - * Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations. - * - * @param provider The peripheral provider to register. - * @see IPeripheral - * @see IPeripheralProvider - */ - public static void registerPeripheralProvider(IPeripheralProvider provider) { - getInstance().registerPeripheralProvider(provider); - } - /** * Registers a capability that can be used by generic peripherals. * * @param capability The capability to register. * @see GenericSource */ - public static void registerGenericCapability(Capability capability) { + public static void registerGenericCapability(BlockCapability capability) { getInstance().registerGenericCapability(capability); } - /** - * Get the wired network element for a block in world. - * - * @param world The world the block exists in - * @param pos The position the block exists in - * @param side The side to extract the network element from - * @return The element's node - * @see WiredElement#getNode() - */ - public static LazyOptional getWiredElementAt(BlockGetter world, BlockPos pos, Direction side) { - return getInstance().getWiredElementAt(world, pos, side); - } - private static ComputerCraftAPIForgeService getInstance() { return ComputerCraftAPIForgeService.get(); } diff --git a/projects/forge-api/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java b/projects/forge-api/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java index 6b93f326e..3b81e4cf6 100644 --- a/projects/forge-api/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java +++ b/projects/forge-api/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java @@ -5,7 +5,7 @@ package dan200.computercraft.api.detail; import dan200.computercraft.impl.ComputerCraftAPIForgeService; -import net.minecraftforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.FluidStack; /** * {@link DetailRegistry}s for Forge-specific types. diff --git a/projects/forge-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementCapability.java b/projects/forge-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementCapability.java new file mode 100644 index 000000000..6ce9a1ffd --- /dev/null +++ b/projects/forge-api/src/main/java/dan200/computercraft/api/network/wired/WiredElementCapability.java @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.api.network.wired; + +import dan200.computercraft.api.ComputerCraftAPI; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.capabilities.BlockCapability; + +/** + * A {@linkplain BlockCapability block capability} for {@link WiredElement}s. This should be used to query wired elements + * from a block. + */ +public final class WiredElementCapability { + public static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_node"); + + private static final BlockCapability capability = BlockCapability.create(ID, WiredElement.class, Direction.class); + + private WiredElementCapability() { + } + + public static BlockCapability get() { + return capability; + } +} diff --git a/projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java b/projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java deleted file mode 100644 index 15abb9fa2..000000000 --- a/projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package dan200.computercraft.api.peripheral; - -import dan200.computercraft.api.ForgeComputerCraftAPI; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.util.LazyOptional; - -/** - * This interface is used to create peripheral implementations for blocks. - *

- * If you have a {@link BlockEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral} - * capability. - *

- * {@link ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)} should be used to register a peripheral - * provider. - */ -@FunctionalInterface -public interface IPeripheralProvider { - /** - * Produce an peripheral implementation from a block location. - * - * @param world The world the block is in. - * @param pos The position the block is at. - * @param side The side to get the peripheral from. - * @return A peripheral, or {@link LazyOptional#empty()} if there is not a peripheral here you'd like to handle. - */ - LazyOptional getPeripheral(Level world, BlockPos pos, Direction side); -} diff --git a/projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/PeripheralCapability.java b/projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/PeripheralCapability.java new file mode 100644 index 000000000..e4067beb0 --- /dev/null +++ b/projects/forge-api/src/main/java/dan200/computercraft/api/peripheral/PeripheralCapability.java @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.api.peripheral; + +import dan200.computercraft.api.ComputerCraftAPI; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.capabilities.BlockCapability; + +/** + * A {@linkplain BlockCapability block capability} for {@link IPeripheral}s. This should be used to register peripherals + * for a block. It should NOT be used to query peripherals. + */ +public final class PeripheralCapability { + public static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "peripheral"); + + private static final BlockCapability capability = BlockCapability.create(ID, IPeripheral.class, Direction.class); + + private PeripheralCapability() { + } + + public static BlockCapability get() { + return capability; + } +} diff --git a/projects/forge-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIForgeService.java b/projects/forge-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIForgeService.java index a9dd5e774..5c4656af7 100644 --- a/projects/forge-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIForgeService.java +++ b/projects/forge-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIForgeService.java @@ -6,14 +6,9 @@ package dan200.computercraft.impl; import dan200.computercraft.api.ForgeComputerCraftAPI; import dan200.computercraft.api.detail.DetailRegistry; -import dan200.computercraft.api.network.wired.WiredElement; -import dan200.computercraft.api.peripheral.IPeripheralProvider; -import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.world.level.BlockGetter; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; +import net.neoforged.neoforge.capabilities.BlockCapability; +import net.neoforged.neoforge.fluids.FluidStack; import org.jetbrains.annotations.ApiStatus; /** @@ -29,11 +24,7 @@ public interface ComputerCraftAPIForgeService extends ComputerCraftAPIService { return (ComputerCraftAPIForgeService) ComputerCraftAPIService.get(); } - void registerPeripheralProvider(IPeripheralProvider provider); - - void registerGenericCapability(Capability capability); - - LazyOptional getWiredElementAt(BlockGetter world, BlockPos pos, Direction side); + void registerGenericCapability(BlockCapability capability); DetailRegistry getFluidStackDetailRegistry(); } diff --git a/projects/forge/build.gradle.kts b/projects/forge/build.gradle.kts index 98813afc9..8eb1edf3c 100644 --- a/projects/forge/build.gradle.kts +++ b/projects/forge/build.gradle.kts @@ -3,12 +3,11 @@ // SPDX-License-Identifier: MPL-2.0 import cc.tweaked.gradle.* -import net.minecraftforge.gradle.common.util.RunConfig +import net.neoforged.gradle.dsl.common.runs.run.Run plugins { id("cc-tweaked.forge") id("cc-tweaked.gametest") - alias(libs.plugins.mixinGradle) id("cc-tweaked.mod-publishing") } @@ -24,93 +23,87 @@ sourceSets { main { resources.srcDir("src/generated/resources") } + + testMod { runs { modIdentifier = "cctest" } } + testFixtures { runs { modIdentifier = "cctest" } } } minecraft { - runs { - // configureEach would be better, but we need to eagerly configure configs or otherwise the run task doesn't - // get set up properly. - all { - property("forge.logging.markers", "REGISTRIES") - property("forge.logging.console.level", "debug") - - mods.register("computercraft") { - cct.sourceDirectories.get().forEach { - if (it.classes) sources(it.sourceSet) - } - } - } - - val client by registering { - workingDirectory(file("run")) - } - - val server by registering { - workingDirectory(file("run/server")) - arg("--nogui") - } - - val data by registering { - workingDirectory(file("run")) - args( - "--mod", "computercraft", "--all", - "--output", file("src/generated/resources/"), - "--existing", project(":common").file("src/main/resources/"), - "--existing", file("src/main/resources/"), - ) - } - - fun RunConfig.configureForGameTest() { - val old = lazyTokens["minecraft_classpath"] - lazyToken("minecraft_classpath") { - // Add all files in testMinecraftLibrary to the classpath. - val allFiles = mutableSetOf() - - val oldVal = old?.get() - if (!oldVal.isNullOrEmpty()) allFiles.addAll(oldVal.split(File.pathSeparatorChar)) - - for (file in configurations["testMinecraftLibrary"].resolve()) allFiles.add(file.absolutePath) - - allFiles.joinToString(File.pathSeparator) - } - - property("cctest.sources", project(":common").file("src/testMod/resources/data/cctest").absolutePath) - - arg("--mixin.config=computercraft-gametest.mixins.json") - - mods.register("cctest") { - source(sourceSets["testMod"]) - source(sourceSets["testFixtures"]) - source(project(":core").sourceSets["testFixtures"]) - } - } - - val testClient by registering { - workingDirectory(file("run/testClient")) - parent(client.get()) - configureForGameTest() - - property("cctest.tags", "client,common") - } - - val gameTestServer by registering { - workingDirectory(file("run/testServer")) - configureForGameTest() - - property("forge.logging.console.level", "info") - jvmArg("-ea") - } + accessTransformers { + file("src/main/resources/META-INF/accesstransformer.cfg") } } -mixin { - add(sourceSets.client.get(), "client-computercraft.refmap.json") +runs { + configureEach { + systemProperty("forge.logging.markers", "REGISTRIES") + systemProperty("forge.logging.console.level", "debug") - config("computercraft-client.forge.mixins.json") + cct.sourceDirectories.get().forEach { + if (it.classes) modSourceAs(it.sourceSet, "computercraft") + } + + dependencies { + runtime(configurations["minecraftLibrary"]) + } + } + + val client by registering { + workingDirectory(file("run")) + } + + val server by registering { + workingDirectory(file("run/server")) + programArgument("--nogui") + } + + val data by registering { + workingDirectory(file("run")) + programArguments.addAll( + "--mod", "computercraft", "--all", + "--output", file("src/generated/resources/").absolutePath, + "--existing", project(":common").file("src/main/resources/").absolutePath, + "--existing", file("src/main/resources/").absolutePath, + ) + } + + fun Run.configureForGameTest() { + gameTest() + + systemProperty("cctest.sources", project(":common").file("src/testMod/resources/data/cctest").absolutePath) + + modSource(sourceSets.testMod.get()) + modSource(sourceSets.testFixtures.get()) + modSourceAs(project(":core").sourceSets.testFixtures.get(), "cctest") + + jvmArgument("-ea") + + dependencies { + runtime(configurations["testMinecraftLibrary"]) + } + } + + val gameTestServer by registering { + workingDirectory(file("run/testServer")) + configureForGameTest() + } + + val gameTestClient by registering { + configure(runTypes.client) + + workingDirectory(file("run/testClient")) + configureForGameTest() + + systemProperties("cctest.tags", "client,common") + } } configurations { - minecraftLibrary { extendsFrom(minecraftEmbed.get()) } + val minecraftLibrary by registering { + isCanBeResolved = true + isCanBeConsumed = false + } + runtimeOnly { extendsFrom(minecraftLibrary.get()) } val testMinecraftLibrary by registering { isCanBeResolved = true @@ -121,37 +114,34 @@ configurations { } dependencies { - annotationProcessor("org.spongepowered:mixin:0.8.5-SQUID:processor") - clientAnnotationProcessor("org.spongepowered:mixin:0.8.5-SQUID:processor") - compileOnly(libs.jetbrainsAnnotations) annotationProcessorEverywhere(libs.autoService) clientCompileOnly(variantOf(libs.emi) { classifier("api") }) - libs.bundles.externalMods.forge.compile.get().map { compileOnly(fg.deobf(it)) } - libs.bundles.externalMods.forge.runtime.get().map { runtimeOnly(fg.deobf(it)) } + compileOnly(libs.bundles.externalMods.forge.compile) + runtimeOnly(libs.bundles.externalMods.forge.runtime) { cct.exclude(this) } // Depend on our other projects. 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) { + "minecraftLibrary"(libs.cobalt) { val version = libs.versions.cobalt.get() jarJar.ranged(this, "[$version,${getNextVersion(version)})") } - minecraftEmbed(libs.jzlib) { + "minecraftLibrary"(libs.jzlib) { jarJar.ranged(this, "[${libs.versions.jzlib.get()},)") } - minecraftEmbed(libs.netty.http) { + "minecraftLibrary"(libs.netty.http) { jarJar.ranged(this, "[${libs.versions.netty.get()},)") isTransitive = false } - minecraftEmbed(libs.netty.socks) { + "minecraftLibrary"(libs.netty.socks) { jarJar.ranged(this, "[${libs.versions.netty.get()},)") isTransitive = false } - minecraftEmbed(libs.netty.proxy) { + "minecraftLibrary"(libs.netty.proxy) { jarJar.ranged(this, "[${libs.versions.netty.get()},)") isTransitive = false } @@ -177,15 +167,14 @@ dependencies { tasks.processResources { inputs.property("modVersion", modVersion) - inputs.property("forgeVersion", libs.versions.forge.get()) + inputs.property("neoVersion", libs.versions.neoForge.get()) filesMatching("META-INF/mods.toml") { - expand(mapOf("forgeVersion" to libs.versions.forge.get(), "file" to mapOf("jarVersion" to modVersion))) + expand(mapOf("neoVersion" to libs.versions.neoForge.get(), "file" to mapOf("jarVersion" to modVersion))) } } tasks.jar { - finalizedBy("reobfJar") archiveClassifier.set("slim") for (source in cct.sourceDirectories.get()) { @@ -198,8 +187,8 @@ tasks.sourcesJar { } tasks.jarJar { - finalizedBy("reobfJarJar") archiveClassifier.set("") + configuration(project.configurations["minecraftLibrary"]) for (source in cct.sourceDirectories.get()) { if (source.classes) from(source.sourceSet.output) @@ -214,22 +203,28 @@ tasks.test { systemProperty("cct.test-files", layout.buildDirectory.dir("tmp/testFiles").getAbsolutePath()) } +tasks.checkDependencyConsistency { + // Forge pulls in slf4j 2.0.9 instead of 2.0.7, so we need to override that. + override(libs.slf4j.asProvider(), "2.0.9") +} + val runGametest by tasks.registering(JavaExec::class) { group = LifecycleBasePlugin.VERIFICATION_GROUP description = "Runs tests on a temporary Minecraft instance." dependsOn("cleanRunGametest") usesService(MinecraftRunnerService.get(gradle)) - setRunConfig(minecraft.runs["gameTestServer"]) + setRunConfig(runs["gameTestServer"]) + systemProperty("forge.logging.console.level", "info") systemProperty("cctest.gametest-report", layout.buildDirectory.dir("test-results/$name.xml").getAbsolutePath()) } cct.jacoco(runGametest) tasks.check { dependsOn(runGametest) } -val runGametestClient by tasks.registering(ClientJavaExec::class) { +/*val runGametestClient by tasks.registering(ClientJavaExec::class) { description = "Runs client-side gametests with no mods" - setRunConfig(minecraft.runs["testClient"]) + setRunConfig(runs["testClient"]) tags("client") } cct.jacoco(runGametestClient) @@ -238,7 +233,7 @@ tasks.register("checkClient") { group = LifecycleBasePlugin.VERIFICATION_GROUP description = "Runs all client-only checks." dependsOn(runGametestClient) -} +}*/ // Upload tasks @@ -251,17 +246,14 @@ for (cfg in listOf(configurations.apiElements, configurations.runtimeElements)) cfg.configure { artifacts.removeIf { it.classifier == "slim" } } } +tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false } publishing { publications { named("maven", MavenPublication::class) { - fg.component(this) - // jarJar.component is broken (https://github.com/MinecraftForge/ForgeGradle/issues/914), so declare the - // artifact explicitly. - artifact(tasks.jarJar) + jarJar.component(this) mavenDependencies { cct.configureExcludes(this) - exclude(libs.jei.forge.get()) } } } diff --git a/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientHooks.java b/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientHooks.java index ea97bf120..ec406e85a 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientHooks.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientHooks.java @@ -7,13 +7,13 @@ package dan200.computercraft.client; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.client.sound.SpeakerSound; import net.minecraft.commands.CommandSourceStack; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.*; -import net.minecraftforge.client.event.sound.PlayStreamingSourceEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.level.LevelEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.client.event.*; +import net.neoforged.neoforge.client.event.sound.PlayStreamingSourceEvent; +import net.neoforged.neoforge.event.TickEvent; +import net.neoforged.neoforge.event.level.LevelEvent; /** * Forge-specific dispatch for {@link ClientHooks}. diff --git a/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientRegistry.java b/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientRegistry.java index 89e9ea8b2..69c32e133 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientRegistry.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/ForgeClientRegistry.java @@ -7,16 +7,15 @@ package dan200.computercraft.client; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent; import dan200.computercraft.client.model.turtle.TurtleModelLoader; +import dan200.computercraft.client.turtle.TurtleUpgradeModellers; import net.minecraft.client.Minecraft; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.ModelEvent; -import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; -import net.minecraftforge.client.event.RegisterColorHandlersEvent; -import net.minecraftforge.client.event.RegisterShadersEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoader; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.client.event.*; import java.io.IOException; @@ -33,7 +32,7 @@ public final class ForgeClientRegistry { @SubscribeEvent public static void registerModelLoaders(ModelEvent.RegisterGeometryLoaders event) { - event.register("turtle", TurtleModelLoader.INSTANCE); + event.register(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle"), TurtleModelLoader.INSTANCE); } /** @@ -49,7 +48,7 @@ public final class ForgeClientRegistry { if (gatheredModellers) return; gatheredModellers = true; - ModLoader.get().postEvent(new RegisterTurtleModellersEvent()); + ModLoader.get().postEvent(new RegisterTurtleModellersEvent(TurtleUpgradeModellers::register)); } } @@ -74,6 +73,11 @@ public final class ForgeClientRegistry { ClientRegistry.registerItemColours(event::register); } + @SubscribeEvent + public static void registerMenuScreens(RegisterMenuScreensEvent event) { + ClientRegistry.registerMenuScreens(event::register); + } + @SubscribeEvent public static void registerReloadListeners(RegisterClientReloadListenersEvent event) { ClientRegistry.registerReloadListeners(event::registerReloadListener, Minecraft.getInstance()); diff --git a/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java b/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java index 948a70589..909271c0a 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java @@ -10,7 +10,7 @@ import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; import net.irisshaders.iris.api.v0.IrisApi; import net.irisshaders.iris.api.v0.IrisTextVertexSink; -import net.minecraftforge.fml.ModList; +import net.neoforged.fml.ModList; import java.nio.ByteBuffer; import java.util.Optional; diff --git a/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java b/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java index c351b79ee..1da96866f 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java @@ -12,8 +12,8 @@ import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.client.model.BakedModelWrapper; -import net.minecraftforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.BakedModelWrapper; +import net.neoforged.neoforge.client.model.data.ModelData; import org.jetbrains.annotations.Nullable; import java.util.List; diff --git a/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java b/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java index 2d457fe52..75919e248 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java @@ -12,8 +12,8 @@ import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.client.model.BakedModelWrapper; -import net.minecraftforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.BakedModelWrapper; +import net.neoforged.neoforge.client.model.data.ModelData; import javax.annotation.Nullable; import java.util.List; diff --git a/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModel.java b/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModel.java index 257e48699..d1eec732b 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModel.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModel.java @@ -9,7 +9,7 @@ import dan200.computercraft.client.model.TransformedBakedModel; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.client.model.BakedModelWrapper; +import net.neoforged.neoforge.client.model.BakedModelWrapper; import java.util.List; import java.util.function.Function; diff --git a/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModelLoader.java b/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModelLoader.java index 4797edb46..6ebed3b6a 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModelLoader.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/model/turtle/TurtleModelLoader.java @@ -15,9 +15,9 @@ import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelState; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.GsonHelper; -import net.minecraftforge.client.model.geometry.IGeometryBakingContext; -import net.minecraftforge.client.model.geometry.IGeometryLoader; -import net.minecraftforge.client.model.geometry.IUnbakedGeometry; +import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; +import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; +import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; import java.util.function.Function; diff --git a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java index 612a79225..d985ba9f6 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java @@ -10,7 +10,7 @@ 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.ForgeMessageType; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.resources.model.BakedModel; @@ -18,12 +18,13 @@ import net.minecraft.client.resources.model.ModelManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.network.protocol.common.ServerCommonPacketListener; +import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.data.ModelData; import javax.annotation.Nullable; import java.util.Arrays; @@ -44,8 +45,8 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper { } @Override - public Packet createPacket(NetworkMessage message) { - return NetworkHandler.createServerboundPacket(message); + public Packet createPacket(NetworkMessage message) { + return new ServerboundCustomPayloadPacket(ForgeMessageType.createPayload(message)); } @Override diff --git a/projects/forge/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java b/projects/forge/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java index 2263a0a2e..8884ebcb7 100644 --- a/projects/forge/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java +++ b/projects/forge/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java @@ -15,7 +15,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.data.ModelData; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/projects/forge/src/client/java/dan200/computercraft/mixin/client/ClientPacketListenerMixin.java b/projects/forge/src/client/java/dan200/computercraft/mixin/client/ClientPacketListenerMixin.java index dd5ecb11b..e98b88df5 100644 --- a/projects/forge/src/client/java/dan200/computercraft/mixin/client/ClientPacketListenerMixin.java +++ b/projects/forge/src/client/java/dan200/computercraft/mixin/client/ClientPacketListenerMixin.java @@ -6,7 +6,7 @@ package dan200.computercraft.mixin.client; import dan200.computercraft.shared.command.CommandComputerCraft; import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraftforge.client.ClientCommandHandler; +import net.neoforged.neoforge.client.ClientCommandHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java index c198a94f2..4c11b9d31 100644 --- a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java @@ -8,60 +8,73 @@ import com.electronwill.nightconfig.core.file.FileConfig; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ForgeComputerCraftAPI; import dan200.computercraft.api.detail.ForgeDetailRegistries; -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.api.network.wired.WiredElementCapability; +import dan200.computercraft.api.peripheral.PeripheralCapability; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.impl.Services; import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.ModRegistry; +import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.details.FluidData; -import dan200.computercraft.shared.integration.MoreRedIntegration; +import dan200.computercraft.shared.network.NetworkMessages; +import dan200.computercraft.shared.network.client.ClientNetworkContext; +import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral; import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods; import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; +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.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; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.event.config.ModConfigEvent; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.registries.NewRegistryEvent; -import net.minecraftforge.registries.RegistryBuilder; +import dan200.computercraft.shared.platform.ForgeMessageType; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; +import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent; +import net.neoforged.neoforge.registries.NewRegistryEvent; +import net.neoforged.neoforge.registries.RegistryBuilder; + +import javax.annotation.Nullable; @Mod(ComputerCraftAPI.MOD_ID) @Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public final class ComputerCraft { - public ComputerCraft() { - ModRegistry.register(); + private static @Nullable IEventBus eventBus; + + public ComputerCraft(IEventBus eventBus) { + withEventBus(eventBus, ModRegistry::register); ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ((ForgeConfigFile) ConfigSpec.serverSpec).spec()); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ((ForgeConfigFile) ConfigSpec.clientSpec).spec()); + } - NetworkHandler.setup(); + private static void withEventBus(IEventBus eventBus, Runnable task) { + ComputerCraft.eventBus = eventBus; + task.run(); + ComputerCraft.eventBus = null; + } + + public static IEventBus getEventBus() { + var bus = eventBus; + if (bus == null) throw new NullPointerException("Bus is not available."); + return bus; } @SubscribeEvent public static void registerRegistries(NewRegistryEvent event) { - event.create(new RegistryBuilder>() - .setName(TurtleUpgradeSerialiser.registryId().location()) - .disableSaving().disableSync()); - - event.create(new RegistryBuilder>() - .setName(PocketUpgradeSerialiser.registryId().location()) - .disableSaving().disableSync()); - } - - @SubscribeEvent - public static void registerCapabilities(RegisterCapabilitiesEvent event) { - event.register(WiredElement.class); - event.register(IPeripheral.class); + event.create(new RegistryBuilder<>(ITurtleUpgrade.serialiserRegistryKey())); + event.create(new RegistryBuilder<>(IPocketUpgrade.serialiserRegistryKey())); } @SubscribeEvent @@ -72,13 +85,61 @@ public final class ComputerCraft { ComputerCraftAPI.registerGenericSource(new FluidMethods()); ComputerCraftAPI.registerGenericSource(new EnergyMethods()); - ForgeComputerCraftAPI.registerGenericCapability(ForgeCapabilities.ITEM_HANDLER); - ForgeComputerCraftAPI.registerGenericCapability(ForgeCapabilities.ENERGY); - ForgeComputerCraftAPI.registerGenericCapability(ForgeCapabilities.FLUID_HANDLER); + ForgeComputerCraftAPI.registerGenericCapability(Capabilities.ItemHandler.BLOCK); + ForgeComputerCraftAPI.registerGenericCapability(Capabilities.FluidHandler.BLOCK); + ForgeComputerCraftAPI.registerGenericCapability(Capabilities.EnergyStorage.BLOCK); ForgeDetailRegistries.FLUID_STACK.addProvider(FluidData::fill); + } - if (ModList.get().isLoaded(MoreRedIntegration.MOD_ID)) MoreRedIntegration.setup(); + @SubscribeEvent + public static void registerNetwork(RegisterPayloadHandlerEvent event) { + var registrar = event.registrar(ComputerCraftAPI.MOD_ID).versioned(ComputerCraftAPI.getInstalledVersion()); + + for (var type : NetworkMessages.getServerbound()) { + var forgeType = ForgeMessageType.cast(type); + registrar.play(forgeType.id(), forgeType.reader(), builder -> builder.server( + (t, context) -> context.workHandler().execute(() -> t.payload().handle(() -> (ServerPlayer) context.player().orElseThrow())) + )); + } + + for (var type : NetworkMessages.getClientbound()) { + var forgeType = ForgeMessageType.cast(type); + registrar.play(forgeType.id(), forgeType.reader(), builder -> builder.client( + (t, context) -> context.workHandler().execute(() -> t.payload().handle(ClientHolderHolder.get())) + )); + } + } + + /** + * Attach capabilities to our block entities. + * + * @param event The event to register capabilities with. + */ + @SubscribeEvent + public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.COMPUTER_NORMAL.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.COMPUTER_ADVANCED.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.TURTLE_NORMAL.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.SPEAKER.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.PRINTER.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.DISK_DRIVE.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.MONITOR_NORMAL.get(), (b, d) -> b.peripheral()); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), (b, d) -> b.peripheral()); + + event.registerBlockEntity( + PeripheralCapability.get(), BlockEntityType.COMMAND_BLOCK, + (b, d) -> Config.enableCommandBlock ? new CommandBlockPeripheral(b) : null + ); + + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.WIRELESS_MODEM_NORMAL.get(), WirelessModemBlockEntity::getPeripheral); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.WIRELESS_MODEM_ADVANCED.get(), WirelessModemBlockEntity::getPeripheral); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get(), WiredModemFullBlockEntity::getPeripheral); + event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.CABLE.get(), CableBlockEntity::getPeripheral); + + event.registerBlockEntity(WiredElementCapability.get(), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get(), (b, d) -> b.getElement()); + event.registerBlockEntity(WiredElementCapability.get(), ModRegistry.BlockEntities.CABLE.get(), CableBlockEntity::getWiredElement); } @SubscribeEvent @@ -107,4 +168,24 @@ public final class ComputerCraft { public static void onCreativeTab(BuildCreativeModeTabContentsEvent event) { CommonHooks.onBuildCreativeTab(event.getTabKey(), event.getParameters(), event); } + + /** + * This holds an instance of {@link ClientNetworkContext}. This is a separate class to ensure that the instance is + * lazily created when needed on the client. + */ + private static final class ClientHolderHolder { + private static final @Nullable ClientNetworkContext INSTANCE; + private static final @Nullable Throwable ERROR; + + static { + var helper = Services.tryLoad(ClientNetworkContext.class); + INSTANCE = helper.instance(); + ERROR = helper.error(); + } + + static ClientNetworkContext get() { + var instance = INSTANCE; + return instance == null ? Services.raise(ClientNetworkContext.class, ERROR) : instance; + } + } } diff --git a/projects/forge/src/main/java/dan200/computercraft/data/Generators.java b/projects/forge/src/main/java/dan200/computercraft/data/Generators.java index 42b87b5aa..48126ef7c 100644 --- a/projects/forge/src/main/java/dan200/computercraft/data/Generators.java +++ b/projects/forge/src/main/java/dan200/computercraft/data/Generators.java @@ -5,12 +5,12 @@ package dan200.computercraft.data; import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.shared.platform.RegistryWrappers; import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.DataGenerator; import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; import net.minecraft.data.loot.LootTableProvider; import net.minecraft.data.models.BlockModelGenerators; import net.minecraft.data.models.ItemModelGenerators; @@ -21,16 +21,14 @@ import net.minecraft.server.packs.PackType; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; -import net.minecraftforge.common.data.BlockTagsProvider; -import net.minecraftforge.common.data.ExistingFileHelper; -import net.minecraftforge.common.data.JsonCodecProvider; -import net.minecraftforge.data.event.GatherDataEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.common.data.BlockTagsProvider; +import net.neoforged.neoforge.common.data.ExistingFileHelper; +import net.neoforged.neoforge.common.data.JsonCodecProvider; +import net.neoforged.neoforge.data.event.GatherDataEvent; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @@ -57,9 +55,16 @@ public class Generators { @Override public void addFromCodec(String name, PackType type, String directory, Codec codec, Consumer> output) { add(out -> { - Map map = new HashMap<>(); - output.accept(map::put); - return new JsonCodecProvider<>(out, existingFiles, ComputerCraftAPI.MOD_ID, JsonOps.INSTANCE, type, directory, codec, map); + var target = switch (type) { + case SERVER_DATA -> PackOutput.Target.DATA_PACK; + case CLIENT_RESOURCES -> PackOutput.Target.RESOURCE_PACK; + }; + return new JsonCodecProvider(out, target, directory, type, codec, registries, ComputerCraftAPI.MOD_ID, existingFiles) { + @Override + protected void gather() { + output.accept(this::unconditional); + } + }; }); } @@ -73,7 +78,7 @@ public class Generators { return add(out -> new BlockTagsProvider(out, registries, ComputerCraftAPI.MOD_ID, existingFiles) { @Override protected void addTags(HolderLookup.Provider registries) { - tags.accept(x -> new TagProvider.TagAppender<>(RegistryWrappers.BLOCKS, getOrCreateRawBuilder(x))); + tags.accept(x -> new TagProvider.TagAppender<>(BuiltInRegistries.BLOCK, getOrCreateRawBuilder(x))); } }); } @@ -87,7 +92,7 @@ public class Generators { tags.accept(new TagProvider.ItemTagConsumer() { @Override public TagProvider.TagAppender tag(TagKey tag) { - return new TagProvider.TagAppender<>(RegistryWrappers.ITEMS, getOrCreateRawBuilder(tag)); + return new TagProvider.TagAppender<>(BuiltInRegistries.ITEM, getOrCreateRawBuilder(tag)); } @Override diff --git a/projects/forge/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java b/projects/forge/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java index 597a71618..817d2d2bc 100644 --- a/projects/forge/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java +++ b/projects/forge/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java @@ -7,21 +7,20 @@ package dan200.computercraft.impl; import com.google.auto.service.AutoService; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.detail.DetailRegistry; -import dan200.computercraft.api.network.wired.WiredElement; -import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.impl.detail.DetailRegistryImpl; import dan200.computercraft.shared.details.FluidData; +import dan200.computercraft.shared.peripheral.generic.ComponentLookup; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.world.level.BlockGetter; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.ModList; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.fml.ModList; +import net.neoforged.neoforge.capabilities.BlockCapability; +import net.neoforged.neoforge.fluids.FluidStack; import javax.annotation.Nullable; - -import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT; +import java.util.Objects; @AutoService(ComputerCraftAPIService.class) public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI implements ComputerCraftAPIForgeService { @@ -38,23 +37,30 @@ public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI impleme } @Override - public void registerPeripheralProvider(IPeripheralProvider provider) { - Peripherals.register(provider); - } - - @Override - public void registerGenericCapability(Capability capability) { - Peripherals.registerGenericCapability(capability); - } - - @Override - public LazyOptional getWiredElementAt(BlockGetter world, BlockPos pos, Direction side) { - var tile = world.getBlockEntity(pos); - return tile == null ? LazyOptional.empty() : tile.getCapability(CAPABILITY_WIRED_ELEMENT, side); + public void registerGenericCapability(BlockCapability capability) { + Objects.requireNonNull(capability, "Capability cannot be null"); + Peripherals.addGenericLookup(new CapabilityLookup<>(capability)); } @Override public DetailRegistry getFluidStackDetailRegistry() { return fluidStackDetails; } + + /** + * A {@link ComponentLookup} for {@linkplain BlockCapability capabilities}. + *

+ * This is a record to ensure that adding the same capability multiple times only results in one lookup being + * present in the resulting list. + * + * @param capability The capability to lookup + * @param The type of the capability we look up. + */ + private record CapabilityLookup(BlockCapability capability) implements ComponentLookup { + @Nullable + @Override + public T find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side) { + return level.getCapability(capability, pos, state, blockEntity, side); + } + } } diff --git a/projects/forge/src/main/java/dan200/computercraft/impl/Peripherals.java b/projects/forge/src/main/java/dan200/computercraft/impl/Peripherals.java deleted file mode 100644 index 916af6367..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/impl/Peripherals.java +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package dan200.computercraft.impl; - -import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.api.peripheral.IPeripheralProvider; -import dan200.computercraft.shared.peripheral.generic.ComponentLookup; -import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider; -import dan200.computercraft.shared.platform.InvalidateCallback; -import dan200.computercraft.shared.util.CapabilityUtil; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Objects; - -import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL; - -/** - * The registry for peripheral providers. - *

- * This lives in the {@code impl} package despite it not being part of the public API, in order to mirror Forge's class. - */ -public final class Peripherals { - private static final Logger LOG = LoggerFactory.getLogger(Peripherals.class); - - private static final Collection providers = new LinkedHashSet<>(); - private static final GenericPeripheralProvider genericProvider = new GenericPeripheralProvider<>(); - - private Peripherals() { - } - - public static synchronized void register(IPeripheralProvider provider) { - Objects.requireNonNull(provider, "provider cannot be null"); - providers.add(provider); - } - - public static void registerGenericLookup(ComponentLookup lookup) { - genericProvider.registerLookup(lookup); - } - - /** - * A {@link ComponentLookup} for {@linkplain Capability capabilities}. - *

- * This is a record to ensure that adding the same capability multiple times only results in one lookup being - * present in the resulting list. - * - * @param capability The capability to lookup - * @param The type of the capability we look up. - */ - private record CapabilityLookup(Capability capability) implements ComponentLookup { - @Nullable - @Override - public T find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, InvalidateCallback invalidate) { - return CapabilityUtil.unwrap(CapabilityUtil.getCapability(blockEntity, this.capability(), side), invalidate); - } - } - - public static void registerGenericCapability(Capability capability) { - Objects.requireNonNull(capability, "Capability cannot be null"); - registerGenericLookup(new CapabilityLookup<>(capability)); - } - - @Nullable - public static IPeripheral getPeripheral(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) { - if (!world.isInWorldBounds(pos)) return null; - - var block = world.getBlockEntity(pos); - if (block != null) { - var peripheral = block.getCapability(CAPABILITY_PERIPHERAL, side); - if (peripheral.isPresent()) return CapabilityUtil.unwrap(peripheral, invalidate); - } - - // Try the handlers in order: - for (var peripheralProvider : providers) { - try { - var peripheral = peripheralProvider.getPeripheral(world, pos, side); - if (peripheral.isPresent()) return CapabilityUtil.unwrap(peripheral, invalidate); - } catch (Exception e) { - LOG.error("Peripheral provider " + peripheralProvider + " errored.", e); - } - } - - return genericProvider.getPeripheral(world, pos, side, block, invalidate); - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/Capabilities.java b/projects/forge/src/main/java/dan200/computercraft/shared/Capabilities.java deleted file mode 100644 index 06b28df0d..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/Capabilities.java +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared; - -import dan200.computercraft.api.network.wired.WiredElement; -import dan200.computercraft.api.peripheral.IPeripheral; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.CapabilityManager; -import net.minecraftforge.common.capabilities.CapabilityToken; - -public final class Capabilities { - public static final Capability CAPABILITY_PERIPHERAL = CapabilityManager.get(new CapabilityToken<>() { - }); - - public static final Capability CAPABILITY_WIRED_ELEMENT = CapabilityManager.get(new CapabilityToken<>() { - }); - - private Capabilities() { - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java index ed36e39c7..33ff2330a 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java @@ -6,38 +6,18 @@ package dan200.computercraft.shared; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.command.CommandComputerCraft; -import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity; -import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; import dan200.computercraft.shared.network.server.ServerNetworking; -import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral; -import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity; -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.peripheral.monitor.MonitorBlockEntity; -import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity; -import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity; -import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; -import dan200.computercraft.shared.util.CapabilityProvider; -import dan200.computercraft.shared.util.SidedCapabilityProvider; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.CommandBlockEntity; -import net.minecraftforge.event.*; -import net.minecraftforge.event.entity.EntityJoinLevelEvent; -import net.minecraftforge.event.entity.living.LivingDropsEvent; -import net.minecraftforge.event.level.ChunkWatchEvent; -import net.minecraftforge.event.server.ServerStartingEvent; -import net.minecraftforge.event.server.ServerStoppedEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.items.wrapper.InvWrapper; -import net.minecraftforge.items.wrapper.SidedInvWrapper; - -import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL; -import static net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER; +import net.neoforged.bus.api.EventPriority; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.event.*; +import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; +import net.neoforged.neoforge.event.entity.living.LivingDropsEvent; +import net.neoforged.neoforge.event.level.ChunkWatchEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.event.server.ServerStoppedEvent; /** * Forge-specific dispatch for {@link CommonHooks}. @@ -57,6 +37,11 @@ public class ForgeCommonHooks { CommonHooks.onServerStarting(event.getServer()); } + @SubscribeEvent + public static void onServerStarted(ServerStartedEvent event) { + CommonHooks.onServerStarted(event.getServer()); + } + @SubscribeEvent public static void onServerStopped(ServerStoppedEvent event) { CommonHooks.onServerStopped(); @@ -68,7 +53,7 @@ public class ForgeCommonHooks { } @SubscribeEvent - public static void onChunkWatch(ChunkWatchEvent.Watch event) { + public static void onChunkWatch(ChunkWatchEvent.Sent event) { CommonHooks.onChunkWatch(event.getChunk(), event.getPlayer()); } @@ -87,57 +72,6 @@ public class ForgeCommonHooks { } } - private static final ResourceLocation PERIPHERAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "peripheral"); - private static final ResourceLocation WIRED_ELEMENT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_node"); - private static final ResourceLocation INVENTORY = new ResourceLocation(ComputerCraftAPI.MOD_ID, "inventory"); - - /** - * Attach capabilities to our block entities. - * - * @param event The {@link AttachCapabilitiesEvent} event. - */ - @SubscribeEvent - public static void onCapability(AttachCapabilitiesEvent event) { - var blockEntity = event.getObject(); - if (blockEntity instanceof ComputerBlockEntity computer) { - CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, computer::peripheral); - } else if (blockEntity instanceof TurtleBlockEntity turtle) { - CapabilityProvider.attach(event, INVENTORY, ITEM_HANDLER, () -> new InvWrapper(turtle)); - - var peripheral = CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, turtle::peripheral); - turtle.onMoved(peripheral::invalidate); - } else if (blockEntity instanceof DiskDriveBlockEntity diskDrive) { - CapabilityProvider.attach(event, INVENTORY, ITEM_HANDLER, () -> new InvWrapper(diskDrive)); - CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, diskDrive::peripheral); - } else if (blockEntity instanceof CableBlockEntity cable) { - var peripheralHandler = SidedCapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, cable::getPeripheral); - var elementHandler = SidedCapabilityProvider.attach(event, WIRED_ELEMENT, Capabilities.CAPABILITY_WIRED_ELEMENT, cable::getWiredElement); - cable.onModemChanged(() -> { - peripheralHandler.invalidate(); - elementHandler.invalidate(); - }); - } else if (blockEntity instanceof WiredModemFullBlockEntity modem) { - SidedCapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, modem::getPeripheral); - CapabilityProvider.attach(event, WIRED_ELEMENT, Capabilities.CAPABILITY_WIRED_ELEMENT, modem::getElement); - } else if (blockEntity instanceof WirelessModemBlockEntity modem) { - var peripheral = SidedCapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, modem::getPeripheral); - modem.onModemChanged(peripheral::invalidate); - } else if (blockEntity instanceof MonitorBlockEntity monitor) { - CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, monitor::peripheral); - } else if (blockEntity instanceof SpeakerBlockEntity speaker) { - CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, speaker::peripheral); - } else if (blockEntity instanceof PrinterBlockEntity printer) { - CapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, printer::peripheral); - // We don't need to invalidate here as the block's can't be rotated on the X axis! - SidedCapabilityProvider.attach( - event, INVENTORY, ITEM_HANDLER, - s -> s == null ? new InvWrapper(printer) : new SidedInvWrapper(printer, s) - ); - } else if (Config.enableCommandBlock && blockEntity instanceof CommandBlockEntity commandBlock) { - CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, () -> new CommandBlockPeripheral(commandBlock)); - } - } - @SubscribeEvent public static void lootLoad(LootTableLoadEvent event) { var pool = CommonHooks.getExtraLootPool(event.getName()); diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/details/FluidData.java b/projects/forge/src/main/java/dan200/computercraft/shared/details/FluidData.java index 4bd36f78b..8fcf72c49 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/details/FluidData.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/details/FluidData.java @@ -4,14 +4,14 @@ package dan200.computercraft.shared.details; -import dan200.computercraft.shared.platform.RegistryWrappers; -import net.minecraftforge.fluids.FluidStack; +import net.minecraft.core.registries.BuiltInRegistries; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.Map; public class FluidData { public static void fillBasic(Map data, FluidStack stack) { - data.put("name", DetailHelpers.getId(RegistryWrappers.FLUIDS, stack.getFluid())); + data.put("name", DetailHelpers.getId(BuiltInRegistries.FLUID, stack.getFluid())); data.put("amount", stack.getAmount()); } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/integration/ForgePermissionRegistry.java b/projects/forge/src/main/java/dan200/computercraft/shared/integration/ForgePermissionRegistry.java index eaa734ea8..2d391b905 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/integration/ForgePermissionRegistry.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/integration/ForgePermissionRegistry.java @@ -8,12 +8,12 @@ import com.google.auto.service.AutoService; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.command.UserLevel; import net.minecraft.commands.CommandSourceStack; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.server.permission.PermissionAPI; -import net.minecraftforge.server.permission.events.PermissionGatherEvent; -import net.minecraftforge.server.permission.nodes.PermissionNode; -import net.minecraftforge.server.permission.nodes.PermissionType; -import net.minecraftforge.server.permission.nodes.PermissionTypes; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.server.permission.PermissionAPI; +import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent; +import net.neoforged.neoforge.server.permission.nodes.PermissionNode; +import net.neoforged.neoforge.server.permission.nodes.PermissionType; +import net.neoforged.neoforge.server.permission.nodes.PermissionTypes; import java.util.ArrayList; import java.util.List; @@ -52,7 +52,7 @@ public final class ForgePermissionRegistry extends PermissionRegistry { @Override public void register() { super.register(); - MinecraftForge.EVENT_BUS.addListener((PermissionGatherEvent.Nodes event) -> event.addNodes(nodes)); + NeoForge.EVENT_BUS.addListener((PermissionGatherEvent.Nodes event) -> event.addNodes(nodes)); } @AutoService(PermissionRegistry.Provider.class) diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/integration/MoreRedIntegration.java b/projects/forge/src/main/java/dan200/computercraft/shared/integration/MoreRedIntegration.java deleted file mode 100644 index 202b6f89a..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/integration/MoreRedIntegration.java +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.integration; - -import commoble.morered.api.MoreRedAPI; -import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.shared.common.IBundledRedstoneBlock; -import dan200.computercraft.shared.util.SidedCapabilityProvider; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.AttachCapabilitiesEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; - -public class MoreRedIntegration { - public static final String MOD_ID = "morered"; - - private static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, MOD_ID); - - @SubscribeEvent - public static void attachBlockCapabilities(AttachCapabilitiesEvent event) { - var blockEntity = event.getObject(); - - if (blockEntity.getBlockState().getBlock() instanceof IBundledRedstoneBlock bundledBlock) { - // The API is a little unclear on whether this needs to be sided. The API design mirrors Block.getSignal - // (suggesting we can use wireFace.getOpposite(), which is what we did on older versions), but on the other - // hand that parameter is not guaranteed to be non-null (suggesting we should use the cap side instead). - SidedCapabilityProvider.attach(event, ID, MoreRedAPI.CHANNELED_POWER_CAPABILITY, side -> (world, wirePos, wireState, wireFace, channel) -> { - if (side == null) return 0; // It's not clear if there's a sensible implementation here. - - var level = bundledBlock.getBundledRedstoneOutput(world, blockEntity.getBlockPos(), side); - return (level & (1 << channel)) != 0 ? 31 : 0; - }); - } - } - - public static void setup() { - MinecraftForge.EVENT_BUS.register(MoreRedIntegration.class); - ComputerCraftAPI.registerBundledRedstoneProvider(MoreRedIntegration::getBundledPower); - } - - private static int getBundledPower(Level world, BlockPos pos, Direction side) { - var blockEntity = world.getBlockEntity(pos); - if (blockEntity == null) return -1; - - var blockState = blockEntity.getBlockState(); - - // Skip ones already handled by CC. We can do this more efficiently. - if (blockState.getBlock() instanceof IBundledRedstoneBlock) return -1; - - var powerCap = blockEntity.getCapability(MoreRedAPI.CHANNELED_POWER_CAPABILITY, side); - if (!powerCap.isPresent()) return -1; - var power = powerCap.orElseThrow(NullPointerException::new); - - var mask = 0; - for (var i = 0; i < 16; i++) { - mask |= power.getPowerOnChannel(world, pos, blockState, side, i) > 0 ? (1 << i) : 0; - } - return mask; - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java b/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java index 0ed265585..7cb0b6b00 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java @@ -5,7 +5,7 @@ package dan200.computercraft.shared.peripheral.generic.methods; import dan200.computercraft.api.lua.LuaFunction; -import net.minecraftforge.energy.IEnergyStorage; +import net.neoforged.neoforge.energy.IEnergyStorage; /** * Fluid methods for Forge's {@link IEnergyStorage}. diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java b/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java index a550b073f..1d5ff6ba7 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java @@ -8,12 +8,12 @@ import dan200.computercraft.api.detail.ForgeDetailRegistries; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.peripheral.IComputerAccess; -import dan200.computercraft.shared.platform.RegistryWrappers; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.IFluidHandler; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; import javax.annotation.Nullable; import java.util.HashMap; @@ -46,7 +46,7 @@ public final class FluidMethods extends AbstractFluidMethods { String toName, Optional limit, Optional fluidName ) throws LuaException { var fluid = fluidName.isPresent() - ? getRegistryEntry(fluidName.get(), "fluid", RegistryWrappers.FLUIDS) + ? getRegistryEntry(fluidName.get(), "fluid", BuiltInRegistries.FLUID) : null; // Find location to transfer to @@ -71,7 +71,7 @@ public final class FluidMethods extends AbstractFluidMethods { String fromName, Optional limit, Optional fluidName ) throws LuaException { var fluid = fluidName.isPresent() - ? getRegistryEntry(fluidName.get(), "fluid", RegistryWrappers.FLUIDS) + ? getRegistryEntry(fluidName.get(), "fluid", BuiltInRegistries.FLUID) : null; // Find location to transfer to @@ -91,11 +91,14 @@ public final class FluidMethods extends AbstractFluidMethods { @Nullable private static IFluidHandler extractHandler(@Nullable Object object) { - if (object instanceof BlockEntity blockEntity && blockEntity.isRemoved()) return null; + if (object instanceof BlockEntity blockEntity) { + if (blockEntity.isRemoved()) return null; - if (object instanceof ICapabilityProvider provider) { - var cap = provider.getCapability(ForgeCapabilities.FLUID_HANDLER); - if (cap.isPresent()) return cap.orElseThrow(NullPointerException::new); + var level = blockEntity.getLevel(); + if (!(level instanceof ServerLevel serverLevel)) return null; + + var result = serverLevel.getCapability(Capabilities.FluidHandler.BLOCK, blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, null); + if (result != null) return result; } if (object instanceof IFluidHandler handler) return handler; diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java index 49ab64b2d..950de02d7 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java @@ -9,12 +9,12 @@ import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.shared.platform.ForgeContainerTransfer; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Container; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.wrapper.InvWrapper; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.InvWrapper; import javax.annotation.Nullable; import java.util.HashMap; @@ -109,11 +109,14 @@ public final class InventoryMethods extends AbstractInventoryMethods entries; - public ForgeConfigFile(ForgeConfigSpec spec, Trie entries) { + public ForgeConfigFile(ModConfigSpec spec, Trie entries) { this.spec = spec; this.entries = entries; } - public ForgeConfigSpec spec() { + public ModConfigSpec spec() { return spec; } @@ -42,10 +42,10 @@ public final class ForgeConfigFile implements ConfigFile { } /** - * Wraps {@link ForgeConfigSpec.Builder} into our own config builder abstraction. + * Wraps {@link ModConfigSpec.Builder} into our own config builder abstraction. */ static class Builder extends ConfigFile.Builder { - private final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + private final ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); private final Trie entries = new Trie<>(); private void translation(String name) { @@ -80,7 +80,7 @@ public final class ForgeConfigFile implements ConfigFile { return this; } - private ConfigFile.Value defineValue(ForgeConfigSpec.ConfigValue value) { + private ConfigFile.Value defineValue(ModConfigSpec.ConfigValue value) { var wrapped = new ValueImpl<>(value); entries.setValue(value.getPath(), wrapped); return wrapped; @@ -129,7 +129,7 @@ public final class ForgeConfigFile implements ConfigFile { private static final class GroupImpl implements ConfigFile.Group { private final List path; - private @Nullable ForgeConfigSpec owner; + private @Nullable ModConfigSpec owner; private GroupImpl(List path) { this.path = path; @@ -149,14 +149,14 @@ public final class ForgeConfigFile implements ConfigFile { } private static final class ValueImpl implements ConfigFile.Value { - private final ForgeConfigSpec.ConfigValue value; - private @Nullable ForgeConfigSpec owner; + private final ModConfigSpec.ConfigValue value; + private @Nullable ModConfigSpec owner; - private ValueImpl(ForgeConfigSpec.ConfigValue value) { + private ValueImpl(ModConfigSpec.ConfigValue value) { this.value = value; } - private ForgeConfigSpec.ValueSpec spec() { + private ModConfigSpec.ValueSpec spec() { if (owner == null) throw new IllegalStateException("Config has not been built yet"); return owner.getSpec().get(value.getPath()); } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeContainerTransfer.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeContainerTransfer.java index d802cfc2e..3e24c0321 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeContainerTransfer.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeContainerTransfer.java @@ -5,7 +5,7 @@ package dan200.computercraft.shared.platform; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.IItemHandler; +import net.neoforged.neoforge.items.IItemHandler; public class ForgeContainerTransfer implements ContainerTransfer.Slotted { private final IItemHandler handler; diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeMessageType.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeMessageType.java new file mode 100644 index 000000000..512ed4095 --- /dev/null +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeMessageType.java @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 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.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +/** + * A {@link MessageType} implementation for Forge. + *

+ * This wraps {@link NetworkMessage}s into a {@link CustomPacketPayload}, allowing us to easily use Minecraft's existing + * custom packets. + * + * @param id The id of this message. + * @param reader Read this message from a network buffer. + * @param The type of our {@link NetworkMessage}. + */ +public record ForgeMessageType>( + ResourceLocation id, FriendlyByteBuf.Reader> reader +) implements MessageType { + public static > ForgeMessageType cast(MessageType type) { + return (ForgeMessageType) type; + } + + public static CustomPacketPayload createPayload(NetworkMessage message) { + return new Payload<>(message); + } + + public record Payload>(T payload) implements CustomPacketPayload { + @Override + public void write(FriendlyByteBuf buf) { + payload().write(buf); + } + + @Override + public ResourceLocation id() { + return payload().type().id(); + } + } +} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/InvalidateCallback.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/InvalidateCallback.java deleted file mode 100644 index 441f44b14..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/InvalidateCallback.java +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.platform; - -import dan200.computercraft.shared.peripheral.generic.ComponentLookup; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.common.util.NonNullConsumer; - -/** - * A function which may be called when a capability (or some other object) has been invalidated. - *

- * This extends {@link NonNullConsumer} for use with {@link LazyOptional#addListener(NonNullConsumer)}, and - * {@link Runnable} for use with {@link ComponentLookup}. - */ -public interface InvalidateCallback extends Runnable, NonNullConsumer { - @Override - default void accept(Object o) { - run(); - } - - /** - * Cast this callback to a {@link NonNullConsumer} of an arbitrary type. - * - * @param The type of the consumer, normally a {@link LazyOptional}. - * @return {@code this}, but with a compatible type. - */ - @SuppressWarnings("unchecked") - default NonNullConsumer castConsumer() { - return (NonNullConsumer) this; - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java deleted file mode 100644 index 1c07b3b24..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java +++ /dev/null @@ -1,119 +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.impl.Services; -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 net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientGamePacketListener; -import net.minecraft.network.protocol.game.ServerGamePacketListener; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.network.NetworkDirection; -import net.minecraftforge.network.NetworkEvent; -import net.minecraftforge.network.NetworkRegistry; -import net.minecraftforge.network.simple.SimpleChannel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.util.function.Function; - -import static dan200.computercraft.core.util.Nullability.assertNonNull; - -public final class NetworkHandler { - private static final Logger LOG = LoggerFactory.getLogger(NetworkHandler.class); - - private static final SimpleChannel network; - - static { - var version = ComputerCraftAPI.getInstalledVersion(); - network = NetworkRegistry.ChannelBuilder.named(new ResourceLocation(ComputerCraftAPI.MOD_ID, "network")) - .networkProtocolVersion(() -> version) - .clientAcceptedVersions(version::equals).serverAcceptedVersions(version::equals) - .simpleChannel(); - } - - private NetworkHandler() { - } - - public static void setup() { - for (var type : NetworkMessages.getServerbound()) { - var forgeType = (MessageTypeImpl>) type; - registerMainThread(forgeType, NetworkDirection.PLAY_TO_SERVER, c -> () -> assertNonNull(c.getSender())); - } - - for (var type : NetworkMessages.getClientbound()) { - var forgeType = (MessageTypeImpl>) type; - registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientHolder.get()); - } - } - - @SuppressWarnings("unchecked") - public static Packet createClientboundPacket(NetworkMessage packet) { - return (Packet) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_CLIENT); - } - - @SuppressWarnings("unchecked") - public static Packet createServerboundPacket(NetworkMessage packet) { - return (Packet) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_SERVER); - } - - /** - * Register packet, and a thread-unsafe handler for it. - * - * @param The type of the packet to send. - * @param The context this packet is evaluated under. - * @param type The message type to register. - * @param direction A network direction which will be asserted before any processing of this message occurs - * @param handler Gets or constructs the handler for this packet. - */ - static > void registerMainThread( - MessageTypeImpl type, NetworkDirection direction, Function handler - ) { - network.messageBuilder(type.klass(), type.id(), direction) - .encoder(NetworkMessage::write) - .decoder(type.reader()) - .consumerMainThread((packet, contextSup) -> { - try { - packet.handle(handler.apply(contextSup.get())); - } catch (RuntimeException | Error e) { - LOG.error("Failed handling packet", e); - throw e; - } - }) - .add(); - } - - public record MessageTypeImpl>( - int id, Class klass, Function reader - ) implements MessageType { - } - - /** - * This holds an instance of {@link ClientNetworkContext}. This is a separate class to ensure that the instance is - * lazily created when needed on the client. - */ - private static final class ClientHolder { - private static final @Nullable ClientNetworkContext INSTANCE; - private static final @Nullable Throwable ERROR; - - static { - var helper = Services.tryLoad(ClientNetworkContext.class); - INSTANCE = helper.instance(); - ERROR = helper.error(); - } - - static ClientNetworkContext get() { - var instance = INSTANCE; - return instance == null ? Services.raise(ClientNetworkContext.class, ERROR) : instance; - } - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 63bc64304..8cb0694b6 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -9,34 +9,39 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.mojang.authlib.GameProfile; import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.serialization.JsonOps; +import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.network.wired.WiredElement; +import dan200.computercraft.api.network.wired.WiredElementCapability; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.PeripheralCapability; 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; -import dan200.computercraft.shared.util.CapabilityUtil; import dan200.computercraft.shared.util.InventoryUtil; import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.ArgumentTypeInfos; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.common.ClientCommonPacketListener; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.TagKey; import net.minecraft.util.GsonHelper; -import net.minecraft.world.*; +import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -55,29 +60,28 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.common.ForgeHooks; -import net.minecraftforge.common.Tags; -import net.minecraftforge.common.ToolActions; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.crafting.CraftingHelper; -import net.minecraftforge.common.crafting.conditions.ICondition; -import net.minecraftforge.common.crafting.conditions.ModLoadedCondition; -import net.minecraftforge.common.extensions.IForgeMenuType; -import net.minecraftforge.event.ForgeEventFactory; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.FMLLoader; -import net.minecraftforge.items.wrapper.InvWrapper; -import net.minecraftforge.items.wrapper.SidedInvWrapper; -import net.minecraftforge.network.NetworkHooks; -import net.minecraftforge.registries.DeferredRegister; -import net.minecraftforge.registries.ForgeRegistry; -import net.minecraftforge.registries.RegistryManager; -import net.minecraftforge.registries.RegistryObject; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.neoforge.capabilities.BlockCapability; +import net.neoforged.neoforge.capabilities.BlockCapabilityCache; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.common.CommonHooks; +import net.neoforged.neoforge.common.Tags; +import net.neoforged.neoforge.common.ToolActions; +import net.neoforged.neoforge.common.conditions.ConditionalOps; +import net.neoforged.neoforge.common.conditions.ICondition; +import net.neoforged.neoforge.common.conditions.ModLoadedCondition; +import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; +import net.neoforged.neoforge.event.EventHooks; +import net.neoforged.neoforge.items.wrapper.InvWrapper; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; import javax.annotation.Nullable; -import java.util.*; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.function.*; @AutoService(dan200.computercraft.impl.PlatformHelper.class) @@ -92,50 +96,27 @@ public class PlatformHelperImpl implements PlatformHelper { return new ForgeConfigFile.Builder(); } - @Override - public ResourceLocation getRegistryKey(ResourceKey> registry, T object) { - var key = RegistryManager.ACTIVE.getRegistry(registry).getKey(object); - if (key == null) throw new IllegalArgumentException(object + " was not registered in " + registry); - return key; - } - - @Override - public T getRegistryObject(ResourceKey> registry, ResourceLocation id) { - var value = RegistryManager.ACTIVE.getRegistry(registry).getValue(id); - if (value == null) throw new IllegalArgumentException(id + " was not registered in " + registry); - return value; - } - - @Override - public RegistryWrappers.RegistryWrapper wrap(ResourceKey> key) { - return new RegistryWrapperImpl<>(key.location(), RegistryManager.ACTIVE.getRegistry(key)); - } - @Override public RegistrationHelper createRegistrationHelper(ResourceKey> registry) { return new RegistrationHelperImpl<>(DeferredRegister.create(registry, ComputerCraftAPI.MOD_ID)); } - @Nullable - @Override - public K tryGetRegistryObject(ResourceKey> registry, ResourceLocation id) { - return RegistryManager.ACTIVE.getRegistry(registry).getValue(id); - } - @Override public boolean shouldLoadResource(JsonObject object) { - return ICondition.shouldRegisterEntry(object); + return ICondition.conditionsMatched(JsonOps.INSTANCE, object); } @Override public void addRequiredModCondition(JsonObject object, String modId) { - var conditions = GsonHelper.getAsJsonArray(object, "forge:conditions", null); + // FIXME: Test this, though maybe this should be implemented a different way anyway? + var conditions = GsonHelper.getAsJsonArray(object, ConditionalOps.DEFAULT_CONDITIONS_KEY, null); if (conditions == null) { conditions = new JsonArray(); - object.add("forge:conditions", conditions); + object.add(ConditionalOps.DEFAULT_CONDITIONS_KEY, conditions); } - conditions.add(CraftingHelper.serialize(new ModLoadedCondition(modId))); + conditions.add(ICondition.CODEC.encodeStart(JsonOps.INSTANCE, new ModLoadedCondition(modId)).getOrThrow(false, x -> { + })); } @Override @@ -150,22 +131,27 @@ public class PlatformHelperImpl implements PlatformHelper { @Override public MenuType createMenuType(Function reader, ContainerData.Factory factory) { - return IForgeMenuType.create((id, player, data) -> factory.create(id, player, reader.apply(data))); + return IMenuTypeExtension.create((id, player, data) -> factory.create(id, player, reader.apply(data))); } @Override public void openMenu(Player player, MenuProvider owner, ContainerData menu) { - NetworkHooks.openScreen((ServerPlayer) player, owner, menu::toBytes); + ((ServerPlayer) player).openMenu(owner, menu::toBytes); } @Override - public > MessageType createMessageType(int id, ResourceLocation channel, Class klass, FriendlyByteBuf.Reader reader) { - return new NetworkHandler.MessageTypeImpl<>(id, klass, reader); + public > MessageType createMessageType(ResourceLocation id, FriendlyByteBuf.Reader reader) { + return new ForgeMessageType<>(id, b -> new ForgeMessageType.Payload<>(reader.apply(b))); } @Override - public Packet createPacket(NetworkMessage message) { - return NetworkHandler.createClientboundPacket(message); + public Packet createPacket(NetworkMessage message) { + return new ClientboundCustomPayloadPacket(ForgeMessageType.createPayload(message)); + } + + @Override + public void invalidateComponent(BlockEntity owner) { + owner.invalidateCapabilities(); } @Override @@ -175,15 +161,13 @@ public class PlatformHelperImpl implements PlatformHelper { @Override public ComponentAccess createWiredElementAccess(BlockEntity owner, Consumer invalidate) { - return new CapabilityAccess<>(owner, Capabilities.CAPABILITY_WIRED_ELEMENT, invalidate); + return new ComponentAccessImpl<>(owner, WiredElementCapability.get(), invalidate); } @Override public boolean hasWiredElementIn(Level level, BlockPos pos, Direction direction) { if (!level.isLoaded(pos)) return false; - - var blockEntity = level.getBlockEntity(pos.relative(direction)); - return blockEntity != null && blockEntity.getCapability(Capabilities.CAPABILITY_WIRED_ELEMENT, direction.getOpposite()).isPresent(); + return level.getCapability(WiredElementCapability.get(), pos.relative(direction), direction.getOpposite()) != null; } @Override @@ -194,30 +178,13 @@ public class PlatformHelperImpl implements PlatformHelper { @Nullable @Override public ContainerTransfer getContainer(ServerLevel level, BlockPos pos, Direction side) { - var block = level.getBlockState(pos); - if (block.getBlock() instanceof WorldlyContainerHolder holder) { - var container = holder.getContainer(block, level, pos); - return new ForgeContainerTransfer(new SidedInvWrapper(container, side)); - } - - var blockEntity = level.getBlockEntity(pos); - if (blockEntity != null) { - var inventory = blockEntity.getCapability(ForgeCapabilities.ITEM_HANDLER, side); - if (inventory.isPresent()) { - return new ForgeContainerTransfer(inventory.orElseThrow(NullPointerException::new)); - } - } + var inventory = level.getCapability(Capabilities.ItemHandler.BLOCK, pos, side); + if (inventory != null) return new ForgeContainerTransfer(inventory); var entity = InventoryUtil.getEntityContainer(level, pos, side); return entity == null ? null : new ForgeContainerTransfer(new InvWrapper(entity)); } - @Nullable - @Override - public CompoundTag getShareTag(ItemStack item) { - return item.getShareTag(); - } - @Override public RecipeIngredients getRecipeIngredients() { return new RecipeIngredients( @@ -260,7 +227,7 @@ public class PlatformHelperImpl implements PlatformHelper { @Override public int getBurnTime(ItemStack stack) { - return ForgeHooks.getBurnTime(stack, null); + return CommonHooks.getBurnTime(stack, null); } @Override @@ -275,20 +242,20 @@ public class PlatformHelperImpl implements PlatformHelper { @Override public List getRecipeRemainingItems(ServerPlayer player, Recipe recipe, CraftingContainer container) { - ForgeHooks.setCraftingPlayer(player); + CommonHooks.setCraftingPlayer(player); var result = recipe.getRemainingItems(container); - ForgeHooks.setCraftingPlayer(null); + CommonHooks.setCraftingPlayer(null); return result; } @Override public void onItemCrafted(ServerPlayer player, CraftingContainer container, ItemStack stack) { - ForgeEventFactory.firePlayerCraftingEvent(player, stack, container); + EventHooks.firePlayerCraftingEvent(player, stack, container); } @Override public boolean onNotifyNeighbour(Level level, BlockPos pos, BlockState block, Direction direction) { - return !ForgeEventFactory.onNeighborNotify(level, pos, block, EnumSet.of(direction), false).isCanceled(); + return !EventHooks.onNeighborNotify(level, pos, block, EnumSet.of(direction), false).isCanceled(); } @Override @@ -308,14 +275,14 @@ public class PlatformHelperImpl implements PlatformHelper { @Override public InteractionResult canAttackEntity(ServerPlayer player, Entity entity) { - return ForgeHooks.onPlayerAttackTarget(player, entity) ? InteractionResult.PASS : InteractionResult.SUCCESS; + return CommonHooks.onPlayerAttackTarget(player, entity) ? InteractionResult.PASS : InteractionResult.SUCCESS; } @Override public boolean interactWithEntity(ServerPlayer player, Entity entity, Vec3 hitPos) { // Our behaviour is slightly different here - we call onInteractEntityAt before the interact methods, while // Forge does the call afterwards (on the server, not on the client). - var interactAt = ForgeHooks.onInteractEntityAt(player, entity, hitPos, InteractionHand.MAIN_HAND); + var interactAt = CommonHooks.onInteractEntityAt(player, entity, hitPos, InteractionHand.MAIN_HAND); if (interactAt == null) { interactAt = entity.interactAt(player, hitPos.subtract(entity.position()), InteractionHand.MAIN_HAND); } @@ -327,7 +294,7 @@ public class PlatformHelperImpl implements PlatformHelper { public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate canUseBlock) { var level = player.level(); var pos = hit.getBlockPos(); - var event = ForgeHooks.onRightClickBlock(player, InteractionHand.MAIN_HAND, pos, hit); + var event = CommonHooks.onRightClickBlock(player, InteractionHand.MAIN_HAND, pos, hit); if (event.isCanceled()) return event.getCancellationResult(); var context = new UseOnContext(player, InteractionHand.MAIN_HAND, hit); @@ -345,63 +312,19 @@ public class PlatformHelperImpl implements PlatformHelper { return event.getUseItem() == Event.Result.DENY ? InteractionResult.PASS : stack.useOn(context); } - private record RegistryWrapperImpl( - ResourceLocation name, ForgeRegistry registry - ) implements RegistryWrappers.RegistryWrapper { + private record RegistrationHelperImpl(DeferredRegister registry) implements RegistrationHelper { @Override - public int getId(T object) { - return registry.getID(object); - } - - @Override - public ResourceLocation getKey(T object) { - var key = registry.getKey(object); - if (key == null) throw new IllegalStateException(object + " was not registered in " + name); - return key; - } - - @Override - public T get(ResourceLocation location) { - var object = registry.getValue(location); - if (object == null) throw new IllegalStateException(location + " was not registered in " + name); - return object; - } - - @Nullable - @Override - public T tryGet(ResourceLocation location) { - return registry.getValue(location); - } - - @Override - public @Nullable T byId(int id) { - return registry.getValue(id); - } - - @Override - public int size() { - return registry.getKeys().size(); - } - - @Override - public Iterator iterator() { - return registry.iterator(); - } - } - - private record RegistrationHelperImpl(DeferredRegister registry) implements RegistrationHelper { - @Override - public RegistryEntry register(String name, Supplier create) { + public RegistryEntry register(String name, Supplier create) { return new RegistryEntryImpl<>(registry().register(name, create)); } @Override public void register() { - registry().register(FMLJavaModLoadingContext.get().getModEventBus()); + registry().register(ComputerCraft.getEventBus()); } } - private record RegistryEntryImpl(RegistryObject object) implements RegistryEntry { + private record RegistryEntryImpl(DeferredHolder object) implements RegistryEntry { @Override public ResourceLocation id() { return object().getId(); @@ -413,61 +336,52 @@ public class PlatformHelperImpl implements PlatformHelper { } } - private abstract static class ComponentAccessImpl implements ComponentAccess { + private static class ComponentAccessImpl implements ComponentAccess { private final BlockEntity owner; - private final InvalidateCallback[] invalidators; + private final BlockCapability capability; + private final Consumer invalidate; + @SuppressWarnings({ "unchecked", "rawtypes" }) + final BlockCapabilityCache[] caches = new BlockCapabilityCache[6]; - ComponentAccessImpl(BlockEntity owner, Consumer invalidate) { + ComponentAccessImpl(BlockEntity owner, BlockCapability capability, Consumer invalidate) { this.owner = owner; - - // Generate a cache of invalidation functions so we can guarantee we only ever have one registered per - // capability - there's no way to remove these callbacks! - var invalidators = this.invalidators = new InvalidateCallback[6]; - for (var dir : Direction.values()) invalidators[dir.ordinal()] = () -> invalidate.accept(dir); + this.capability = capability; + this.invalidate = invalidate; } - @Nullable - protected abstract T get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate); - @Nullable @Override public T get(Direction direction) { - return get(getLevel(), owner.getBlockPos().relative(direction), direction.getOpposite(), invalidators[direction.ordinal()]); + var level = getLevel(); + var cache = caches[direction.ordinal()]; + if (cache == null) { + cache = caches[direction.ordinal()] = BlockCapabilityCache.create( + capability, level, owner.getBlockPos().relative(direction), + direction.getOpposite(), () -> !owner.isRemoved(), () -> this.invalidate.accept(direction) + ); + } + + return cache.getCapability(); } final ServerLevel getLevel() { return Objects.requireNonNull((ServerLevel) owner.getLevel(), "Block entity is not in a level"); } - } private static class PeripheralAccess extends ComponentAccessImpl { PeripheralAccess(BlockEntity owner, Consumer invalidate) { - super(owner, invalidate); + super(owner, PeripheralCapability.get(), invalidate); } @Nullable @Override - protected IPeripheral get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) { - return Peripherals.getPeripheral(world, pos, side, invalidate); - } - } + public IPeripheral get(Direction direction) { + var result = super.get(direction); + if (result != null) return result; - private static class CapabilityAccess extends ComponentAccessImpl { - private final Capability capability; - - CapabilityAccess(BlockEntity owner, Capability capability, Consumer invalidate) { - super(owner, invalidate); - this.capability = capability; - } - - @Nullable - @Override - protected T get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) { - if (!world.isLoaded(pos)) return null; - - var blockEntity = world.getBlockEntity(pos); - return blockEntity != null ? CapabilityUtil.unwrap(blockEntity.getCapability(capability, side), invalidate) : null; + var cache = caches[direction.ordinal()]; + return Peripherals.getGenericPeripheral(cache.level(), cache.pos(), cache.context(), cache.level().getBlockEntity(cache.pos())); } } } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityProvider.java b/projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityProvider.java deleted file mode 100644 index 4c5b2ca14..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.util; - -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.event.AttachCapabilitiesEvent; - -import javax.annotation.Nullable; -import java.util.Objects; -import java.util.function.BooleanSupplier; -import java.util.function.Supplier; - -/** - * A basic {@link ICapabilityProvider} which provides a single capability, returning the same instance for every - * direction. - *

- * This is designed for use with {@link AttachCapabilitiesEvent}, to attach individual capabilities to a specific - * block entity. - * - * @param The capability to provide. - */ -public final class CapabilityProvider implements ICapabilityProvider { - private final Capability cap; - private final Supplier supplier; - private final BooleanSupplier isRemoved; - private @Nullable LazyOptional instance; - - private CapabilityProvider(Capability cap, Supplier supplier, BooleanSupplier isRemoved) { - this.cap = Objects.requireNonNull(cap, "Capability cannot be null"); - this.supplier = supplier; - this.isRemoved = isRemoved; - } - - public static CapabilityProvider attach(AttachCapabilitiesEvent event, ResourceLocation id, Capability cap, Supplier instance) { - BooleanSupplier isRemoved - = event.getObject() instanceof BlockEntity be ? be::isRemoved - : event.getObject() instanceof Entity entity ? entity::isRemoved - : () -> true; - var provider = new CapabilityProvider<>(cap, instance, isRemoved); - event.addCapability(id, provider); - event.addListener(provider::invalidate); - return provider; - } - - public void invalidate() { - instance = CapabilityUtil.invalidate(instance); - } - - @Override - public LazyOptional getCapability(Capability cap, @Nullable Direction side) { - if (cap != this.cap || isRemoved.getAsBoolean()) return LazyOptional.empty(); - - var instance = this.instance; - if (instance == null) { - var created = supplier.get(); - instance = this.instance = created == null ? LazyOptional.empty() : LazyOptional.of(() -> created); - } - return instance.cast(); - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java b/projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java deleted file mode 100644 index c1e1dddf8..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.util; - -import dan200.computercraft.shared.platform.InvalidateCallback; -import net.minecraft.core.Direction; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.util.LazyOptional; - -import javax.annotation.Nullable; - -public final class CapabilityUtil { - private CapabilityUtil() { - } - - @Nullable - public static LazyOptional invalidate(@Nullable LazyOptional cap) { - if (cap != null) cap.invalidate(); - return null; - } - - public static void invalidate(@Nullable LazyOptional[] caps) { - if (caps == null) return; - - for (var i = 0; i < caps.length; i++) { - var cap = caps[i]; - if (cap != null) cap.invalidate(); - caps[i] = null; - } - } - - @Nullable - public static T unwrap(LazyOptional p, InvalidateCallback invalidate) { - if (!p.isPresent()) return null; - - p.addListener(invalidate.castConsumer()); - return p.orElseThrow(NullPointerException::new); - } - - /** - * Find a capability, preferring the internal/null side but falling back to a given side if a mod doesn't support - * the internal one. - * - * @param provider The capability provider to get the capability from. - * @param capability The capability to get. - * @param side The side we'll fall back to. - * @param The type of the underlying capability. - * @return The extracted capability, if present. - */ - public static LazyOptional getCapability(ICapabilityProvider provider, Capability capability, Direction side) { - var cap = provider.getCapability(capability); - return cap.isPresent() ? cap : provider.getCapability(capability, side); - } -} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/util/SidedCapabilityProvider.java b/projects/forge/src/main/java/dan200/computercraft/shared/util/SidedCapabilityProvider.java deleted file mode 100644 index eb50ef21a..000000000 --- a/projects/forge/src/main/java/dan200/computercraft/shared/util/SidedCapabilityProvider.java +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.util; - -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.event.AttachCapabilitiesEvent; - -import javax.annotation.Nullable; -import java.util.Objects; -import java.util.function.BooleanSupplier; - -/** - * A {@link ICapabilityProvider} which provides a different single capability, with different instances for each - * direction. - *

- * This is designed for use with {@link AttachCapabilitiesEvent}, to attach individual capabilities to a specific - * block entity. - * - * @param The capability to provide. - * @see CapabilityProvider - */ -public final class SidedCapabilityProvider implements ICapabilityProvider { - private final Capability cap; - private final Provider supplier; - private final BooleanSupplier isRemoved; - private @Nullable LazyOptional[] instances; - - private SidedCapabilityProvider(Capability cap, Provider supplier, BooleanSupplier isRemoved) { - this.cap = Objects.requireNonNull(cap, "Capability cannot be null"); - this.supplier = supplier; - this.isRemoved = isRemoved; - } - - public static SidedCapabilityProvider attach(AttachCapabilitiesEvent event, ResourceLocation id, Capability cap, Provider supplier) { - BooleanSupplier isRemoved - = event.getObject() instanceof BlockEntity be ? be::isRemoved - : event.getObject() instanceof Entity entity ? entity::isRemoved - : () -> true; - var provider = new SidedCapabilityProvider<>(cap, supplier, isRemoved); - event.addCapability(id, provider); - event.addListener(provider::invalidate); - return provider; - } - - public void invalidate() { - CapabilityUtil.invalidate(instances); - } - - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - public LazyOptional getCapability(Capability cap, @Nullable Direction side) { - if (cap != this.cap || isRemoved.getAsBoolean()) return LazyOptional.empty(); - - var instances = this.instances; - if (instances == null) instances = this.instances = new LazyOptional[6]; - - var index = side == null ? 6 : side.ordinal(); - - var instance = instances[index]; - if (instance == null) { - var created = supplier.get(side); - instance = instances[index] = created == null ? LazyOptional.empty() : LazyOptional.of(() -> created); - } - - return instance.cast(); - } - - public interface Provider { - @Nullable - T get(@Nullable Direction direction); - } -} diff --git a/projects/forge/src/main/resources/META-INF/accesstransformer.cfg b/projects/forge/src/main/resources/META-INF/accesstransformer.cfg index a2af2da8f..b5da3bbda 100644 --- a/projects/forge/src/main/resources/META-INF/accesstransformer.cfg +++ b/projects/forge/src/main/resources/META-INF/accesstransformer.cfg @@ -3,33 +3,33 @@ # SPDX-License-Identifier: MPL-2.0 # DirectVertexBuffer -protected com.mojang.blaze3d.vertex.VertexBuffer f_231217_ # vertexBufferId -protected com.mojang.blaze3d.vertex.VertexBuffer f_166861_ # indexType -protected com.mojang.blaze3d.vertex.VertexBuffer f_166863_ # indexCount -protected com.mojang.blaze3d.vertex.VertexBuffer f_166864_ # mode -protected com.mojang.blaze3d.vertex.VertexBuffer f_166865_ # sequentialIndices -protected com.mojang.blaze3d.vertex.VertexBuffer f_85917_ # format +protected com.mojang.blaze3d.vertex.VertexBuffer vertexBufferId +protected com.mojang.blaze3d.vertex.VertexBuffer indexType +protected com.mojang.blaze3d.vertex.VertexBuffer indexCount +protected com.mojang.blaze3d.vertex.VertexBuffer mode +protected com.mojang.blaze3d.vertex.VertexBuffer sequentialIndices +protected com.mojang.blaze3d.vertex.VertexBuffer format # ClientTableFormatter -public net.minecraft.client.gui.components.ChatComponent f_93760_ # allMessages -public net.minecraft.client.gui.components.ChatComponent m_241120_()V # refreshTrimmedMessage +public net.minecraft.client.gui.components.ChatComponent allMessages +public net.minecraft.client.gui.components.ChatComponent refreshTrimmedMessage()V # ItemPocketRenderer/ItemPrintoutRenderer -public net.minecraft.client.renderer.ItemInHandRenderer m_109312_(F)F # calculateMapTilt -public net.minecraft.client.renderer.ItemInHandRenderer m_109361_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/HumanoidArm;)V # renderMapHand -public net.minecraft.client.renderer.ItemInHandRenderer m_109346_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IFFLnet/minecraft/world/entity/HumanoidArm;)V # renderPlayerArm +public net.minecraft.client.renderer.ItemInHandRenderer calculateMapTilt(F)F +public net.minecraft.client.renderer.ItemInHandRenderer renderMapHand(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/HumanoidArm;)V +public net.minecraft.client.renderer.ItemInHandRenderer renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IFFLnet/minecraft/world/entity/HumanoidArm;)V # SpeakerInstance/SpeakerManager -public com.mojang.blaze3d.audio.Channel m_83652_(I)V # pumpBuffers -public net.minecraft.client.sounds.SoundEngine f_120223_ # executor +public com.mojang.blaze3d.audio.Channel pumpBuffers(I)V +public net.minecraft.client.sounds.SoundEngine executor # Data generators -public net.minecraft.data.models.BlockModelGenerators f_124477_ # blockStateOutput -public net.minecraft.data.models.BlockModelGenerators f_124478_ # modelOutput -public net.minecraft.data.models.BlockModelGenerators m_124797_(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/resources/ResourceLocation;)V # delegateItemModel -public net.minecraft.data.models.BlockModelGenerators m_124519_(Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;)V # delegateItemModel -public net.minecraft.data.models.BlockModelGenerators m_124744_(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/data/models/model/TexturedModel$Provider;)V # createHorizontallyRotatedBlock -public net.minecraft.data.models.ItemModelGenerators f_125080_ # output -public net.minecraft.data.models.ItemModelGenerators m_125091_(Lnet/minecraft/world/item/Item;Ljava/lang/String;Lnet/minecraft/data/models/model/ModelTemplate;)V # generateFlatItem -public net.minecraft.data.models.ItemModelGenerators m_125088_(Lnet/minecraft/world/item/Item;Lnet/minecraft/data/models/model/ModelTemplate;)V # generateFlatItem -public net.minecraft.data.models.model.TextureSlot m_125898_(Ljava/lang/String;)Lnet/minecraft/data/models/model/TextureSlot; # create +public net.minecraft.data.models.BlockModelGenerators blockStateOutput +public net.minecraft.data.models.BlockModelGenerators modelOutput +public net.minecraft.data.models.BlockModelGenerators delegateItemModel(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/resources/ResourceLocation;)V +public net.minecraft.data.models.BlockModelGenerators delegateItemModel(Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;)V +public net.minecraft.data.models.BlockModelGenerators createHorizontallyRotatedBlock(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/data/models/model/TexturedModel$Provider;)V +public net.minecraft.data.models.ItemModelGenerators output +public net.minecraft.data.models.ItemModelGenerators generateFlatItem(Lnet/minecraft/world/item/Item;Lnet/minecraft/data/models/model/ModelTemplate;)V +public net.minecraft.data.models.ItemModelGenerators generateFlatItem(Lnet/minecraft/world/item/Item;Ljava/lang/String;Lnet/minecraft/data/models/model/ModelTemplate;)V +public net.minecraft.data.models.model.TextureSlot create(Ljava/lang/String;)Lnet/minecraft/data/models/model/TextureSlot; diff --git a/projects/forge/src/main/resources/META-INF/mods.toml b/projects/forge/src/main/resources/META-INF/mods.toml index ea4350a4f..7e3e156c6 100644 --- a/projects/forge/src/main/resources/META-INF/mods.toml +++ b/projects/forge/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MPL-2.0 modLoader="javafml" -loaderVersion="[47,48)" +loaderVersion="[1,)" issueTrackerURL="https://github.com/cc-tweaked/CC-Tweaked/issues" logoFile="pack.png" @@ -24,8 +24,11 @@ CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles a ''' [[dependencies.computercraft]] - modId="forge" - mandatory=true - versionRange="[${forgeVersion},48)" + modId="neoforge" + type="required" + versionRange="[${neoVersion},20.5)" ordering="NONE" side="BOTH" + +[[mixins]] +config = "computercraft-client.forge.mixins.json" diff --git a/projects/forge/src/test/java/dan200/computercraft/shared/platform/ForgeContainerTransferTest.java b/projects/forge/src/test/java/dan200/computercraft/shared/platform/ForgeContainerTransferTest.java index c2465f00e..b31a7ccc1 100644 --- a/projects/forge/src/test/java/dan200/computercraft/shared/platform/ForgeContainerTransferTest.java +++ b/projects/forge/src/test/java/dan200/computercraft/shared/platform/ForgeContainerTransferTest.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared.platform; import dan200.computercraft.test.shared.WithMinecraft; import dan200.computercraft.test.shared.platform.ContainerTransferContract; import net.minecraft.world.Container; -import net.minecraftforge.items.wrapper.InvWrapper; +import net.neoforged.neoforge.items.wrapper.InvWrapper; @WithMinecraft public class ForgeContainerTransferTest implements ContainerTransferContract { diff --git a/projects/forge/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java b/projects/forge/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java index ad295ffd1..e1bfcb924 100644 --- a/projects/forge/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java +++ b/projects/forge/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java @@ -5,35 +5,34 @@ package dan200.computercraft.gametest.core; import dan200.computercraft.export.Exporter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.RegisterClientCommandsEvent; -import net.minecraftforge.client.event.ScreenEvent; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.RegisterGameTestsEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.server.ServerStartedEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.fml.DistExecutor; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.EventPriority; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent; +import net.neoforged.neoforge.client.event.ScreenEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.RegisterGameTestsEvent; +import net.neoforged.neoforge.event.TickEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; @Mod("cctest") public class TestMod { - public TestMod() { + public TestMod(IEventBus modBus) { TestHooks.init(); - var bus = MinecraftForge.EVENT_BUS; + var bus = NeoForge.EVENT_BUS; bus.addListener(EventPriority.LOW, (ServerStartedEvent e) -> TestHooks.onServerStarted(e.getServer())); bus.addListener((RegisterCommandsEvent e) -> CCTestCommand.register(e.getDispatcher())); - DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> TestMod::onInitializeClient); + if (FMLEnvironment.dist == Dist.CLIENT) TestMod.onInitializeClient(); - var modBus = FMLJavaModLoadingContext.get().getModEventBus(); modBus.addListener((RegisterGameTestsEvent event) -> TestHooks.loadTests(event::register)); } private static void onInitializeClient() { - var bus = MinecraftForge.EVENT_BUS; + var bus = NeoForge.EVENT_BUS; bus.addListener((TickEvent.ServerTickEvent e) -> { if (e.phase == TickEvent.Phase.START) ClientTestHooks.onServerTick(e.getServer()); diff --git a/projects/forge/src/testMod/resources/META-INF/mods.toml b/projects/forge/src/testMod/resources/META-INF/mods.toml index a994a60a3..accd403a6 100644 --- a/projects/forge/src/testMod/resources/META-INF/mods.toml +++ b/projects/forge/src/testMod/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MPL-2.0 modLoader="javafml" -loaderVersion="[30,)" +loaderVersion="[1,)" issueTrackerURL="https://github.com/cc-tweaked/CC-Tweaked/issues" displayURL="https://github.com/cc-tweaked/CC-Tweaked" @@ -22,7 +22,10 @@ A test framework for ensuring CC: Tweaked works correctly. [[dependencies.cctest]] modId="computercraft" -mandatory=true +type="required" versionRange="[1.0,)" ordering="AFTER" side="BOTH" + +[[mixins]] +config="computercraft-gametest.mixins.json" diff --git a/projects/lints/build.gradle.kts b/projects/lints/build.gradle.kts index 210b44d7f..ae7d16714 100644 --- a/projects/lints/build.gradle.kts +++ b/projects/lints/build.gradle.kts @@ -8,9 +8,10 @@ plugins { } repositories { - maven("https://maven.minecraftforge.net") { - content { - includeGroup("net.minecraftforge") + exclusiveContent { + forRepositories(maven("https://maven.neoforged.net/releases")) + filter { + includeGroup("net.neoforged") includeGroup("cpw.mods") } } @@ -23,7 +24,7 @@ dependencies { testImplementation(libs.bundles.test) testImplementation(libs.errorProne.testHelpers) - testImplementation(libs.forgeSpi) + testImplementation(libs.neoForgeSpi) testCompileOnly(project(":core-api")) testRuntimeOnly(libs.bundles.testRuntime) } diff --git a/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt b/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt index eb869e1a5..e01748111 100644 --- a/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt +++ b/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt @@ -93,7 +93,7 @@ internal class SideProvider { private fun fromAnnotationStream(annotations: Stream) = annotations.flatMap { when (it.annotationType.toString()) { - "net.minecraftforge.api.distmarker.OnlyIn" -> { + "net.neoforged.api.distmarker.OnlyIn" -> { val value = it.getValue("value", AnnotationGetters.enum())!! Stream.of(value) } diff --git a/projects/lints/src/test/java/cc/tweaked/linter/AnnotatedClientClass.java b/projects/lints/src/test/java/cc/tweaked/linter/AnnotatedClientClass.java index 4f8171a46..f9e6a35d3 100644 --- a/projects/lints/src/test/java/cc/tweaked/linter/AnnotatedClientClass.java +++ b/projects/lints/src/test/java/cc/tweaked/linter/AnnotatedClientClass.java @@ -4,8 +4,8 @@ package cc.tweaked.linter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class AnnotatedClientClass { diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b5807cc8..774ee0a66 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,25 +8,14 @@ pluginManagement { mavenCentral() gradlePluginPortal() - maven("https://maven.minecraftforge.net") { - name = "Forge" + maven("https://maven.neoforged.net/releases") { + name = "NeoForge" content { includeGroup("net.minecraftforge") - includeGroup("net.minecraftforge.gradle") - } - } - - maven("https://maven.parchmentmc.org") { - name = "Librarian" - content { - includeGroupByRegex("^org\\.parchmentmc.*") - } - } - - maven("https://repo.spongepowered.org/repository/maven-public/") { - name = "Sponge" - content { - includeGroup("org.spongepowered") + includeGroup("net.neoforged") + includeGroup("net.neoforged.gradle") + includeModule("codechicken", "DiffPatch") + includeModule("net.covers1624", "Quack") } } @@ -45,14 +34,6 @@ pluginManagement { } } } - - resolutionStrategy { - eachPlugin { - if (requested.id.id == "org.spongepowered.mixin") { - useModule("org.spongepowered:mixingradle:${requested.version}") - } - } - } } val mcVersion: String by settings