mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-14 05:44:52 +00:00
Update to 1.20.5
- Switch most network code to use StreamCodec - Turtle/pocket computer upgrades now use DataComponentPatch instead of raw NBT.
This commit is contained in:
parent
bd2fd9d4c8
commit
07684080d5
4
.github/workflows/main-ci.yml
vendored
4
.github/workflows/main-ci.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
- name: 📥 Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: 📥 Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
@ -87,7 +87,7 @@ jobs:
|
|||||||
- name: 📥 Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: 📥 Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
|
2
.github/workflows/make-doc.yml
vendored
2
.github/workflows/make-doc.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
- name: Set up Java
|
- name: Set up Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
|
@ -78,8 +78,16 @@ dependencies {
|
|||||||
// Configure default JavaCompile tasks with our arguments.
|
// Configure default JavaCompile tasks with our arguments.
|
||||||
sourceSets.all {
|
sourceSets.all {
|
||||||
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||||
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
|
||||||
options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing"))
|
options.compilerArgs.addAll(
|
||||||
|
listOf(
|
||||||
|
"-Xlint",
|
||||||
|
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
||||||
|
"-Xlint:-processing",
|
||||||
|
// We violate this pattern too often for it to be a helpful warning. Something to improve one day!
|
||||||
|
"-Xlint:-this-escape",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
options.errorprone {
|
options.errorprone {
|
||||||
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
||||||
@ -148,7 +156,7 @@ tasks.javadoc {
|
|||||||
options {
|
options {
|
||||||
val stdOptions = this as StandardJavadocDocletOptions
|
val stdOptions = this as StandardJavadocDocletOptions
|
||||||
stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
|
stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
|
||||||
stdOptions.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
stdOptions.links("https://docs.oracle.com/en/java/javase/21/docs/api/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URL
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
abstract class CCTweakedExtension(
|
abstract class CCTweakedExtension(
|
||||||
@ -226,12 +225,12 @@ abstract class CCTweakedExtension(
|
|||||||
* where possible.
|
* where possible.
|
||||||
*/
|
*/
|
||||||
fun downloadFile(label: String, url: String): File {
|
fun downloadFile(label: String, url: String): File {
|
||||||
val url = URL(url)
|
val url = URI(url)
|
||||||
val path = File(url.path)
|
val path = File(url.path)
|
||||||
|
|
||||||
project.repositories.ivy {
|
project.repositories.ivy {
|
||||||
name = label
|
name = label
|
||||||
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
setUrl(URI(url.scheme, url.userInfo, url.host, url.port, path.parent, null, null))
|
||||||
patternLayout {
|
patternLayout {
|
||||||
artifact("[artifact].[ext]")
|
artifact("[artifact].[ext]")
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,6 @@ class CCTweakedPlugin : Plugin<Project> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val JAVA_VERSION = JavaLanguageVersion.of(17)
|
val JAVA_VERSION = JavaLanguageVersion.of(21)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
|
|
||||||
<!-- Allow underscores in our test classes. -->
|
<!-- Allow underscores in our test classes. -->
|
||||||
<suppress checks="MethodName" files=".*(Contract|Test).java" />
|
<suppress checks="MethodName" files=".*(Contract|Test).java" />
|
||||||
|
|
||||||
|
<!-- Allow underscores in Mixin classes -->
|
||||||
|
<suppress checks="TypeName" files=".*[\\/]mixin[\\/].*.java" />
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
@ -13,4 +13,4 @@ isUnstable=true
|
|||||||
modVersion=1.110.2
|
modVersion=1.110.2
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.20.4
|
mcVersion=1.20.5
|
||||||
|
@ -7,26 +7,26 @@
|
|||||||
# Minecraft
|
# Minecraft
|
||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
||||||
fabric-api = "0.93.1+1.20.4"
|
fabric-api = "0.97.6+1.20.5"
|
||||||
fabric-loader = "0.15.3"
|
fabric-loader = "0.15.10"
|
||||||
neoForge = "20.4.210"
|
neoForge = "20.5.0-beta"
|
||||||
neoForgeSpi = "8.0.1"
|
neoForgeSpi = "8.0.1"
|
||||||
mixin = "0.8.5"
|
mixin = "0.8.5"
|
||||||
parchment = "2023.12.31"
|
parchment = "2024.04.14"
|
||||||
parchmentMc = "1.20.3"
|
parchmentMc = "1.20.4"
|
||||||
yarn = "1.20.4+build.3"
|
yarn = "1.20.5+build.1"
|
||||||
|
|
||||||
# Core dependencies (these versions are tied to the version Minecraft uses)
|
# Core dependencies (these versions are tied to the version Minecraft uses)
|
||||||
fastutil = "8.5.12"
|
fastutil = "8.5.12"
|
||||||
guava = "32.1.2-jre"
|
guava = "32.1.2-jre"
|
||||||
netty = "4.1.97.Final"
|
netty = "4.1.97.Final"
|
||||||
slf4j = "2.0.7"
|
slf4j = "2.0.9"
|
||||||
|
|
||||||
# Core dependencies (independent of Minecraft)
|
# Core dependencies (independent of Minecraft)
|
||||||
asm = "9.6"
|
asm = "9.6"
|
||||||
autoService = "1.1.1"
|
autoService = "1.1.1"
|
||||||
checkerFramework = "3.42.0"
|
checkerFramework = "3.42.0"
|
||||||
cobalt = "0.9.3"
|
cobalt = { strictly = "0.9.3" }
|
||||||
commonsCli = "1.6.0"
|
commonsCli = "1.6.0"
|
||||||
jetbrainsAnnotations = "24.1.0"
|
jetbrainsAnnotations = "24.1.0"
|
||||||
jsr305 = "3.0.2"
|
jsr305 = "3.0.2"
|
||||||
@ -46,6 +46,7 @@ oculus = "1.2.5"
|
|||||||
rei = "14.0.688"
|
rei = "14.0.688"
|
||||||
rubidium = "0.6.1"
|
rubidium = "0.6.1"
|
||||||
sodium = "mc1.20-0.4.10"
|
sodium = "mc1.20-0.4.10"
|
||||||
|
mixinExtra = "0.3.5"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
@ -66,7 +67,7 @@ ideaExt = "1.1.7"
|
|||||||
illuaminate = "0.1.0-71-g378d86e"
|
illuaminate = "0.1.0-71-g378d86e"
|
||||||
lwjgl = "3.3.3"
|
lwjgl = "3.3.3"
|
||||||
minotaur = "2.8.7"
|
minotaur = "2.8.7"
|
||||||
neoGradle = "7.0.100"
|
neoGradle = "7.0.107"
|
||||||
nullAway = "0.10.25"
|
nullAway = "0.10.25"
|
||||||
spotless = "6.23.3"
|
spotless = "6.23.3"
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
@ -109,6 +110,7 @@ jei-api = { module = "mezz.jei:jei-1.20.4-common-api", version.ref = "jei" }
|
|||||||
jei-fabric = { module = "mezz.jei:jei-1.20.4-fabric", version.ref = "jei" }
|
jei-fabric = { module = "mezz.jei:jei-1.20.4-fabric", version.ref = "jei" }
|
||||||
jei-forge = { module = "mezz.jei:jei-1.20.4-forge", version.ref = "jei" }
|
jei-forge = { module = "mezz.jei:jei-1.20.4-forge", version.ref = "jei" }
|
||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
|
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
|
||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
||||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||||
@ -179,7 +181,7 @@ externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
|||||||
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
||||||
externalMods-forge-runtime = []
|
externalMods-forge-runtime = []
|
||||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
||||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
externalMods-fabric-runtime = [] # ["jei-fabric", "modmenu"]
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
|
@ -9,7 +9,7 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
|
|||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -38,7 +38,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
* @param data Upgrade data instance for current turtle side.
|
* @param data Upgrade data instance for current turtle side.
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
* @return The model that you wish to be used to render your upgrade.
|
||||||
*/
|
*/
|
||||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data);
|
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of models that this turtle modeller depends on.
|
* Get a list of models that this turtle modeller depends on.
|
||||||
@ -55,7 +55,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(CompoundTag)}
|
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(DataComponentPatch)}
|
||||||
* upgrade item}.
|
* upgrade item}.
|
||||||
* <p>
|
* <p>
|
||||||
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
||||||
@ -80,7 +80,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||||
return new TurtleUpgradeModeller<>() {
|
return new TurtleUpgradeModeller<>() {
|
||||||
@Override
|
@Override
|
||||||
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
|
||||||
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -36,7 +36,7 @@ final class TurtleUpgradeModellers {
|
|||||||
|
|
||||||
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||||
@Override
|
@Override
|
||||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
|
||||||
var stack = upgrade.getUpgradeItem(data);
|
var stack = upgrade.getUpgradeItem(data);
|
||||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
||||||
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
||||||
|
@ -6,10 +6,12 @@ package dan200.computercraft.api;
|
|||||||
|
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.ItemTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.context.UseOnContext;
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
@ -35,6 +37,14 @@ public class ComputerCraftTags {
|
|||||||
*/
|
*/
|
||||||
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
|
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Items which can be dyed.
|
||||||
|
* <p>
|
||||||
|
* This is similar to {@link ItemTags#DYEABLE}, but allows cleaning the item with a sponge, rather than in a
|
||||||
|
* cauldron.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> DYEABLE = make("dyeable");
|
||||||
|
|
||||||
private static TagKey<Item> make(String name) {
|
private static TagKey<Item> make(String name) {
|
||||||
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
||||||
}
|
}
|
||||||
@ -75,8 +85,8 @@ public class ComputerCraftTags {
|
|||||||
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block which can be {@linkplain BlockState#use(Level, Player, InteractionHand, BlockHitResult) used} when
|
* Block which can be {@linkplain BlockState#useItemOn(ItemStack, Level, Player, InteractionHand, BlockHitResult) used}
|
||||||
* calling {@code turtle.place()}.
|
* when calling {@code turtle.place()}.
|
||||||
*/
|
*/
|
||||||
public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
|
public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@ -67,18 +67,19 @@ public interface IPocketAccess {
|
|||||||
* This is persisted between computer reboots and chunk loads.
|
* This is persisted between computer reboots and chunk loads.
|
||||||
*
|
*
|
||||||
* @return The upgrade's NBT.
|
* @return The upgrade's NBT.
|
||||||
* @see #updateUpgradeNBTData()
|
* @see #setUpgradeData(DataComponentPatch)
|
||||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
* @see UpgradeBase#getUpgradeItem(DataComponentPatch)
|
||||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||||
*/
|
*/
|
||||||
CompoundTag getUpgradeNBTData();
|
DataComponentPatch getUpgradeData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the upgrade-specific NBT as dirty.
|
* Update the upgrade-specific data.
|
||||||
*
|
*
|
||||||
* @see #getUpgradeNBTData()
|
* @param data The new upgrade data.
|
||||||
|
* @see #getUpgradeData()
|
||||||
*/
|
*/
|
||||||
void updateUpgradeNBTData();
|
void setUpgradeData(DataComponentPatch data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the current peripheral and create a new one.
|
* Remove the current peripheral and create a new one.
|
||||||
|
@ -12,7 +12,7 @@ import dan200.computercraft.api.upgrades.UpgradeBase;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
@ -229,37 +229,22 @@ public interface ITurtleAccess {
|
|||||||
* @param side The side to get the upgrade from.
|
* @param side The side to get the upgrade from.
|
||||||
* @return The upgrade on the specified side of the turtle, if there is one.
|
* @return The upgrade on the specified side of the turtle, if there is one.
|
||||||
* @see #getUpgradeWithData(TurtleSide)
|
* @see #getUpgradeWithData(TurtleSide)
|
||||||
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
* @see #setUpgrade(TurtleSide, UpgradeData)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
ITurtleUpgrade getUpgrade(TurtleSide side);
|
ITurtleUpgrade getUpgrade(TurtleSide side);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeNBTData(TurtleSide)
|
* Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeData(TurtleSide)
|
||||||
* update data}.
|
* update data}.
|
||||||
*
|
*
|
||||||
* @param side The side to get the upgrade from.
|
* @param side The side to get the upgrade from.
|
||||||
* @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
|
* @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
|
||||||
* @see #getUpgradeWithData(TurtleSide)
|
* @see #getUpgradeWithData(TurtleSide)
|
||||||
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
* @see #setUpgrade(TurtleSide, UpgradeData)
|
||||||
*/
|
*/
|
||||||
default @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side) {
|
@Nullable
|
||||||
var upgrade = getUpgrade(side);
|
UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side);
|
||||||
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData(side));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
|
||||||
*
|
|
||||||
* @param side The side to set the upgrade on.
|
|
||||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
|
||||||
* @see #getUpgrade(TurtleSide)
|
|
||||||
* @deprecated Use {@link #setUpgradeWithData(TurtleSide, UpgradeData)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
default void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
|
|
||||||
setUpgradeWithData(side, upgrade == null ? null : UpgradeData.ofDefault(upgrade));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the upgrade for a given side and its upgrade data.
|
* Set the upgrade for a given side and its upgrade data.
|
||||||
@ -268,7 +253,7 @@ public interface ITurtleAccess {
|
|||||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||||
* @see #getUpgradeWithData(TurtleSide)
|
* @see #getUpgradeWithData(TurtleSide)
|
||||||
*/
|
*/
|
||||||
void setUpgradeWithData(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
|
void setUpgrade(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
||||||
@ -282,23 +267,23 @@ public interface ITurtleAccess {
|
|||||||
/**
|
/**
|
||||||
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
|
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
|
||||||
* <p>
|
* <p>
|
||||||
* This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You must
|
* This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You can
|
||||||
* call {@link #updateUpgradeNBTData(TurtleSide)} after modifying it.
|
* call {@link #setUpgrade(TurtleSide, UpgradeData)} to modify it.
|
||||||
*
|
*
|
||||||
* @param side The side to get the upgrade data for.
|
* @param side The side to get the upgrade data for.
|
||||||
* @return The upgrade-specific data.
|
* @return The upgrade-specific data.
|
||||||
* @see #updateUpgradeNBTData(TurtleSide)
|
* @see #setUpgradeData(TurtleSide, DataComponentPatch)
|
||||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
* @see UpgradeBase#getUpgradeItem(DataComponentPatch)
|
||||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||||
*/
|
*/
|
||||||
CompoundTag getUpgradeNBTData(TurtleSide side);
|
DataComponentPatch getUpgradeData(TurtleSide side);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the
|
* Update the upgrade-specific data.
|
||||||
* client and persisted.
|
|
||||||
*
|
*
|
||||||
* @param side The side to mark dirty.
|
* @param side The side to set the upgrade data for.
|
||||||
* @see #updateUpgradeNBTData(TurtleSide)
|
* @param data The new upgrade data.
|
||||||
|
* @see #getUpgradeData(TurtleSide)
|
||||||
*/
|
*/
|
||||||
void updateUpgradeNBTData(TurtleSide side);
|
void setUpgradeData(TurtleSide side, DataComponentPatch data);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
|||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -133,7 +133,7 @@ public interface ITurtleUpgrade extends UpgradeBase {
|
|||||||
* @param upgradeData Data that currently stored for this upgrade
|
* @param upgradeData Data that currently stored for this upgrade
|
||||||
* @return Filtered version of this data.
|
* @return Filtered version of this data.
|
||||||
*/
|
*/
|
||||||
default CompoundTag getPersistedData(CompoundTag upgradeData) {
|
default DataComponentPatch getPersistedData(DataComponentPatch upgradeData) {
|
||||||
return upgradeData;
|
return upgradeData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.turtle;
|
package dan200.computercraft.api.turtle;
|
||||||
|
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.util.StringRepresentable;
|
import net.minecraft.util.StringRepresentable;
|
||||||
import net.minecraft.world.entity.EquipmentSlot;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,7 +21,7 @@ public enum TurtleToolDurability implements StringRepresentable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
|
* The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
|
||||||
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
* {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
|
||||||
*/
|
*/
|
||||||
WHEN_ENCHANTED("when_enchanted"),
|
WHEN_ENCHANTED("when_enchanted"),
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ import dan200.computercraft.api.ComputerCraftTags;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.impl.RegistryHelper;
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.data.DataGenerator;
|
import net.minecraft.data.DataGenerator;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.entity.EquipmentSlot;
|
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@ -111,7 +111,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
||||||
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
* {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
|
||||||
*
|
*
|
||||||
* @return The tool builder, for further use.
|
* @return The tool builder, for further use.
|
||||||
*/
|
*/
|
||||||
|
@ -10,12 +10,10 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
|
|||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
|
* Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
|
||||||
*/
|
*/
|
||||||
@ -56,8 +54,8 @@ public interface UpgradeBase {
|
|||||||
/**
|
/**
|
||||||
* Returns the item stack representing a currently equipped turtle upgrade.
|
* Returns the item stack representing a currently equipped turtle upgrade.
|
||||||
* <p>
|
* <p>
|
||||||
* While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} and
|
* While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeData(TurtleSide)} and
|
||||||
* {@link IPocketAccess#getUpgradeNBTData()}}, by default this data is discarded when an upgrade is unequipped,
|
* {@link IPocketAccess#getUpgradeData()}}, by default this data is discarded when an upgrade is unequipped,
|
||||||
* and the original item stack is returned.
|
* and the original item stack is returned.
|
||||||
* <p>
|
* <p>
|
||||||
* By overriding this method, you can create a new {@link ItemStack} which contains enough data to
|
* By overriding this method, you can create a new {@link ItemStack} which contains enough data to
|
||||||
@ -69,24 +67,24 @@ public interface UpgradeBase {
|
|||||||
* @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
|
* @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
|
||||||
* @return The item stack returned when unequipping.
|
* @return The item stack returned when unequipping.
|
||||||
*/
|
*/
|
||||||
default ItemStack getUpgradeItem(CompoundTag upgradeData) {
|
default ItemStack getUpgradeItem(DataComponentPatch upgradeData) {
|
||||||
return getCraftingItem();
|
return getCraftingItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract upgrade data from an {@link ItemStack}.
|
* Extract upgrade data from an {@link ItemStack}.
|
||||||
* <p>
|
* <p>
|
||||||
* This upgrade data will be available with {@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} or
|
* This upgrade data will be available with {@link ITurtleAccess#getUpgradeData(TurtleSide)} or
|
||||||
* {@link IPocketAccess#getUpgradeNBTData()}.
|
* {@link IPocketAccess#getUpgradeData()}.
|
||||||
* <p>
|
* <p>
|
||||||
* This should be an inverse to {@link #getUpgradeItem(CompoundTag)}.
|
* This should be an inverse to {@link #getUpgradeItem(DataComponentPatch)}.
|
||||||
*
|
*
|
||||||
* @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
|
* @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
|
||||||
* {@link #getCraftingItem()}.
|
* {@link #getCraftingItem()}.
|
||||||
* @return The upgrade data that should be set on the turtle or pocket computer.
|
* @return The upgrade data that should be set on the turtle or pocket computer.
|
||||||
*/
|
*/
|
||||||
default CompoundTag getUpgradeData(ItemStack stack) {
|
default DataComponentPatch getUpgradeData(ItemStack stack) {
|
||||||
return new CompoundTag();
|
return DataComponentPatch.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,26 +94,15 @@ public interface UpgradeBase {
|
|||||||
* the original stack. In order to prevent people losing items with enchantments (or
|
* the original stack. In order to prevent people losing items with enchantments (or
|
||||||
* repairing items with non-0 damage), we impose additional checks on the item.
|
* repairing items with non-0 damage), we impose additional checks on the item.
|
||||||
* <p>
|
* <p>
|
||||||
* The default check requires that any non-capability NBT is exactly the same as the
|
* The default check requires that any NBT is exactly the same as the crafting item,
|
||||||
* crafting item, but this may be relaxed for your upgrade.
|
* but this may be relaxed for your upgrade.
|
||||||
* <p>
|
|
||||||
* This is based on {@code net.neoforged.common.crafting.StrictNBTIngredient}'s check.
|
|
||||||
*
|
*
|
||||||
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
||||||
* {@link #getCraftingItem()}.
|
* {@link #getCraftingItem()}.
|
||||||
* @return If this stack may be used to equip this upgrade.
|
* @return If this stack may be used to equip this upgrade.
|
||||||
*/
|
*/
|
||||||
default boolean isItemSuitable(ItemStack stack) {
|
default boolean isItemSuitable(ItemStack stack) {
|
||||||
var crafting = getCraftingItem();
|
return ItemStack.isSameItemSameComponents(getCraftingItem(), stack);
|
||||||
|
|
||||||
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
|
||||||
// null one.
|
|
||||||
var tag = stack.getTag();
|
|
||||||
var craftingTag = crafting.getTag();
|
|
||||||
if (tag == craftingTag) return true;
|
|
||||||
if (tag == null) return Objects.requireNonNull(craftingTag).isEmpty();
|
|
||||||
if (craftingTag == null) return tag.isEmpty();
|
|
||||||
return tag.equals(craftingTag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,23 +6,17 @@ package dan200.computercraft.api.upgrades;
|
|||||||
|
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
|
* An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
|
||||||
* <p>
|
|
||||||
* <strong>IMPORTANT:</strong> The {@link #data()} in an upgrade data is often a reference to the original upgrade data.
|
|
||||||
* Be careful to take a {@linkplain #copy() defensive copy} if you plan to use the data in this upgrade.
|
|
||||||
*
|
*
|
||||||
* @param upgrade The current upgrade.
|
* @param upgrade The current upgrade.
|
||||||
* @param data The upgrade's data.
|
* @param data The upgrade's data.
|
||||||
* @param <T> The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
|
* @param <T> The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
|
||||||
*/
|
*/
|
||||||
public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
public record UpgradeData<T extends UpgradeBase>(T upgrade, DataComponentPatch data) {
|
||||||
/**
|
/**
|
||||||
* A utility method to construct a new {@link UpgradeData} instance.
|
* A utility method to construct a new {@link UpgradeData} instance.
|
||||||
*
|
*
|
||||||
@ -31,7 +25,7 @@ public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
|||||||
* @param <T> The type of upgrade.
|
* @param <T> The type of upgrade.
|
||||||
* @return The new {@link UpgradeData} instance.
|
* @return The new {@link UpgradeData} instance.
|
||||||
*/
|
*/
|
||||||
public static <T extends UpgradeBase> UpgradeData<T> of(T upgrade, CompoundTag data) {
|
public static <T extends UpgradeBase> UpgradeData<T> of(T upgrade, DataComponentPatch data) {
|
||||||
return new UpgradeData<>(upgrade, data);
|
return new UpgradeData<>(upgrade, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,19 +41,7 @@ public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take a copy of a (possibly {@code null}) {@link UpgradeData} instance.
|
* Get the {@linkplain UpgradeBase#getUpgradeItem(DataComponentPatch) upgrade item} for this upgrade.
|
||||||
*
|
|
||||||
* @param upgrade The copied upgrade data.
|
|
||||||
* @param <T> The type of upgrade.
|
|
||||||
* @return The newly created upgrade data.
|
|
||||||
*/
|
|
||||||
@Contract("!null -> !null; null -> null")
|
|
||||||
public static <T extends UpgradeBase> @Nullable UpgradeData<T> copyOf(@Nullable UpgradeData<T> upgrade) {
|
|
||||||
return upgrade == null ? null : upgrade.copy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the {@linkplain UpgradeBase#getUpgradeItem(CompoundTag) upgrade item} for this upgrade.
|
|
||||||
* <p>
|
* <p>
|
||||||
* This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
|
* This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
|
||||||
* {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
|
* {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
|
||||||
@ -69,14 +51,4 @@ public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
|||||||
public ItemStack getUpgradeItem() {
|
public ItemStack getUpgradeItem() {
|
||||||
return upgrade.getUpgradeItem(data).copy();
|
return upgrade.getUpgradeItem(data).copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Take a copy of this {@link UpgradeData}. This returns a new instance with the same upgrade and a fresh copy of
|
|
||||||
* the upgrade data.
|
|
||||||
*
|
|
||||||
* @return A copy of the current instance.
|
|
||||||
*/
|
|
||||||
public UpgradeData<T> copy() {
|
|
||||||
return new UpgradeData<>(upgrade(), data().copy());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
|||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
@ -58,7 +58,7 @@ public interface UpgradeSerialiser<T extends UpgradeBase> {
|
|||||||
* @param buffer The buffer object to read this upgrade from.
|
* @param buffer The buffer object to read this upgrade from.
|
||||||
* @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}.
|
* @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}.
|
||||||
*/
|
*/
|
||||||
T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer);
|
T fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write this upgrade to a network packet, to be sent to the client.
|
* Write this upgrade to a network packet, to be sent to the client.
|
||||||
@ -66,7 +66,7 @@ public interface UpgradeSerialiser<T extends UpgradeBase> {
|
|||||||
* @param buffer The buffer object to write this upgrade to
|
* @param buffer The buffer object to write this upgrade to
|
||||||
* @param upgrade The upgrade to write.
|
* @param upgrade The upgrade to write.
|
||||||
*/
|
*/
|
||||||
void toNetwork(FriendlyByteBuf buffer, T upgrade);
|
void toNetwork(RegistryFriendlyByteBuf buffer, T upgrade);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||||
|
@ -7,7 +7,7 @@ package dan200.computercraft.impl.upgrades;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@ -37,13 +37,13 @@ public final class SerialiserWithCraftingItem<T extends UpgradeBase> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
public T fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer) {
|
||||||
var item = buffer.readItem();
|
var item = ItemStack.STREAM_CODEC.decode(buffer);
|
||||||
return factory.apply(id, item);
|
return factory.apply(id, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
public void toNetwork(RegistryFriendlyByteBuf buffer, T upgrade) {
|
||||||
buffer.writeItem(upgrade.getCraftingItem());
|
ItemStack.STREAM_CODEC.encode(buffer, upgrade.getCraftingItem());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ package dan200.computercraft.impl.upgrades;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
@ -34,11 +34,11 @@ public final class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
public T fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer) {
|
||||||
return constructor.apply(id);
|
return constructor.apply(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
public void toNetwork(RegistryFriendlyByteBuf buffer, T upgrade) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://maven.minecraftforge.net/") {
|
maven("https://maven.neoforged.net/") {
|
||||||
content {
|
content {
|
||||||
includeModule("org.spongepowered", "mixin")
|
includeModule("org.spongepowered", "mixin")
|
||||||
}
|
}
|
||||||
@ -37,6 +37,7 @@ dependencies {
|
|||||||
clientImplementation(clientClasses(project(":common-api")))
|
clientImplementation(clientClasses(project(":common-api")))
|
||||||
|
|
||||||
compileOnly(libs.mixin)
|
compileOnly(libs.mixin)
|
||||||
|
compileOnly(libs.mixinExtra)
|
||||||
compileOnly(libs.bundles.externalMods.common)
|
compileOnly(libs.bundles.externalMods.common)
|
||||||
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||||
|
|
||||||
|
@ -21,13 +21,11 @@ import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
|||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.color.item.ItemColor;
|
import net.minecraft.client.color.item.ItemColor;
|
||||||
@ -44,11 +42,13 @@ import net.minecraft.network.chat.Component;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
|
import net.minecraft.util.FastColor;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.level.ItemLike;
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -94,7 +94,7 @@ public final class ClientRegistry {
|
|||||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
);
|
);
|
||||||
registerItemProperty(itemProperties, "coloured",
|
registerItemProperty(itemProperties, "coloured",
|
||||||
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
(stack, world, player, random) -> DyedItemColor.getOrDefault(stack, -1) != -1 ? 1 : 0,
|
||||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -162,12 +162,12 @@ public final class ClientRegistry {
|
|||||||
|
|
||||||
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
||||||
register.accept(
|
register.accept(
|
||||||
(stack, layer) -> layer == 1 ? ((DiskItem) stack.getItem()).getColour(stack) : 0xFFFFFF,
|
(stack, layer) -> layer == 1 ? DiskItem.getColour(stack) : -1,
|
||||||
ModRegistry.Items.DISK.get()
|
ModRegistry.Items.DISK.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
register.accept(
|
register.accept(
|
||||||
(stack, layer) -> layer == 1 ? TreasureDiskItem.getColour(stack) : 0xFFFFFF,
|
(stack, layer) -> layer == 1 ? DyedItemColor.getOrDefault(stack, Colour.BLUE.getARGB()) : -1,
|
||||||
ModRegistry.Items.TREASURE_DISK.get()
|
ModRegistry.Items.TREASURE_DISK.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -180,17 +180,17 @@ public final class ClientRegistry {
|
|||||||
|
|
||||||
private static int getPocketColour(ItemStack stack, int layer) {
|
private static int getPocketColour(ItemStack stack, int layer) {
|
||||||
return switch (layer) {
|
return switch (layer) {
|
||||||
default -> 0xFFFFFF;
|
default -> -1;
|
||||||
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
|
case 1 -> DyedItemColor.getOrDefault(stack, -1); // Frame colour
|
||||||
case 2 -> { // Light colour
|
case 2 -> { // Light colour
|
||||||
var computer = ClientPocketComputers.get(stack);
|
var computer = ClientPocketComputers.get(stack);
|
||||||
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getARGB() : FastColor.ARGB32.opaque(computer.getLightState());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTurtleColour(ItemStack stack, int layer) {
|
private static int getTurtleColour(ItemStack stack, int layer) {
|
||||||
return layer == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF;
|
return layer == 0 ? DyedItemColor.getOrDefault(stack, -1) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
||||||
|
@ -75,7 +75,7 @@ public class ClientTableFormatter implements TableFormatter {
|
|||||||
|
|
||||||
var tag = createTag(table.getId());
|
var tag = createTag(table.getId());
|
||||||
if (chat.allMessages.removeIf(guiMessage -> guiMessage.tag() != null && Objects.equals(guiMessage.tag().logTag(), tag.logTag()))) {
|
if (chat.allMessages.removeIf(guiMessage -> guiMessage.tag() != null && Objects.equals(guiMessage.tag().logTag(), tag.logTag()))) {
|
||||||
chat.refreshTrimmedMessage();
|
chat.rescaleChat();
|
||||||
}
|
}
|
||||||
|
|
||||||
TableFormatter.super.display(table);
|
TableFormatter.super.display(table);
|
||||||
|
@ -6,7 +6,9 @@ package dan200.computercraft.client.gui;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
import dan200.computercraft.core.terminal.TextBuffer;
|
import dan200.computercraft.core.terminal.TextBuffer;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
@ -35,16 +37,17 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
|||||||
|
|
||||||
imageHeight = Y_SIZE;
|
imageHeight = Y_SIZE;
|
||||||
|
|
||||||
var text = PrintoutItem.getText(container.getStack());
|
var printout = container.getStack().getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
||||||
this.text = new TextBuffer[text.length];
|
this.text = new TextBuffer[printout.lines().size()];
|
||||||
for (var i = 0; i < this.text.length; i++) this.text[i] = new TextBuffer(text[i]);
|
this.colours = new TextBuffer[printout.lines().size()];
|
||||||
|
for (var i = 0; i < this.text.length; i++) {
|
||||||
var colours = PrintoutItem.getColours(container.getStack());
|
var line = printout.lines().get(i);
|
||||||
this.colours = new TextBuffer[colours.length];
|
this.text[i] = new TextBuffer(line.text());
|
||||||
for (var i = 0; i < this.colours.length; i++) this.colours[i] = new TextBuffer(colours[i]);
|
this.colours[i] = new TextBuffer(line.foreground());
|
||||||
|
}
|
||||||
|
|
||||||
page = 0;
|
page = 0;
|
||||||
pages = Math.max(this.text.length / PrintoutItem.LINES_PER_PAGE, 1);
|
pages = Math.max(this.text.length / PrintoutData.LINES_PER_PAGE, 1);
|
||||||
book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
|
book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +92,7 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
|||||||
|
|
||||||
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||||
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
||||||
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutData.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
||||||
renderer.endBatch();
|
renderer.endBatch();
|
||||||
|
|
||||||
graphics.pose().popPose();
|
graphics.pose().popPose();
|
||||||
|
@ -27,13 +27,11 @@ public class EMIComputerCraft implements EmiPlugin {
|
|||||||
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
|
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Comparison turtleComparison = compareStacks((left, right) ->
|
private static final Comparison turtleComparison = compareStacks((left, right)
|
||||||
left.getItem() instanceof TurtleItem turtle
|
-> TurtleItem.getUpgrade(left, TurtleSide.LEFT) == TurtleItem.getUpgrade(right, TurtleSide.LEFT)
|
||||||
&& turtle.getUpgrade(left, TurtleSide.LEFT) == turtle.getUpgrade(right, TurtleSide.LEFT)
|
&& TurtleItem.getUpgrade(left, TurtleSide.RIGHT) == TurtleItem.getUpgrade(right, TurtleSide.RIGHT));
|
||||||
&& turtle.getUpgrade(left, TurtleSide.RIGHT) == turtle.getUpgrade(right, TurtleSide.RIGHT));
|
|
||||||
|
|
||||||
private static final Comparison pocketComparison = compareStacks((left, right) ->
|
private static final Comparison pocketComparison = compareStacks((left, right) -> PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
|
||||||
left.getItem() instanceof PocketComputerItem && PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
|
|
||||||
|
|
||||||
private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
|
private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
|
||||||
return Comparison.of((left, right) -> {
|
return Comparison.of((left, right) -> {
|
||||||
|
@ -15,9 +15,11 @@ import dan200.computercraft.client.platform.ClientPlatformHelper;
|
|||||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import dan200.computercraft.shared.util.Holiday;
|
import dan200.computercraft.shared.util.Holiday;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@ -54,13 +56,6 @@ public final class TurtleModelParts<T> {
|
|||||||
boolean christmas,
|
boolean christmas,
|
||||||
boolean flip
|
boolean flip
|
||||||
) {
|
) {
|
||||||
Combination copy() {
|
|
||||||
if (leftUpgrade == null && rightUpgrade == null) return this;
|
|
||||||
return new Combination(
|
|
||||||
colour, UpgradeData.copyOf(leftUpgrade), UpgradeData.copyOf(rightUpgrade),
|
|
||||||
overlay, christmas, flip
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BakedModel familyModel;
|
private final BakedModel familyModel;
|
||||||
@ -96,31 +91,18 @@ public final class TurtleModelParts<T> {
|
|||||||
|
|
||||||
public T getModel(ItemStack stack) {
|
public T getModel(ItemStack stack) {
|
||||||
var combination = getCombination(stack);
|
var combination = getCombination(stack);
|
||||||
var existing = modelCache.get(combination);
|
return modelCache.computeIfAbsent(combination, buildModel);
|
||||||
if (existing != null) return existing;
|
|
||||||
|
|
||||||
// Take a defensive copy of the upgrade data, and add it to the cache.
|
|
||||||
var newCombination = combination.copy();
|
|
||||||
var newModel = buildModel.apply(newCombination);
|
|
||||||
modelCache.put(newCombination, newModel);
|
|
||||||
return newModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Combination getCombination(ItemStack stack) {
|
private Combination getCombination(ItemStack stack) {
|
||||||
var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
|
var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
|
||||||
|
var leftUpgrade = TurtleItem.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||||
if (!(stack.getItem() instanceof TurtleItem turtle)) {
|
var rightUpgrade = TurtleItem.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||||
return new Combination(false, null, null, null, christmas, false);
|
var overlay = TurtleItem.getOverlay(stack);
|
||||||
}
|
var label = DataComponentUtil.getCustomName(stack);
|
||||||
|
|
||||||
var colour = turtle.getColour(stack);
|
|
||||||
var leftUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.LEFT);
|
|
||||||
var rightUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
|
||||||
var overlay = turtle.getOverlay(stack);
|
|
||||||
var label = turtle.getLabel(stack);
|
|
||||||
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
||||||
|
|
||||||
return new Combination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
return new Combination(stack.has(DataComponents.DYED_COLOR), leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BakedModel> buildModel(Combination combo) {
|
private List<BakedModel> buildModel(Combination combo) {
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.network;
|
package dan200.computercraft.client.network;
|
||||||
|
|
||||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Methods for sending packets from clients to the server.
|
* Methods for sending packets from clients to the server.
|
||||||
@ -23,6 +23,6 @@ public final class ClientNetworking {
|
|||||||
*/
|
*/
|
||||||
public static void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
public static void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
||||||
var connection = Minecraft.getInstance().getConnection();
|
var connection = Minecraft.getInstance().getConnection();
|
||||||
if (connection != null) connection.send(ClientPlatformHelper.get().createPacket(message));
|
if (connection != null) connection.send(new ServerboundCustomPayloadPacket(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,9 @@
|
|||||||
package dan200.computercraft.client.platform;
|
package dan200.computercraft.client.platform;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.protocol.Packet;
|
|
||||||
import net.minecraft.network.protocol.common.ServerCommonPacketListener;
|
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -21,14 +17,6 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
|||||||
return (ClientPlatformHelper) dan200.computercraft.impl.client.ClientPlatformHelper.get();
|
return (ClientPlatformHelper) dan200.computercraft.impl.client.ClientPlatformHelper.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a serverbound {@link NetworkMessage} to a Minecraft {@link Packet}.
|
|
||||||
*
|
|
||||||
* @param message The messsge to convert.
|
|
||||||
* @return The converted message.
|
|
||||||
*/
|
|
||||||
Packet<ServerCommonPacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a {@link BakedModel}, using any loader-specific hooks.
|
* Render a {@link BakedModel}, using any loader-specific hooks.
|
||||||
*
|
*
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.pocket;
|
package dan200.computercraft.client.pocket;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -53,7 +53,7 @@ public final class ClientPocketComputers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable PocketComputerData get(ItemStack stack) {
|
public static @Nullable PocketComputerData get(ItemStack stack) {
|
||||||
var id = PocketComputerItem.getInstanceID(stack);
|
var id = stack.get(ModRegistry.DataComponents.COMPUTER.get());
|
||||||
return id == null ? null : instances.get(id);
|
return id == null ? null : instances.get(id.instance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,6 @@ public final class CableHighlightRenderer {
|
|||||||
|
|
||||||
var buffer = bufferSource.getBuffer(RenderType.lines());
|
var buffer = bufferSource.getBuffer(RenderType.lines());
|
||||||
var matrix4f = transform.last().pose();
|
var matrix4f = transform.last().pose();
|
||||||
var normal = transform.last().normal();
|
|
||||||
// TODO: Can we just accesstransformer out LevelRenderer.renderShape?
|
// TODO: Can we just accesstransformer out LevelRenderer.renderShape?
|
||||||
shape.forAllEdges((x1, y1, z1, x2, y2, z2) -> {
|
shape.forAllEdges((x1, y1, z1, x2, y2, z2) -> {
|
||||||
var xDelta = (float) (x2 - x1);
|
var xDelta = (float) (x2 - x1);
|
||||||
@ -65,12 +64,12 @@ public final class CableHighlightRenderer {
|
|||||||
buffer
|
buffer
|
||||||
.vertex(matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset))
|
.vertex(matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset))
|
||||||
.color(0, 0, 0, 0.4f)
|
.color(0, 0, 0, 0.4f)
|
||||||
.normal(normal, xDelta, yDelta, zDelta)
|
.normal(transform.last(), xDelta, yDelta, zDelta)
|
||||||
.endVertex();
|
.endVertex();
|
||||||
buffer
|
buffer
|
||||||
.vertex(matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset))
|
.vertex(matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset))
|
||||||
.color(0, 0, 0, 0.4f)
|
.color(0, 0, 0, 0.4f)
|
||||||
.normal(normal, xDelta, yDelta, zDelta)
|
.normal(transform.last(), xDelta, yDelta, zDelta)
|
||||||
.endVertex();
|
.endVertex();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import dan200.computercraft.shared.config.Config;
|
|||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
|
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
|
||||||
@ -61,7 +62,7 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
// Render the main frame
|
// Render the main frame
|
||||||
var item = (PocketComputerItem) stack.getItem();
|
var item = (PocketComputerItem) stack.getItem();
|
||||||
var family = item.getFamily();
|
var family = item.getFamily();
|
||||||
var frameColour = item.getColour(stack);
|
var frameColour = DyedItemColor.getOrDefault(stack, -1);
|
||||||
|
|
||||||
var matrix = transform.last().pose();
|
var matrix = transform.last().pose();
|
||||||
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
||||||
|
@ -6,6 +6,8 @@ package dan200.computercraft.client.render;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
@ -15,8 +17,8 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
||||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINES_PER_PAGE;
|
import static dan200.computercraft.shared.media.items.PrintoutData.LINES_PER_PAGE;
|
||||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINE_MAX_LENGTH;
|
import static dan200.computercraft.shared.media.items.PrintoutData.LINE_LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates map and item-frame rendering for printouts.
|
* Emulates map and item-frame rendering for printouts.
|
||||||
@ -37,8 +39,6 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int packedLight) {
|
public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int packedLight) {
|
||||||
if (!(stack.getItem() instanceof PrintoutItem)) return;
|
|
||||||
|
|
||||||
// Move a little bit forward to ensure we're not clipping with the frame
|
// Move a little bit forward to ensure we're not clipping with the frame
|
||||||
transform.translate(0.0f, 0.0f, -0.001f);
|
transform.translate(0.0f, 0.0f, -0.001f);
|
||||||
transform.mulPose(Axis.ZP.rotationDegrees(180f));
|
transform.mulPose(Axis.ZP.rotationDegrees(180f));
|
||||||
@ -50,10 +50,12 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void drawPrintout(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
|
private static void drawPrintout(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
|
||||||
var pages = PrintoutItem.getPageCount(stack);
|
var pageData = stack.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
||||||
|
|
||||||
|
var pages = pageData.pages();
|
||||||
var book = ((PrintoutItem) stack.getItem()).getType() == PrintoutItem.Type.BOOK;
|
var book = ((PrintoutItem) stack.getItem()).getType() == PrintoutItem.Type.BOOK;
|
||||||
|
|
||||||
double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
double width = LINE_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
||||||
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
||||||
|
|
||||||
// Non-books will be left aligned
|
// Non-books will be left aligned
|
||||||
@ -75,9 +77,6 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
|
|||||||
transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
|
transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
|
||||||
|
|
||||||
drawBorder(transform, render, 0, 0, -0.01f, 0, pages, book, light);
|
drawBorder(transform, render, 0, 0, -0.01f, 0, pages, book, light);
|
||||||
drawText(
|
drawText(transform, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, pageData.lines());
|
||||||
transform, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light,
|
|
||||||
PrintoutItem.getText(stack), PrintoutItem.getColours(stack)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,14 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
|
|||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import dan200.computercraft.core.terminal.Palette;
|
import dan200.computercraft.core.terminal.Palette;
|
||||||
import dan200.computercraft.core.terminal.TextBuffer;
|
import dan200.computercraft.core.terminal.TextBuffer;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINES_PER_PAGE;
|
import static dan200.computercraft.shared.media.items.PrintoutData.LINES_PER_PAGE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders printed pages or books, either for a GUI ({@link dan200.computercraft.client.gui.PrintoutScreen}) or
|
* Renders printed pages or books, either for a GUI ({@link dan200.computercraft.client.gui.PrintoutScreen}) or
|
||||||
@ -69,13 +72,14 @@ public final class PrintoutRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void drawText(PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, String[] text, String[] colours) {
|
public static void drawText(PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, List<PrintoutData.Line> lines) {
|
||||||
var buffer = bufferSource.getBuffer(RenderTypes.PRINTOUT_TEXT);
|
var buffer = bufferSource.getBuffer(RenderTypes.PRINTOUT_TEXT);
|
||||||
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, buffer);
|
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, buffer);
|
||||||
for (var line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
|
for (var line = 0; line < LINES_PER_PAGE && line < lines.size(); line++) {
|
||||||
|
var lineContents = lines.get(start + line);
|
||||||
FixedWidthFontRenderer.drawString(emitter,
|
FixedWidthFontRenderer.drawString(emitter,
|
||||||
x, y + line * FONT_HEIGHT,
|
x, y + line * FONT_HEIGHT,
|
||||||
new TextBuffer(text[start + line]), new TextBuffer(colours[start + line]),
|
new TextBuffer(lineContents.text()), new TextBuffer(lineContents.foreground()),
|
||||||
Palette.DEFAULT, light
|
Palette.DEFAULT, light
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -191,17 +191,23 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our VBO doesn't transform its vertices with the provided pose stack, which means that the inverse view
|
// Our VBO renders coordinates in monitor-space rather than world space. A full sized monitor (8x6) will
|
||||||
// rotation matrix gives entirely wrong numbers for fog distances. We just set it to the identity which
|
// use positions from (0, 0) to (164*FONT_WIDTH, 81*FONT_HEIGHT) = (984, 729). This is far outside the
|
||||||
// gives a good enough approximation.
|
// normal render distance (~200), and the edges of the monitor fade out due to fog.
|
||||||
var oldInverseRotation = RenderSystem.getInverseViewRotationMatrix();
|
// There's not really a good way around this, at least without using a custom render type (which the VBO
|
||||||
RenderSystem.setInverseViewRotationMatrix(IDENTITY_NORMAL);
|
// renderer is trying to avoid!). Instead, we just disable fog entirely by setting the fog start to an
|
||||||
|
// absurdly high value.
|
||||||
|
var oldFogStart = RenderSystem.getShaderFogStart();
|
||||||
|
RenderSystem.setShaderFogStart(1e4f);
|
||||||
|
|
||||||
RenderTypes.TERMINAL.setupRenderState();
|
RenderTypes.TERMINAL.setupRenderState();
|
||||||
|
|
||||||
|
// Compose the existing model view matrix with our transformation matrix.
|
||||||
|
var modelView = new Matrix4f(RenderSystem.getModelViewMatrix()).mul(matrix);
|
||||||
|
|
||||||
// Render background geometry
|
// Render background geometry
|
||||||
backgroundBuffer.bind();
|
backgroundBuffer.bind();
|
||||||
backgroundBuffer.drawWithShader(matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader());
|
backgroundBuffer.drawWithShader(modelView, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader());
|
||||||
|
|
||||||
// Render foreground geometry with glPolygonOffset enabled.
|
// Render foreground geometry with glPolygonOffset enabled.
|
||||||
RenderSystem.polygonOffset(-1.0f, -10.0f);
|
RenderSystem.polygonOffset(-1.0f, -10.0f);
|
||||||
@ -209,7 +215,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
|||||||
|
|
||||||
foregroundBuffer.bind();
|
foregroundBuffer.bind();
|
||||||
foregroundBuffer.drawWithShader(
|
foregroundBuffer.drawWithShader(
|
||||||
matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(),
|
modelView, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(),
|
||||||
// As mentioned in the above comment, render the extra cursor quad if it is visible this frame. Each
|
// As mentioned in the above comment, render the extra cursor quad if it is visible this frame. Each
|
||||||
// // quad has an index count of 6.
|
// // quad has an index count of 6.
|
||||||
FixedWidthFontRenderer.isCursorVisible(terminal) && FrameInfo.getGlobalCursorBlink()
|
FixedWidthFontRenderer.isCursorVisible(terminal) && FrameInfo.getGlobalCursorBlink()
|
||||||
@ -222,7 +228,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
|||||||
RenderTypes.TERMINAL.clearRenderState();
|
RenderTypes.TERMINAL.clearRenderState();
|
||||||
VertexBuffer.unbind();
|
VertexBuffer.unbind();
|
||||||
|
|
||||||
RenderSystem.setInverseViewRotationMatrix(oldInverseRotation);
|
RenderSystem.setShaderFogStart(oldFogStart);
|
||||||
}
|
}
|
||||||
case BEST -> throw new IllegalStateException("Impossible: Should never use BEST renderer");
|
case BEST -> throw new IllegalStateException("Impossible: Should never use BEST renderer");
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
|||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import org.joml.Matrix3f;
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@ -53,7 +52,7 @@ public final class MonitorHighlightRenderer {
|
|||||||
// I wish I could think of a better way to do this
|
// I wish I could think of a better way to do this
|
||||||
var buffer = bufferSource.getBuffer(RenderType.lines());
|
var buffer = bufferSource.getBuffer(RenderType.lines());
|
||||||
var transform = transformStack.last().pose();
|
var transform = transformStack.last().pose();
|
||||||
var normal = transformStack.last().normal();
|
var normal = transformStack.last();
|
||||||
if (faces.contains(NORTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 0, UP);
|
if (faces.contains(NORTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 0, UP);
|
||||||
if (faces.contains(SOUTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 1, UP);
|
if (faces.contains(SOUTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 1, UP);
|
||||||
if (faces.contains(NORTH) || faces.contains(EAST)) line(buffer, transform, normal, 1, 0, 0, UP);
|
if (faces.contains(NORTH) || faces.contains(EAST)) line(buffer, transform, normal, 1, 0, 0, UP);
|
||||||
@ -71,7 +70,7 @@ public final class MonitorHighlightRenderer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void line(VertexConsumer buffer, Matrix4f transform, Matrix3f normal, float x, float y, float z, Direction direction) {
|
private static void line(VertexConsumer buffer, Matrix4f transform, PoseStack.Pose normal, float x, float y, float z, Direction direction) {
|
||||||
buffer
|
buffer
|
||||||
.vertex(transform, x, y, z)
|
.vertex(transform, x, y, z)
|
||||||
.color(0, 0, 0, 0.4f)
|
.color(0, 0, 0, 0.4f)
|
||||||
|
@ -9,8 +9,9 @@ import dan200.computercraft.api.client.TransformedModel;
|
|||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -41,12 +42,9 @@ public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
|
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
|
||||||
var active = false;
|
var component = data.get(ModRegistry.DataComponents.ON.get());
|
||||||
if (turtle != null) {
|
var active = component != null && component.isPresent() && component.get();
|
||||||
var turtleNBT = turtle.getUpgradeNBTData(side);
|
|
||||||
active = turtleNBT.contains("active") && turtleNBT.getBoolean("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
return side == TurtleSide.LEFT
|
return side == TurtleSide.LEFT
|
||||||
? TransformedModel.of(active ? leftOnModel : leftOffModel)
|
? TransformedModel.of(active ? leftOnModel : leftOffModel)
|
||||||
|
@ -15,7 +15,7 @@ import dan200.computercraft.impl.RegistryHelper;
|
|||||||
import dan200.computercraft.impl.TurtleUpgrades;
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
import dan200.computercraft.impl.UpgradeManager;
|
import dan200.computercraft.impl.UpgradeManager;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -60,10 +60,10 @@ public final class TurtleUpgradeModellers {
|
|||||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||||
return modeller.getModel(upgrade, access, side, access.getUpgradeNBTData(side));
|
return modeller.getModel(upgrade, access, side, access.getUpgradeData(side));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||||
return modeller.getModel(upgrade, null, side, data);
|
return modeller.getModel(upgrade, null, side, data);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.data.DataProvider;
|
import net.minecraft.data.DataProvider;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||||
@ -17,7 +18,9 @@ import net.minecraft.world.item.Item;
|
|||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,7 +34,7 @@ public final class DataProviders {
|
|||||||
public static void add(GeneratorSink generator) {
|
public static void add(GeneratorSink generator) {
|
||||||
var turtleUpgrades = generator.add(TurtleUpgradeProvider::new);
|
var turtleUpgrades = generator.add(TurtleUpgradeProvider::new);
|
||||||
var pocketUpgrades = generator.add(PocketUpgradeProvider::new);
|
var pocketUpgrades = generator.add(PocketUpgradeProvider::new);
|
||||||
generator.add(out -> new RecipeProvider(out, turtleUpgrades, pocketUpgrades));
|
generator.add((out, registries) -> new RecipeProvider(out, registries, turtleUpgrades, pocketUpgrades));
|
||||||
|
|
||||||
var blockTags = generator.blockTags(TagProvider::blockTags);
|
var blockTags = generator.blockTags(TagProvider::blockTags);
|
||||||
generator.itemTags(TagProvider::itemTags, blockTags);
|
generator.itemTags(TagProvider::itemTags, blockTags);
|
||||||
@ -55,6 +58,8 @@ public final class DataProviders {
|
|||||||
public interface GeneratorSink {
|
public interface GeneratorSink {
|
||||||
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
|
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
|
||||||
|
|
||||||
|
<T extends DataProvider> T add(BiFunction<PackOutput, CompletableFuture<HolderLookup.Provider>, T> factory);
|
||||||
|
|
||||||
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
|
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
|
||||||
|
|
||||||
void lootTable(List<SubProviderEntry> tables);
|
void lootTable(List<SubProviderEntry> tables);
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.shared.CommonHooks;
|
import dan200.computercraft.shared.CommonHooks;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||||
@ -13,14 +12,15 @@ import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
|||||||
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
||||||
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
||||||
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.storage.loot.LootPool;
|
import net.minecraft.world.level.storage.loot.LootPool;
|
||||||
import net.minecraft.world.level.storage.loot.LootTable;
|
import net.minecraft.world.level.storage.loot.LootTable;
|
||||||
import net.minecraft.world.level.storage.loot.entries.DynamicLoot;
|
|
||||||
import net.minecraft.world.level.storage.loot.entries.LootItem;
|
import net.minecraft.world.level.storage.loot.entries.LootItem;
|
||||||
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||||
|
import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction;
|
||||||
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
|
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
|
||||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
|
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
|
||||||
@ -41,7 +41,7 @@ class LootTableProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerBlocks(BiConsumer<ResourceLocation, LootTable.Builder> add) {
|
private static void registerBlocks(HolderLookup.Provider registries, BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add) {
|
||||||
namedBlockDrop(add, ModRegistry.Blocks.DISK_DRIVE);
|
namedBlockDrop(add, ModRegistry.Blocks.DISK_DRIVE);
|
||||||
selfDrop(add, ModRegistry.Blocks.MONITOR_NORMAL);
|
selfDrop(add, ModRegistry.Blocks.MONITOR_NORMAL);
|
||||||
selfDrop(add, ModRegistry.Blocks.MONITOR_ADVANCED);
|
selfDrop(add, ModRegistry.Blocks.MONITOR_ADVANCED);
|
||||||
@ -78,15 +78,15 @@ class LootTableProvider {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerGeneric(BiConsumer<ResourceLocation, LootTable.Builder> add) {
|
private static void registerGeneric(HolderLookup.Provider registries, BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add) {
|
||||||
add.accept(CommonHooks.TREASURE_DISK_LOOT, LootTable.lootTable());
|
add.accept(CommonHooks.TREASURE_DISK_LOOT, LootTable.lootTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void selfDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
private static void selfDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
||||||
blockDrop(add, wrapper, LootItem.lootTableItem(wrapper.get()), ExplosionCondition.survivesExplosion());
|
blockDrop(add, wrapper, LootItem.lootTableItem(wrapper.get()), ExplosionCondition.survivesExplosion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void namedBlockDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
private static void namedBlockDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
||||||
blockDrop(
|
blockDrop(
|
||||||
add, wrapper,
|
add, wrapper,
|
||||||
LootItem.lootTableItem(wrapper.get()).apply(CopyNameFunction.copyName(CopyNameFunction.NameSource.BLOCK_ENTITY)),
|
LootItem.lootTableItem(wrapper.get()).apply(CopyNameFunction.copyName(CopyNameFunction.NameSource.BLOCK_ENTITY)),
|
||||||
@ -94,10 +94,10 @@ class LootTableProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void computerDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> block) {
|
private static void computerDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> block) {
|
||||||
blockDrop(
|
blockDrop(
|
||||||
add, block,
|
add, block,
|
||||||
DynamicLoot.dynamicEntry(new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer")),
|
LootItem.lootTableItem(block.get()).apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY)),
|
||||||
AnyOfCondition.anyOf(
|
AnyOfCondition.anyOf(
|
||||||
BlockNamedEntityLootCondition.BUILDER,
|
BlockNamedEntityLootCondition.BUILDER,
|
||||||
HasComputerIdLootCondition.BUILDER,
|
HasComputerIdLootCondition.BUILDER,
|
||||||
@ -107,7 +107,7 @@ class LootTableProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void blockDrop(
|
private static void blockDrop(
|
||||||
BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper,
|
BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> wrapper,
|
||||||
LootPoolEntryContainer.Builder<?> drop,
|
LootPoolEntryContainer.Builder<?> drop,
|
||||||
LootItemCondition.Builder condition
|
LootItemCondition.Builder condition
|
||||||
) {
|
) {
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import com.mojang.serialization.JsonOps;
|
import com.mojang.serialization.JsonOps;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
@ -19,26 +18,25 @@ import dan200.computercraft.impl.RegistryHelper;
|
|||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.common.ClearColourRecipe;
|
import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||||
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
import dan200.computercraft.shared.computer.recipe.ComputerConvertRecipe;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
|
||||||
import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
||||||
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.platform.RecipeIngredients;
|
import dan200.computercraft.shared.platform.RecipeIngredients;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
|
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
|
||||||
import dan200.computercraft.shared.recipe.CustomShapelessRecipe;
|
|
||||||
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
||||||
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||||
import dan200.computercraft.shared.util.ColourUtils;
|
import dan200.computercraft.shared.util.ColourUtils;
|
||||||
import net.minecraft.Util;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import net.minecraft.advancements.Criterion;
|
import net.minecraft.advancements.Criterion;
|
||||||
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
||||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
@ -46,12 +44,13 @@ import net.minecraft.data.recipes.RecipeCategory;
|
|||||||
import net.minecraft.data.recipes.RecipeOutput;
|
import net.minecraft.data.recipes.RecipeOutput;
|
||||||
import net.minecraft.data.recipes.ShapedRecipeBuilder;
|
import net.minecraft.data.recipes.ShapedRecipeBuilder;
|
||||||
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
|
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtUtils;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.ItemTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.world.item.*;
|
import net.minecraft.world.item.*;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
|
import net.minecraft.world.item.component.ResolvableProfile;
|
||||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||||
import net.minecraft.world.item.crafting.Ingredient;
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
import net.minecraft.world.item.crafting.Recipe;
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
@ -60,6 +59,7 @@ import net.minecraft.world.level.block.Blocks;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
||||||
@ -70,8 +70,8 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
private final TurtleUpgradeDataProvider turtleUpgrades;
|
private final TurtleUpgradeDataProvider turtleUpgrades;
|
||||||
private final PocketUpgradeDataProvider pocketUpgrades;
|
private final PocketUpgradeDataProvider pocketUpgrades;
|
||||||
|
|
||||||
RecipeProvider(PackOutput output, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
|
RecipeProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
|
||||||
super(output);
|
super(output, registries);
|
||||||
this.turtleUpgrades = turtleUpgrades;
|
this.turtleUpgrades = turtleUpgrades;
|
||||||
this.pocketUpgrades = pocketUpgrades;
|
this.pocketUpgrades = pocketUpgrades;
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
private void diskColours(RecipeOutput output) {
|
private void diskColours(RecipeOutput output) {
|
||||||
for (var colour : Colour.VALUES) {
|
for (var colour : Colour.VALUES) {
|
||||||
ShapelessSpecBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.REDSTONE, DiskItem.createFromIDAndColour(-1, null, colour.getHex()))
|
.shapeless(RecipeCategory.REDSTONE, DataComponentUtil.createStack(ModRegistry.Items.DISK.get(), DataComponents.DYED_COLOR, new DyedItemColor(colour.getHex(), false)))
|
||||||
.requires(ingredients.redstone())
|
.requires(ingredients.redstone())
|
||||||
.requires(Items.PAPER)
|
.requires(Items.PAPER)
|
||||||
.requires(DyeItem.byColor(ofColour(colour)))
|
.requires(DyeItem.byColor(ofColour(colour)))
|
||||||
@ -122,17 +122,16 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
*/
|
*/
|
||||||
private void turtleUpgrades(RecipeOutput add) {
|
private void turtleUpgrades(RecipeOutput add) {
|
||||||
for (var turtleItem : turtleItems()) {
|
for (var turtleItem : turtleItems()) {
|
||||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
|
||||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||||
|
|
||||||
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
||||||
ShapedSpecBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null))
|
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
|
||||||
.group(name.toString())
|
.group(name.toString())
|
||||||
.pattern("#T")
|
.pattern("#T")
|
||||||
.define('T', base.getItem())
|
.define('T', turtleItem)
|
||||||
.define('#', upgrade.getCraftingItem().getItem())
|
.define('#', upgrade.getCraftingItem().getItem())
|
||||||
.unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
.unlockedBy("has_items", inventoryChange(turtleItem, upgrade.getCraftingItem().getItem()))
|
||||||
.build(ImpostorShapedRecipe::new)
|
.build(ImpostorShapedRecipe::new)
|
||||||
.save(
|
.save(
|
||||||
add,
|
add,
|
||||||
@ -153,18 +152,17 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
*/
|
*/
|
||||||
private void pocketUpgrades(RecipeOutput add) {
|
private void pocketUpgrades(RecipeOutput add) {
|
||||||
for (var pocket : pocketComputerItems()) {
|
for (var pocket : pocketComputerItems()) {
|
||||||
var base = pocket.create(-1, null, -1, null);
|
|
||||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, 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()) {
|
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
|
||||||
ShapedSpecBuilder
|
ShapedSpecBuilder
|
||||||
.shaped(RecipeCategory.REDSTONE, pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade)))
|
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
|
||||||
.group(name.toString())
|
.group(name.toString())
|
||||||
.pattern("#")
|
.pattern("#")
|
||||||
.pattern("P")
|
.pattern("P")
|
||||||
.define('P', base.getItem())
|
.define('P', pocket)
|
||||||
.define('#', upgrade.getCraftingItem().getItem())
|
.define('#', upgrade.getCraftingItem().getItem())
|
||||||
.unlockedBy("has_items", inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
|
.unlockedBy("has_items", inventoryChange(pocket, upgrade.getCraftingItem().getItem()))
|
||||||
.build(ImpostorShapedRecipe::new)
|
.build(ImpostorShapedRecipe::new)
|
||||||
.save(
|
.save(
|
||||||
add,
|
add,
|
||||||
@ -197,15 +195,14 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
|
|
||||||
private void turtleOverlay(RecipeOutput add, String overlay, Consumer<ShapelessSpecBuilder> build) {
|
private void turtleOverlay(RecipeOutput add, String overlay, Consumer<ShapelessSpecBuilder> build) {
|
||||||
for (var turtleItem : turtleItems()) {
|
for (var turtleItem : turtleItems()) {
|
||||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
|
||||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||||
|
|
||||||
var builder = ShapelessSpecBuilder.shapeless(RecipeCategory.REDSTONE, base)
|
var builder = ShapelessSpecBuilder.shapeless(RecipeCategory.REDSTONE, new ItemStack(turtleItem))
|
||||||
.group(name.withSuffix("_overlay").toString())
|
.group(name.withSuffix("_overlay").toString())
|
||||||
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
|
.unlockedBy("has_turtle", inventoryChange(turtleItem));
|
||||||
build.accept(builder);
|
build.accept(builder);
|
||||||
builder
|
builder
|
||||||
.requires(base.getItem())
|
.requires(turtleItem)
|
||||||
.build(s -> new TurtleOverlayRecipe(s, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay)))
|
.build(s -> new TurtleOverlayRecipe(s, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay)))
|
||||||
.save(add, name.withSuffix("_overlays/" + overlay));
|
.save(add, name.withSuffix("_overlays/" + overlay));
|
||||||
}
|
}
|
||||||
@ -254,7 +251,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('#', ingredients.goldIngot())
|
.define('#', ingredients.goldIngot())
|
||||||
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||||
.build(ComputerUpgradeRecipe::new)
|
.build(ComputerConvertRecipe::new)
|
||||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
@ -277,7 +274,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||||
.define('I', ingredients.woodenChest())
|
.define('I', ingredients.woodenChest())
|
||||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||||
.buildOrThrow(TurtleRecipe::of)
|
.build(ComputerConvertRecipe::new)
|
||||||
.save(add);
|
.save(add);
|
||||||
|
|
||||||
ShapedSpecBuilder
|
ShapedSpecBuilder
|
||||||
@ -289,7 +286,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
|
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||||
.define('I', ingredients.woodenChest())
|
.define('I', ingredients.woodenChest())
|
||||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||||
.buildOrThrow(TurtleRecipe::of)
|
.build(ComputerConvertRecipe::new)
|
||||||
.save(add);
|
.save(add);
|
||||||
|
|
||||||
ShapedSpecBuilder
|
ShapedSpecBuilder
|
||||||
@ -301,7 +298,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('C', ModRegistry.Items.TURTLE_NORMAL.get())
|
.define('C', ModRegistry.Items.TURTLE_NORMAL.get())
|
||||||
.define('B', ingredients.goldBlock())
|
.define('B', ingredients.goldBlock())
|
||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||||
.build(ComputerUpgradeRecipe::new)
|
.build(ComputerConvertRecipe::new)
|
||||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
@ -366,7 +363,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
.define('#', ingredients.goldIngot())
|
.define('#', ingredients.goldIngot())
|
||||||
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
|
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
|
||||||
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
|
||||||
.build(ComputerUpgradeRecipe::new)
|
.build(ComputerConvertRecipe::new)
|
||||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
|
||||||
|
|
||||||
ShapedRecipeBuilder
|
ShapedRecipeBuilder
|
||||||
@ -436,18 +433,18 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
|
|
||||||
ShapelessSpecBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.DECORATIONS, playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c"))
|
.shapeless(RecipeCategory.DECORATIONS, playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c"))
|
||||||
.requires(ingredients.head())
|
.requires(ItemTags.SKULLS)
|
||||||
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
|
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
|
||||||
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
|
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
|
||||||
.build(CustomShapelessRecipe::new)
|
.build()
|
||||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy"));
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy"));
|
||||||
|
|
||||||
ShapelessSpecBuilder
|
ShapelessSpecBuilder
|
||||||
.shapeless(RecipeCategory.DECORATIONS, playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb"))
|
.shapeless(RecipeCategory.DECORATIONS, playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb"))
|
||||||
.requires(ingredients.head())
|
.requires(ItemTags.SKULLS)
|
||||||
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
|
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||||
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
||||||
.build(CustomShapelessRecipe::new)
|
.build()
|
||||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
|
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
|
||||||
|
|
||||||
ShapelessSpecBuilder
|
ShapelessSpecBuilder
|
||||||
@ -493,11 +490,11 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ItemPredicate itemPredicate(Ingredient ingredient) {
|
private static ItemPredicate itemPredicate(Ingredient ingredient) {
|
||||||
var json = Util.getOrThrow(Ingredient.CODEC_NONEMPTY.encodeStart(JsonOps.INSTANCE, ingredient), JsonParseException::new);
|
var json = Ingredient.CODEC_NONEMPTY.encodeStart(JsonOps.INSTANCE, ingredient).getOrThrow();
|
||||||
if (!(json instanceof JsonObject object)) throw new IllegalStateException("Unknown ingredient " + json);
|
if (!(json instanceof JsonObject object)) throw new IllegalStateException("Unknown ingredient " + json);
|
||||||
|
|
||||||
if (object.has("item")) {
|
if (object.has("item")) {
|
||||||
var item = Util.getOrThrow(ItemStack.ITEM_WITH_COUNT_CODEC.parse(JsonOps.INSTANCE, object), JsonParseException::new);
|
var item = ItemStack.SIMPLE_ITEM_CODEC.parse(JsonOps.INSTANCE, object).getOrThrow();
|
||||||
return itemPredicate(item.getItem());
|
return itemPredicate(item.getItem());
|
||||||
} else if (object.has("tag")) {
|
} else if (object.has("tag")) {
|
||||||
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
|
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
|
||||||
@ -507,10 +504,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ItemStack playerHead(String name, String uuid) {
|
private static ItemStack playerHead(String name, String uuid) {
|
||||||
var item = new ItemStack(Items.PLAYER_HEAD);
|
return DataComponentUtil.createStack(Items.PLAYER_HEAD, DataComponents.PROFILE, new ResolvableProfile(new GameProfile(UUID.fromString(uuid), name)));
|
||||||
var owner = NbtUtils.writeGameProfile(new CompoundTag(), new GameProfile(UUID.fromString(uuid), name));
|
|
||||||
item.getOrCreateTag().put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addSpecial(RecipeOutput add, Recipe<?> recipe) {
|
private static void addSpecial(RecipeOutput add, Recipe<?> recipe) {
|
||||||
|
@ -7,8 +7,8 @@ package dan200.computercraft.data;
|
|||||||
import dan200.computercraft.api.ComputerCraftTags;
|
import dan200.computercraft.api.ComputerCraftTags;
|
||||||
import dan200.computercraft.impl.RegistryHelper;
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import dan200.computercraft.shared.integration.ExternalModTags;
|
import dan200.computercraft.shared.integration.ExternalModTags;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.data.tags.ItemTagsProvider;
|
import net.minecraft.data.tags.ItemTagsProvider;
|
||||||
import net.minecraft.data.tags.TagsProvider;
|
import net.minecraft.data.tags.TagsProvider;
|
||||||
import net.minecraft.tags.BlockTags;
|
import net.minecraft.tags.BlockTags;
|
||||||
@ -97,6 +97,10 @@ class TagProvider {
|
|||||||
tags.tag(ComputerCraftTags.Items.WIRED_MODEM).add(ModRegistry.Items.WIRED_MODEM.get(), ModRegistry.Items.WIRED_MODEM_FULL.get());
|
tags.tag(ComputerCraftTags.Items.WIRED_MODEM).add(ModRegistry.Items.WIRED_MODEM.get(), ModRegistry.Items.WIRED_MODEM_FULL.get());
|
||||||
tags.copy(ComputerCraftTags.Blocks.MONITOR, ComputerCraftTags.Items.MONITOR);
|
tags.copy(ComputerCraftTags.Blocks.MONITOR, ComputerCraftTags.Items.MONITOR);
|
||||||
|
|
||||||
|
tags.tag(ComputerCraftTags.Items.DYEABLE)
|
||||||
|
.addTag(ComputerCraftTags.Items.TURTLE)
|
||||||
|
.add(ModRegistry.Items.DISK.get(), ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
||||||
|
|
||||||
tags.tag(ItemTags.PIGLIN_LOVED).add(
|
tags.tag(ItemTags.PIGLIN_LOVED).add(
|
||||||
ModRegistry.Items.COMPUTER_ADVANCED.get(), ModRegistry.Items.TURTLE_ADVANCED.get(),
|
ModRegistry.Items.COMPUTER_ADVANCED.get(), ModRegistry.Items.TURTLE_ADVANCED.get(),
|
||||||
ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(),
|
ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(),
|
||||||
|
@ -6,7 +6,6 @@ package dan200.computercraft.data.recipe;
|
|||||||
|
|
||||||
import com.mojang.serialization.DataResult;
|
import com.mojang.serialization.DataResult;
|
||||||
import dan200.computercraft.shared.recipe.RecipeProperties;
|
import dan200.computercraft.shared.recipe.RecipeProperties;
|
||||||
import net.minecraft.Util;
|
|
||||||
import net.minecraft.advancements.AdvancementRequirements;
|
import net.minecraft.advancements.AdvancementRequirements;
|
||||||
import net.minecraft.advancements.AdvancementRewards;
|
import net.minecraft.advancements.AdvancementRewards;
|
||||||
import net.minecraft.advancements.Criterion;
|
import net.minecraft.advancements.Criterion;
|
||||||
@ -90,7 +89,7 @@ public abstract class AbstractRecipeBuilder<S extends AbstractRecipeBuilder<S, O
|
|||||||
* @return The "built" recipe.
|
* @return The "built" recipe.
|
||||||
*/
|
*/
|
||||||
public final FinishedRecipe buildOrThrow(Function<O, DataResult<? extends Recipe<?>>> factory) {
|
public final FinishedRecipe buildOrThrow(Function<O, DataResult<? extends Recipe<?>>> factory) {
|
||||||
return build(s -> Util.getOrThrow(factory.apply(s), IllegalStateException::new));
|
return build(s -> factory.apply(s).getOrThrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -13,6 +13,7 @@ import net.minecraft.tags.TagKey;
|
|||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.Ingredient;
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||||
import net.minecraft.world.level.ItemLike;
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,4 +59,8 @@ public final class ShapelessSpecBuilder extends AbstractRecipeBuilder<ShapelessS
|
|||||||
protected ShapelessRecipeSpec build(RecipeProperties properties) {
|
protected ShapelessRecipeSpec build(RecipeProperties properties) {
|
||||||
return new ShapelessRecipeSpec(properties, ingredients, result);
|
return new ShapelessRecipeSpec(properties, ingredients, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FinishedRecipe build() {
|
||||||
|
return build(spec -> new ShapelessRecipe(spec.properties().group(), spec.properties().category(), spec.result(), spec.ingredients()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,19 @@
|
|||||||
package dan200.computercraft.impl;
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
@ -40,16 +47,46 @@ public class UpgradeManager<T extends UpgradeBase> extends SimpleJsonResourceRel
|
|||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||||
|
|
||||||
public record UpgradeWrapper<T extends UpgradeBase>(
|
public record UpgradeWrapper<T extends UpgradeBase>(
|
||||||
String id, T upgrade, UpgradeSerialiser<? extends T> serialiser, String modId
|
ResourceLocation id, T upgrade, UpgradeSerialiser<? extends T> serialiser, String modId
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String kind;
|
private final String kind;
|
||||||
private final ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry;
|
private final ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry;
|
||||||
|
|
||||||
private Map<String, UpgradeWrapper<T>> current = Map.of();
|
private Map<ResourceLocation, UpgradeWrapper<T>> current = Map.of();
|
||||||
private Map<T, UpgradeWrapper<T>> currentWrappers = Map.of();
|
private Map<T, UpgradeWrapper<T>> currentWrappers = Map.of();
|
||||||
|
|
||||||
|
private final Codec<T> upgradeCodec = ResourceLocation.CODEC.flatXmap(
|
||||||
|
x -> {
|
||||||
|
var upgrade = get(x);
|
||||||
|
return upgrade == null ? DataResult.error(() -> "Unknown upgrade " + x) : DataResult.success(upgrade);
|
||||||
|
},
|
||||||
|
x -> DataResult.success(x.getUpgradeID())
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Codec<UpgradeData<T>> fullCodec = RecordCodecBuilder.create(i -> i.group(
|
||||||
|
upgradeCodec.fieldOf("id").forGetter(UpgradeData::upgrade),
|
||||||
|
DataComponentPatch.CODEC.optionalFieldOf("components", DataComponentPatch.EMPTY).forGetter(UpgradeData::data)
|
||||||
|
).apply(i, UpgradeData::new));
|
||||||
|
|
||||||
|
private final Codec<UpgradeData<T>> codec = Codec.withAlternative(fullCodec, upgradeCodec, UpgradeData::ofDefault);
|
||||||
|
|
||||||
|
private final StreamCodec<ByteBuf, T> upgradeStreamCodec = ResourceLocation.STREAM_CODEC.map(
|
||||||
|
x -> {
|
||||||
|
var upgrade = get(x);
|
||||||
|
if (upgrade == null) throw new IllegalStateException("Unknown upgrade " + x);
|
||||||
|
return upgrade;
|
||||||
|
},
|
||||||
|
UpgradeBase::getUpgradeID
|
||||||
|
);
|
||||||
|
|
||||||
|
private final StreamCodec<RegistryFriendlyByteBuf, UpgradeData<T>> streamCodec = StreamCodec.composite(
|
||||||
|
upgradeStreamCodec, UpgradeData::upgrade,
|
||||||
|
DataComponentPatch.STREAM_CODEC, UpgradeData::data,
|
||||||
|
UpgradeData::new
|
||||||
|
);
|
||||||
|
|
||||||
public UpgradeManager(String kind, String path, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry) {
|
public UpgradeManager(String kind, String path, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry) {
|
||||||
super(GSON, path);
|
super(GSON, path);
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
@ -57,7 +94,7 @@ public class UpgradeManager<T extends UpgradeBase> extends SimpleJsonResourceRel
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public T get(String id) {
|
public T get(ResourceLocation id) {
|
||||||
var wrapper = current.get(id);
|
var wrapper = current.get(id);
|
||||||
return wrapper == null ? null : wrapper.upgrade();
|
return wrapper == null ? null : wrapper.upgrade();
|
||||||
}
|
}
|
||||||
@ -91,14 +128,22 @@ public class UpgradeManager<T extends UpgradeBase> extends SimpleJsonResourceRel
|
|||||||
return currentWrappers.keySet();
|
return currentWrappers.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, UpgradeWrapper<T>> getUpgradeWrappers() {
|
public Map<ResourceLocation, UpgradeWrapper<T>> getUpgradeWrappers() {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Codec<UpgradeData<T>> codec() {
|
||||||
|
return codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, UpgradeData<T>> streamCodec() {
|
||||||
|
return streamCodec;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void apply(Map<ResourceLocation, JsonElement> upgrades, ResourceManager manager, ProfilerFiller profiler) {
|
protected void apply(Map<ResourceLocation, JsonElement> upgrades, ResourceManager manager, ProfilerFiller profiler) {
|
||||||
var registry = RegistryHelper.getRegistry(this.registry);
|
var registry = RegistryHelper.getRegistry(this.registry);
|
||||||
Map<String, UpgradeWrapper<T>> newUpgrades = new HashMap<>();
|
Map<ResourceLocation, UpgradeWrapper<T>> newUpgrades = new HashMap<>();
|
||||||
for (var element : upgrades.entrySet()) {
|
for (var element : upgrades.entrySet()) {
|
||||||
try {
|
try {
|
||||||
loadUpgrade(registry, newUpgrades, element.getKey(), element.getValue());
|
loadUpgrade(registry, newUpgrades, element.getKey(), element.getValue());
|
||||||
@ -112,7 +157,7 @@ public class UpgradeManager<T extends UpgradeBase> extends SimpleJsonResourceRel
|
|||||||
LOG.info("Loaded {} {}s", current.size(), kind);
|
LOG.info("Loaded {} {}s", current.size(), kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadUpgrade(Registry<UpgradeSerialiser<? extends T>> registry, Map<String, UpgradeWrapper<T>> current, ResourceLocation id, JsonElement json) {
|
private void loadUpgrade(Registry<UpgradeSerialiser<? extends T>> registry, Map<ResourceLocation, UpgradeWrapper<T>> current, ResourceLocation id, JsonElement json) {
|
||||||
var root = GsonHelper.convertToJsonObject(json, "top element");
|
var root = GsonHelper.convertToJsonObject(json, "top element");
|
||||||
if (!PlatformHelper.get().shouldLoadResource(root)) return;
|
if (!PlatformHelper.get().shouldLoadResource(root)) return;
|
||||||
|
|
||||||
@ -130,11 +175,11 @@ public class UpgradeManager<T extends UpgradeBase> extends SimpleJsonResourceRel
|
|||||||
throw new IllegalArgumentException("Upgrade " + id + " from " + serialiser + " was incorrectly given id " + upgrade.getUpgradeID());
|
throw new IllegalArgumentException("Upgrade " + id + " from " + serialiser + " was incorrectly given id " + upgrade.getUpgradeID());
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new UpgradeWrapper<T>(id.toString(), upgrade, serialiser, modId);
|
var result = new UpgradeWrapper<T>(id, upgrade, serialiser, modId);
|
||||||
current.put(result.id(), result);
|
current.put(result.id(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadFromNetwork(Map<String, UpgradeWrapper<T>> newUpgrades) {
|
public void loadFromNetwork(Map<ResourceLocation, UpgradeWrapper<T>> newUpgrades) {
|
||||||
current = Collections.unmodifiableMap(newUpgrades);
|
current = Collections.unmodifiableMap(newUpgrades);
|
||||||
currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x));
|
currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.mixin;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
|
import com.mojang.datafixers.DataFix;
|
||||||
|
import com.mojang.datafixers.TypeRewriteRule;
|
||||||
|
import com.mojang.datafixers.schemas.Schema;
|
||||||
|
import com.mojang.serialization.Dynamic;
|
||||||
|
import dan200.computercraft.shared.util.ComponentizationFixers;
|
||||||
|
import net.minecraft.util.datafix.fixes.ItemStackComponentizationFix;
|
||||||
|
import net.minecraft.util.datafix.fixes.References;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrates CC's item NBT to use components.
|
||||||
|
*
|
||||||
|
* @see V3818_3Mixin
|
||||||
|
* @see ComponentizationFixers
|
||||||
|
*/
|
||||||
|
@Mixin(ItemStackComponentizationFix.class)
|
||||||
|
abstract class ItemStackComponentizationFixMixin extends DataFix {
|
||||||
|
@SuppressWarnings("UnusedMethod")
|
||||||
|
private ItemStackComponentizationFixMixin(Schema outputSchema, boolean changesType) {
|
||||||
|
super(outputSchema, changesType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "fixItemStack", at = @At("TAIL"))
|
||||||
|
@SuppressWarnings("UnusedMethod")
|
||||||
|
private static void fixItemStack(ItemStackComponentizationFix.ItemStackData data, Dynamic<?> ops, CallbackInfo ci) {
|
||||||
|
ComponentizationFixers.fixItemComponents(data, ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModifyReturnValue(method = "makeRule", at = @At("RETURN"), remap = false)
|
||||||
|
@SuppressWarnings("UnusedMethod")
|
||||||
|
private TypeRewriteRule wrapMakeRule(TypeRewriteRule existing) {
|
||||||
|
return TypeRewriteRule.seq(existing, fixTypeEverywhereTyped(
|
||||||
|
"Turtle upgrade componentization",
|
||||||
|
getInputSchema().getType(References.BLOCK_ENTITY),
|
||||||
|
getOutputSchema().getType(References.BLOCK_ENTITY),
|
||||||
|
ComponentizationFixers.makeBlockEntityRewrites(getInputSchema(), getOutputSchema())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -7,16 +7,17 @@ package dan200.computercraft.mixin;
|
|||||||
import com.mojang.datafixers.DSL;
|
import com.mojang.datafixers.DSL;
|
||||||
import com.mojang.datafixers.schemas.Schema;
|
import com.mojang.datafixers.schemas.Schema;
|
||||||
import com.mojang.datafixers.types.templates.TypeTemplate;
|
import com.mojang.datafixers.types.templates.TypeTemplate;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
|
||||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
||||||
import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity;
|
import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity;
|
||||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||||
import net.minecraft.util.datafix.fixes.References;
|
import net.minecraft.util.datafix.fixes.References;
|
||||||
|
import net.minecraft.util.datafix.schemas.NamespacedSchema;
|
||||||
import net.minecraft.util.datafix.schemas.V1460;
|
import net.minecraft.util.datafix.schemas.V1460;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -37,16 +38,34 @@ class V1460Mixin {
|
|||||||
var map = ci.getReturnValue();
|
var map = ci.getReturnValue();
|
||||||
|
|
||||||
// Basic inventories
|
// Basic inventories
|
||||||
registerInventory(schema, map, ModRegistry.BlockEntities.TURTLE_NORMAL.id().toString());
|
registerTurtle(schema, map, "computercraft:turtle_normal");
|
||||||
registerInventory(schema, map, ModRegistry.BlockEntities.TURTLE_ADVANCED.id().toString());
|
registerTurtle(schema, map, "computercraft:turtle_advanced");
|
||||||
registerInventory(schema, map, ModRegistry.BlockEntities.PRINTER.id().toString());
|
registerInventory(schema, map, "computercraft:printer");
|
||||||
|
|
||||||
// Disk drives contain a single item
|
// Disk drives contain a single item
|
||||||
schema.register(map, ModRegistry.BlockEntities.DISK_DRIVE.id().toString(), () -> DSL.optionalFields(
|
schema.register(map, "computercraft:disk_drive", () -> DSL.optionalFields(
|
||||||
"Item", References.ITEM_STACK.in(schema)
|
"Item", References.ITEM_STACK.in(schema)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TypeTemplate upgradeData(Schema schema) {
|
||||||
|
return DSL.or(
|
||||||
|
// Pre-1.20.5 we just use the upgrade ID.
|
||||||
|
DSL.constType(NamespacedSchema.namespacedString()),
|
||||||
|
// In newer versions this is represented as a component.
|
||||||
|
DSL.optionalFields("components", References.DATA_COMPONENTS.in(schema))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static void registerTurtle(Schema schema, Map<String, Supplier<TypeTemplate>> map, String name) {
|
||||||
|
schema.register(map, name, () -> DSL.optionalFields(
|
||||||
|
"LeftUpgrade", upgradeData(schema),
|
||||||
|
"RightUpgrade", upgradeData(schema),
|
||||||
|
"Items", DSL.list(References.ITEM_STACK.in(schema))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected static void registerInventory(Schema schema, Map<String, Supplier<TypeTemplate>> map, String name) {
|
protected static void registerInventory(Schema schema, Map<String, Supplier<TypeTemplate>> map, String name) {
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.mixin;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
|
import com.mojang.datafixers.DSL;
|
||||||
|
import com.mojang.datafixers.schemas.Schema;
|
||||||
|
import com.mojang.datafixers.types.templates.TypeTemplate;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import dan200.computercraft.impl.UpgradeManager;
|
||||||
|
import dan200.computercraft.shared.ModRegistry.DataComponents;
|
||||||
|
import net.minecraft.util.datafix.fixes.References;
|
||||||
|
import net.minecraft.util.datafix.schemas.V3818_3;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add our custom data components to the datafixer system.
|
||||||
|
*
|
||||||
|
* @see UpgradeManager#codec()
|
||||||
|
* @see DataComponents#POCKET_UPGRADE
|
||||||
|
* @see DataComponents#LEFT_TURTLE_UPGRADE
|
||||||
|
* @see DataComponents#RIGHT_TURTLE_UPGRADE
|
||||||
|
* @see ItemStackComponentizationFixMixin
|
||||||
|
*/
|
||||||
|
@Mixin(V3818_3.class)
|
||||||
|
class V3818_3Mixin {
|
||||||
|
@ModifyReturnValue(
|
||||||
|
method = "method_57277",
|
||||||
|
at = @At("TAIL")
|
||||||
|
)
|
||||||
|
@SuppressWarnings("UnusedMethod")
|
||||||
|
private static TypeTemplate addExtraTypes(TypeTemplate type, Schema schema) {
|
||||||
|
// Create a codec for UpgradeData
|
||||||
|
var upgradeData = DSL.optionalFields("components", References.DATA_COMPONENTS.in(schema));
|
||||||
|
|
||||||
|
return extraOptionalFields(type,
|
||||||
|
Pair.of("computercraft:pocket_upgrade", upgradeData),
|
||||||
|
Pair.of("computercraft:left_turtle_upgrade", upgradeData),
|
||||||
|
Pair.of("computercraft:right_turtle_upgrade", upgradeData)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
@SuppressWarnings("varargs")
|
||||||
|
private static TypeTemplate extraOptionalFields(TypeTemplate base, Pair<String, TypeTemplate>... fields) {
|
||||||
|
return DSL.and(Stream.concat(
|
||||||
|
Arrays.stream(fields).map(entry -> DSL.optional(DSL.field(entry.getFirst(), entry.getSecond()))),
|
||||||
|
Stream.of(base)
|
||||||
|
).toList());
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import dan200.computercraft.shared.computer.metrics.ComputerMBean;
|
|||||||
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
|
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
|
||||||
import dan200.computercraft.shared.util.DropConsumer;
|
import dan200.computercraft.shared.util.DropConsumer;
|
||||||
import dan200.computercraft.shared.util.TickScheduler;
|
import dan200.computercraft.shared.util.TickScheduler;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
@ -28,7 +29,8 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
|
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
|
||||||
import net.minecraft.world.level.storage.loot.LootPool;
|
import net.minecraft.world.level.storage.loot.LootPool;
|
||||||
import net.minecraft.world.level.storage.loot.entries.LootTableReference;
|
import net.minecraft.world.level.storage.loot.LootTable;
|
||||||
|
import net.minecraft.world.level.storage.loot.entries.NestedLootTable;
|
||||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -92,9 +94,9 @@ public final class CommonHooks {
|
|||||||
TickScheduler.onChunkTicketChanged(level, chunkPos, oldLevel, newLevel);
|
TickScheduler.onChunkTicketChanged(level, chunkPos, oldLevel, newLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final ResourceLocation TREASURE_DISK_LOOT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
|
public static final ResourceKey<LootTable> TREASURE_DISK_LOOT = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk"));
|
||||||
|
|
||||||
private static final Set<ResourceLocation> TREASURE_DISK_LOOT_TABLES = Set.of(
|
private static final Set<ResourceKey<LootTable>> TREASURE_DISK_LOOT_TABLES = Set.of(
|
||||||
BuiltInLootTables.SIMPLE_DUNGEON,
|
BuiltInLootTables.SIMPLE_DUNGEON,
|
||||||
BuiltInLootTables.ABANDONED_MINESHAFT,
|
BuiltInLootTables.ABANDONED_MINESHAFT,
|
||||||
BuiltInLootTables.STRONGHOLD_CORRIDOR,
|
BuiltInLootTables.STRONGHOLD_CORRIDOR,
|
||||||
@ -107,13 +109,13 @@ public final class CommonHooks {
|
|||||||
BuiltInLootTables.VILLAGE_CARTOGRAPHER
|
BuiltInLootTables.VILLAGE_CARTOGRAPHER
|
||||||
);
|
);
|
||||||
|
|
||||||
public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) {
|
public static @Nullable LootPool.Builder getExtraLootPool(ResourceKey<LootTable> lootTable) {
|
||||||
if (!lootTable.getNamespace().equals("minecraft") || !TREASURE_DISK_LOOT_TABLES.contains(lootTable)) {
|
if (!TREASURE_DISK_LOOT_TABLES.contains(lootTable)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return LootPool.lootPool()
|
return LootPool.lootPool()
|
||||||
.add(LootTableReference.lootTableReference(TREASURE_DISK_LOOT))
|
.add(NestedLootTable.lootTableReference(TREASURE_DISK_LOOT))
|
||||||
.setRolls(ConstantValue.exactly(1));
|
.setRolls(ConstantValue.exactly(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared;
|
|||||||
|
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.detail.DetailProvider;
|
import dan200.computercraft.api.detail.DetailProvider;
|
||||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||||
@ -32,9 +33,11 @@ import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
|||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
|
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
||||||
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
||||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||||
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
import dan200.computercraft.shared.computer.items.ServerComputerReference;
|
||||||
|
import dan200.computercraft.shared.computer.recipe.ComputerConvertRecipe;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||||
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
||||||
@ -42,10 +45,7 @@ import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
|||||||
import dan200.computercraft.shared.details.BlockDetails;
|
import dan200.computercraft.shared.details.BlockDetails;
|
||||||
import dan200.computercraft.shared.details.ItemDetails;
|
import dan200.computercraft.shared.details.ItemDetails;
|
||||||
import dan200.computercraft.shared.integration.PermissionRegistry;
|
import dan200.computercraft.shared.integration.PermissionRegistry;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.*;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
|
||||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
|
||||||
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
|
||||||
import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
||||||
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
@ -81,19 +81,23 @@ import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
|||||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||||
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
|
import dan200.computercraft.shared.util.NonNegativeId;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.cauldron.CauldronInteraction;
|
import net.minecraft.core.cauldron.CauldronInteraction;
|
||||||
|
import net.minecraft.core.component.DataComponentType;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.flag.FeatureFlags;
|
import net.minecraft.world.flag.FeatureFlags;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.*;
|
import net.minecraft.world.item.*;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||||
@ -101,12 +105,12 @@ import net.minecraft.world.level.block.Block;
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.material.MapColor;
|
import net.minecraft.world.level.material.MapColor;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
|
* Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
|
||||||
@ -174,8 +178,8 @@ public final class ModRegistry {
|
|||||||
public static class BlockEntities {
|
public static class BlockEntities {
|
||||||
static final RegistrationHelper<BlockEntityType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK_ENTITY_TYPE);
|
static final RegistrationHelper<BlockEntityType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK_ENTITY_TYPE);
|
||||||
|
|
||||||
private static <T extends BlockEntity> RegistryEntry<BlockEntityType<T>> ofBlock(RegistryEntry<? extends Block> block, BiFunction<BlockPos, BlockState, T> factory) {
|
private static <T extends BlockEntity> RegistryEntry<BlockEntityType<T>> ofBlock(RegistryEntry<? extends Block> block, BlockEntityType.BlockEntitySupplier<T> factory) {
|
||||||
return REGISTRY.register(block.id().getPath(), () -> PlatformHelper.get().createBlockEntityType(factory, block.get()));
|
return REGISTRY.register(block.id().getPath(), () -> BlockEntityType.Builder.of(factory, block.get()).build(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final RegistryEntry<BlockEntityType<MonitorBlockEntity>> MONITOR_NORMAL =
|
public static final RegistryEntry<BlockEntityType<MonitorBlockEntity>> MONITOR_NORMAL =
|
||||||
@ -262,6 +266,114 @@ public final class ModRegistry {
|
|||||||
() -> new CableBlockItem.WiredModem(Blocks.CABLE.get(), properties()));
|
() -> new CableBlockItem.WiredModem(Blocks.CABLE.get(), properties()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class DataComponents {
|
||||||
|
static final RegistrationHelper<DataComponentType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.DATA_COMPONENT_TYPE);
|
||||||
|
|
||||||
|
private static <T> RegistryEntry<DataComponentType<T>> register(String name, UnaryOperator<DataComponentType.Builder<T>> unaryOperator) {
|
||||||
|
return REGISTRY.register(name, () -> unaryOperator.apply(DataComponentType.builder()).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of a computer.
|
||||||
|
*
|
||||||
|
* @see AbstractComputerItem
|
||||||
|
* @see PocketComputerItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<NonNegativeId>> COMPUTER_ID = register("computer_id", b -> b
|
||||||
|
.persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The left upgrade of a turtle.
|
||||||
|
*
|
||||||
|
* @see TurtleItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<UpgradeData<ITurtleUpgrade>>> LEFT_TURTLE_UPGRADE = register("left_turtle_upgrade", b -> b
|
||||||
|
.persistent(TurtleUpgrades.instance().codec()).networkSynchronized(TurtleUpgrades.instance().streamCodec())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The right upgrade of a turtle.
|
||||||
|
*
|
||||||
|
* @see TurtleItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<UpgradeData<ITurtleUpgrade>>> RIGHT_TURTLE_UPGRADE = register("right_turtle_upgrade", b -> b
|
||||||
|
.persistent(TurtleUpgrades.instance().codec()).networkSynchronized(TurtleUpgrades.instance().streamCodec())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fuel level of a turtle.
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<Integer>> FUEL = register("fuel", b -> b
|
||||||
|
.persistent(Codec.INT).networkSynchronized(ByteBufCodecs.VAR_INT)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The overlay on a turtle.
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<ResourceLocation>> OVERLAY = register("overlay", b -> b
|
||||||
|
.persistent(ResourceLocation.CODEC).networkSynchronized(ResourceLocation.STREAM_CODEC)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The back upgrade of a pocket computer.
|
||||||
|
*
|
||||||
|
* @see PocketComputerItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<UpgradeData<IPocketUpgrade>>> POCKET_UPGRADE = register("pocket_upgrade", b -> b
|
||||||
|
.persistent(PocketUpgrades.instance().codec()).networkSynchronized(PocketUpgrades.instance().streamCodec())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to the currently running {@link dan200.computercraft.shared.computer.core.ServerComputer}.
|
||||||
|
*
|
||||||
|
* @see ServerComputerReference
|
||||||
|
* @see PocketComputerItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<ServerComputerReference>> COMPUTER = register("computer", b -> b
|
||||||
|
.persistent(ServerComputerReference.CODEC).networkSynchronized(ServerComputerReference.STREAM_CODEC)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this item is currently on.
|
||||||
|
*
|
||||||
|
* @see PocketComputerItem
|
||||||
|
* @see TurtleModem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<Boolean>> ON = register("on", b -> b
|
||||||
|
.persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a treasure disk's mount.
|
||||||
|
*
|
||||||
|
* @see TreasureDiskItem
|
||||||
|
* @see TreasureDisk
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<TreasureDisk>> TREASURE_DISK = register("treasure_disk", b -> b
|
||||||
|
.persistent(TreasureDisk.CODEC).networkSynchronized(TreasureDisk.STREAM_CODEC)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of a disk.
|
||||||
|
*
|
||||||
|
* @see DiskItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<NonNegativeId>> DISK_ID = register("disk_id", b -> b
|
||||||
|
.persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contents of a printed page/printed pages.
|
||||||
|
*
|
||||||
|
* @see PrintoutItem
|
||||||
|
* @see PrintoutData
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<PrintoutData>> PRINTOUT = register("printout", b -> b
|
||||||
|
.persistent(PrintoutData.CODEC).networkSynchronized(PrintoutData.STREAM_CODEC)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static class TurtleSerialisers {
|
public static class TurtleSerialisers {
|
||||||
static final RegistrationHelper<UpgradeSerialiser<? extends ITurtleUpgrade>> REGISTRY = PlatformHelper.get().createRegistrationHelper(ITurtleUpgrade.serialiserRegistryKey());
|
static final RegistrationHelper<UpgradeSerialiser<? extends ITurtleUpgrade>> REGISTRY = PlatformHelper.get().createRegistrationHelper(ITurtleUpgrade.serialiserRegistryKey());
|
||||||
|
|
||||||
@ -292,16 +404,16 @@ public final class ModRegistry {
|
|||||||
static final RegistrationHelper<MenuType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.MENU);
|
static final RegistrationHelper<MenuType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.MENU);
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> COMPUTER = REGISTRY.register("computer",
|
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> COMPUTER = REGISTRY.register("computer",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.COMPUTER.get(), id, inv, data)));
|
() -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.COMPUTER.get(), id, inv, data)));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER = REGISTRY.register("pocket_computer",
|
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER = REGISTRY.register("pocket_computer",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER.get(), id, inv, data)));
|
() -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER.get(), id, inv, data)));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER_NO_TERM = REGISTRY.register("pocket_computer_no_term",
|
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER_NO_TERM = REGISTRY.register("pocket_computer_no_term",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER_NO_TERM.get(), id, inv, data)));
|
() -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER_NO_TERM.get(), id, inv, data)));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<TurtleMenu>> TURTLE = REGISTRY.register("turtle",
|
public static final RegistryEntry<MenuType<TurtleMenu>> TURTLE = REGISTRY.register("turtle",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, TurtleMenu::ofMenuData));
|
() -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, TurtleMenu::ofMenuData));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<DiskDriveMenu>> DISK_DRIVE = REGISTRY.register("disk_drive",
|
public static final RegistryEntry<MenuType<DiskDriveMenu>> DISK_DRIVE = REGISTRY.register("disk_drive",
|
||||||
() -> new MenuType<>(DiskDriveMenu::new, FeatureFlags.VANILLA_SET));
|
() -> new MenuType<>(DiskDriveMenu::new, FeatureFlags.VANILLA_SET));
|
||||||
@ -311,12 +423,12 @@ public final class ModRegistry {
|
|||||||
|
|
||||||
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
|
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
|
||||||
() -> ContainerData.toType(
|
() -> ContainerData.toType(
|
||||||
HeldItemContainerData::new,
|
HeldItemContainerData.STREAM_CODEC,
|
||||||
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
|
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.hand())
|
||||||
));
|
));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ViewComputerMenu>> VIEW_COMPUTER = REGISTRY.register("view_computer",
|
public static final RegistryEntry<MenuType<ViewComputerMenu>> VIEW_COMPUTER = REGISTRY.register("view_computer",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, ViewComputerMenu::new));
|
() -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, ViewComputerMenu::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ArgumentTypes {
|
static class ArgumentTypes {
|
||||||
@ -346,13 +458,13 @@ public final class ModRegistry {
|
|||||||
static final RegistrationHelper<LootItemConditionType> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.LOOT_CONDITION_TYPE);
|
static final RegistrationHelper<LootItemConditionType> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.LOOT_CONDITION_TYPE);
|
||||||
|
|
||||||
public static final RegistryEntry<LootItemConditionType> BLOCK_NAMED = REGISTRY.register("block_named",
|
public static final RegistryEntry<LootItemConditionType> BLOCK_NAMED = REGISTRY.register("block_named",
|
||||||
() -> new LootItemConditionType(Codec.unit(BlockNamedEntityLootCondition.INSTANCE)));
|
() -> new LootItemConditionType(MapCodec.unit(BlockNamedEntityLootCondition.INSTANCE)));
|
||||||
|
|
||||||
public static final RegistryEntry<LootItemConditionType> PLAYER_CREATIVE = REGISTRY.register("player_creative",
|
public static final RegistryEntry<LootItemConditionType> PLAYER_CREATIVE = REGISTRY.register("player_creative",
|
||||||
() -> new LootItemConditionType(Codec.unit(PlayerCreativeLootCondition.INSTANCE)));
|
() -> new LootItemConditionType(MapCodec.unit(PlayerCreativeLootCondition.INSTANCE)));
|
||||||
|
|
||||||
public static final RegistryEntry<LootItemConditionType> HAS_ID = REGISTRY.register("has_id",
|
public static final RegistryEntry<LootItemConditionType> HAS_ID = REGISTRY.register("has_id",
|
||||||
() -> new LootItemConditionType(Codec.unit(HasComputerIdLootCondition.INSTANCE)));
|
() -> new LootItemConditionType(MapCodec.unit(HasComputerIdLootCondition.INSTANCE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RecipeSerializers {
|
public static class RecipeSerializers {
|
||||||
@ -362,21 +474,17 @@ public final class ModRegistry {
|
|||||||
return REGISTRY.register(name, () -> new SimpleCraftingRecipeSerializer<>(factory));
|
return REGISTRY.register(name, () -> new SimpleCraftingRecipeSerializer<>(factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final RegistryEntry<RecipeSerializer<CustomShapedRecipe>> SHAPED = REGISTRY.register("shaped", () -> CustomShapedRecipe.serialiser(CustomShapedRecipe::new));
|
|
||||||
public static final RegistryEntry<RecipeSerializer<CustomShapelessRecipe>> SHAPELESS = REGISTRY.register("shapeless", () -> CustomShapelessRecipe.serialiser(CustomShapelessRecipe::new));
|
|
||||||
|
|
||||||
public static final RegistryEntry<RecipeSerializer<ImpostorShapedRecipe>> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", () -> CustomShapedRecipe.serialiser(ImpostorShapedRecipe::new));
|
public static final RegistryEntry<RecipeSerializer<ImpostorShapedRecipe>> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", () -> CustomShapedRecipe.serialiser(ImpostorShapedRecipe::new));
|
||||||
public static final RegistryEntry<RecipeSerializer<ImpostorShapelessRecipe>> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", () -> CustomShapelessRecipe.serialiser(ImpostorShapelessRecipe::new));
|
public static final RegistryEntry<RecipeSerializer<ImpostorShapelessRecipe>> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", () -> CustomShapelessRecipe.serialiser(ImpostorShapelessRecipe::new));
|
||||||
|
|
||||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
|
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
|
||||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
|
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
|
||||||
public static final RegistryEntry<RecipeSerializer<TurtleRecipe>> TURTLE = REGISTRY.register("turtle", () -> TurtleRecipe.validatingSerialiser(TurtleRecipe::of));
|
|
||||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
|
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
|
||||||
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serialiser::new);
|
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe::serialiser);
|
||||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
|
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
|
||||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
|
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
|
||||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
|
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
|
||||||
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", () -> CustomShapedRecipe.validatingSerialiser(ComputerUpgradeRecipe::of));
|
public static final RegistryEntry<RecipeSerializer<ComputerConvertRecipe>> COMPUTER_CONVERT = REGISTRY.register("computer_convert", () -> CustomShapedRecipe.serialiser(ComputerConvertRecipe::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Permissions {
|
public static class Permissions {
|
||||||
@ -425,7 +533,7 @@ public final class ModRegistry {
|
|||||||
|
|
||||||
out.accept(Items.DISK_DRIVE.get());
|
out.accept(Items.DISK_DRIVE.get());
|
||||||
for (var colour = 0; colour < 16; colour++) {
|
for (var colour = 0; colour < 16; colour++) {
|
||||||
out.accept(DiskItem.createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
|
out.accept(DataComponentUtil.createStack(Items.DISK.get(), net.minecraft.core.component.DataComponents.DYED_COLOR, new DyedItemColor(Colour.VALUES[colour].getHex(), false)));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build());
|
.build());
|
||||||
@ -438,6 +546,7 @@ public final class ModRegistry {
|
|||||||
Blocks.REGISTRY.register();
|
Blocks.REGISTRY.register();
|
||||||
BlockEntities.REGISTRY.register();
|
BlockEntities.REGISTRY.register();
|
||||||
Items.REGISTRY.register();
|
Items.REGISTRY.register();
|
||||||
|
DataComponents.REGISTRY.register();
|
||||||
TurtleSerialisers.REGISTRY.register();
|
TurtleSerialisers.REGISTRY.register();
|
||||||
PocketUpgradeSerialisers.REGISTRY.register();
|
PocketUpgradeSerialisers.REGISTRY.register();
|
||||||
Menus.REGISTRY.register();
|
Menus.REGISTRY.register();
|
||||||
@ -470,14 +579,14 @@ public final class ModRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
||||||
out.accept(turtle.create(-1, null, -1, null, null, 0, null));
|
out.accept(new ItemStack(turtle));
|
||||||
TurtleUpgrades.getVanillaUpgrades()
|
TurtleUpgrades.getVanillaUpgrades()
|
||||||
.map(x -> turtle.create(-1, null, -1, null, UpgradeData.ofDefault(x), 0, null))
|
.map(x -> DataComponentUtil.createStack(turtle, DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(x)))
|
||||||
.forEach(out::accept);
|
.forEach(out::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket) {
|
private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket) {
|
||||||
out.accept(pocket.create(-1, null, -1, null));
|
out.accept(new ItemStack(pocket));
|
||||||
PocketUpgrades.getVanillaUpgrades().map(x -> pocket.create(-1, null, -1, UpgradeData.ofDefault(x))).forEach(out::accept);
|
PocketUpgrades.getVanillaUpgrades().map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ public final class CommandComputerCraft {
|
|||||||
*/
|
*/
|
||||||
private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
|
private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
|
||||||
var player = source.getPlayerOrException();
|
var player = source.getPlayerOrException();
|
||||||
new ComputerContainerData(computer, ItemStack.EMPTY).open(player, new MenuProvider() {
|
new ComputerContainerData(computer, new ItemStack(ModRegistry.Items.COMPUTER_NORMAL.get())).open(player, new MenuProvider() {
|
||||||
@Override
|
@Override
|
||||||
public Component getDisplayName() {
|
public Component getDisplayName() {
|
||||||
return Component.translatable("gui.computercraft.view_computer");
|
return Component.translatable("gui.computercraft.view_computer");
|
||||||
|
@ -14,8 +14,6 @@ import net.minecraft.core.registries.BuiltInRegistries;
|
|||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for working with arguments.
|
* Utilities for working with arguments.
|
||||||
*
|
*
|
||||||
@ -45,13 +43,12 @@ public class ArgumentUtils {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>> void serializeToNetwork(FriendlyByteBuf buffer, ArgumentTypeInfo<A, T> type, ArgumentTypeInfo.Template<A> template) {
|
private static <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>> void serializeToNetwork(FriendlyByteBuf buffer, ArgumentTypeInfo<A, T> type, ArgumentTypeInfo.Template<A> template) {
|
||||||
buffer.writeId(BuiltInRegistries.COMMAND_ARGUMENT_TYPE, type);
|
buffer.writeVarInt(BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getIdOrThrow(type));
|
||||||
type.serializeToNetwork((T) template, buffer);
|
type.serializeToNetwork((T) template, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArgumentTypeInfo.Template<?> deserialize(FriendlyByteBuf buffer) {
|
public static ArgumentTypeInfo.Template<?> deserialize(FriendlyByteBuf buffer) {
|
||||||
var type = buffer.readById(BuiltInRegistries.COMMAND_ARGUMENT_TYPE);
|
var type = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.byIdOrThrow(buffer.readVarInt());
|
||||||
Objects.requireNonNull(type, "Unknown argument type");
|
|
||||||
return type.deserializeFromNetwork(buffer);
|
return type.deserializeFromNetwork(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,10 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
|||||||
import net.minecraft.commands.CommandBuildContext;
|
import net.minecraft.commands.CommandBuildContext;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.ComponentSerialization;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -114,14 +116,14 @@ public final class RepeatArgumentType<T, U> implements ArgumentType<List<T>> {
|
|||||||
public void serializeToNetwork(RepeatArgumentType.Template arg, FriendlyByteBuf buf) {
|
public void serializeToNetwork(RepeatArgumentType.Template arg, FriendlyByteBuf buf) {
|
||||||
buf.writeBoolean(arg.flatten);
|
buf.writeBoolean(arg.flatten);
|
||||||
ArgumentUtils.serializeToNetwork(buf, arg.child);
|
ArgumentUtils.serializeToNetwork(buf, arg.child);
|
||||||
buf.writeComponent(ArgumentUtils.getMessage(arg.some));
|
ComponentSerialization.TRUSTED_CONTEXT_FREE_STREAM_CODEC.encode(buf, ArgumentUtils.getMessage(arg.some));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RepeatArgumentType.Template deserializeFromNetwork(FriendlyByteBuf buf) {
|
public RepeatArgumentType.Template deserializeFromNetwork(FriendlyByteBuf buf) {
|
||||||
var isList = buf.readBoolean();
|
var isList = buf.readBoolean();
|
||||||
var child = ArgumentUtils.deserialize(buf);
|
var child = ArgumentUtils.deserialize(buf);
|
||||||
var message = buf.readComponent();
|
var message = ComponentSerialization.TRUSTED_CONTEXT_FREE_STREAM_CODEC.decode(buf);
|
||||||
return new RepeatArgumentType.Template(this, child, isList, new SimpleCommandExceptionType(message));
|
return new RepeatArgumentType.Template(this, child, isList, new SimpleCommandExceptionType(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +136,7 @@ public final class RepeatArgumentType<T, U> implements ArgumentType<List<T>> {
|
|||||||
public void serializeToJson(RepeatArgumentType.Template arg, JsonObject json) {
|
public void serializeToJson(RepeatArgumentType.Template arg, JsonObject json) {
|
||||||
json.addProperty("flatten", arg.flatten);
|
json.addProperty("flatten", arg.flatten);
|
||||||
json.add("child", ArgumentUtils.serializeToJson(arg.child));
|
json.add("child", ArgumentUtils.serializeToJson(arg.child));
|
||||||
json.addProperty("error", Component.Serializer.toJson(ArgumentUtils.getMessage(arg.some)));
|
json.addProperty("error", Component.Serializer.toJson(ArgumentUtils.getMessage(arg.some), RegistryAccess.EMPTY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.common;
|
package dan200.computercraft.shared.common;
|
||||||
|
|
||||||
import dan200.computercraft.shared.container.BasicContainer;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.Container;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
@ -17,7 +14,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||||||
/**
|
/**
|
||||||
* A {@link BlockEntity} which exposes an inventory.
|
* A {@link BlockEntity} which exposes an inventory.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractContainerBlockEntity extends BaseContainerBlockEntity implements BasicContainer {
|
public abstract class AbstractContainerBlockEntity extends BaseContainerBlockEntity {
|
||||||
protected AbstractContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
protected AbstractContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||||
super(type, pos, state);
|
super(type, pos, state);
|
||||||
}
|
}
|
||||||
@ -26,9 +23,4 @@ public abstract class AbstractContainerBlockEntity extends BaseContainerBlockEnt
|
|||||||
protected final Component getDefaultName() {
|
protected final Component getDefaultName() {
|
||||||
return Component.translatable(getBlockState().getBlock().getDescriptionId());
|
return Component.translatable(getBlockState().getBlock().getDescriptionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean stillValid(Player player) {
|
|
||||||
return Container.stillValidBlockEntity(this, player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,12 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.common;
|
package dan200.computercraft.shared.common;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftTags;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.core.NonNullList;
|
import net.minecraft.core.NonNullList;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.world.inventory.CraftingContainer;
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
@ -16,7 +19,7 @@ import net.minecraft.world.item.crafting.RecipeSerializer;
|
|||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Craft a wet sponge with a {@linkplain IColouredItem dyable item} to remove its dye.
|
* Craft a wet sponge with a {@linkplain ComputerCraftTags.Items#DYEABLE dyable item} to remove its dye.
|
||||||
*/
|
*/
|
||||||
public final class ClearColourRecipe extends CustomRecipe {
|
public final class ClearColourRecipe extends CustomRecipe {
|
||||||
public ClearColourRecipe(CraftingBookCategory category) {
|
public ClearColourRecipe(CraftingBookCategory category) {
|
||||||
@ -31,9 +34,9 @@ public final class ClearColourRecipe extends CustomRecipe {
|
|||||||
var stack = inv.getItem(i);
|
var stack = inv.getItem(i);
|
||||||
if (stack.isEmpty()) continue;
|
if (stack.isEmpty()) continue;
|
||||||
|
|
||||||
if (stack.getItem() instanceof IColouredItem colourable) {
|
if (stack.is(ComputerCraftTags.Items.DYEABLE)) {
|
||||||
if (hasColourable) return false;
|
if (hasColourable) return false;
|
||||||
if (colourable.getColour(stack) == -1) return false;
|
if (!stack.has(DataComponents.DYED_COLOR)) return false;
|
||||||
hasColourable = true;
|
hasColourable = true;
|
||||||
} else if (stack.getItem() == Items.WET_SPONGE) {
|
} else if (stack.getItem() == Items.WET_SPONGE) {
|
||||||
if (hasSponge) return false;
|
if (hasSponge) return false;
|
||||||
@ -47,19 +50,17 @@ public final class ClearColourRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack assemble(CraftingContainer inv, RegistryAccess registryAccess) {
|
public ItemStack assemble(CraftingContainer inv, HolderLookup.Provider registryAccess) {
|
||||||
var colourable = ItemStack.EMPTY;
|
var colourable = ItemStack.EMPTY;
|
||||||
|
|
||||||
for (var i = 0; i < inv.getContainerSize(); i++) {
|
for (var i = 0; i < inv.getContainerSize(); i++) {
|
||||||
var stack = inv.getItem(i);
|
var stack = inv.getItem(i);
|
||||||
if (stack.getItem() instanceof IColouredItem) colourable = stack;
|
if (stack.is(ComputerCraftTags.Items.DYEABLE)) colourable = stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colourable.isEmpty()) return ItemStack.EMPTY;
|
if (colourable.isEmpty()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
var stack = ((IColouredItem) colourable.getItem()).withColour(colourable, -1);
|
return DataComponentUtil.createResult(colourable, DataComponents.DYED_COLOR, null);
|
||||||
stack.setCount(1);
|
|
||||||
return stack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.common;
|
package dan200.computercraft.shared.common;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftTags;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.util.ColourTracker;
|
import dan200.computercraft.shared.util.ColourTracker;
|
||||||
import dan200.computercraft.shared.util.ColourUtils;
|
import dan200.computercraft.shared.util.ColourUtils;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.world.inventory.CraftingContainer;
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
@ -28,7 +32,7 @@ public final class ColourableRecipe extends CustomRecipe {
|
|||||||
var stack = inv.getItem(i);
|
var stack = inv.getItem(i);
|
||||||
if (stack.isEmpty()) continue;
|
if (stack.isEmpty()) continue;
|
||||||
|
|
||||||
if (stack.getItem() instanceof IColouredItem) {
|
if (stack.is(ComputerCraftTags.Items.DYEABLE)) {
|
||||||
if (hasColourable) return false;
|
if (hasColourable) return false;
|
||||||
hasColourable = true;
|
hasColourable = true;
|
||||||
} else if (ColourUtils.getStackColour(stack) != null) {
|
} else if (ColourUtils.getStackColour(stack) != null) {
|
||||||
@ -42,7 +46,7 @@ public final class ColourableRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack assemble(CraftingContainer inv, RegistryAccess registryAccess) {
|
public ItemStack assemble(CraftingContainer inv, HolderLookup.Provider registryAccess) {
|
||||||
var colourable = ItemStack.EMPTY;
|
var colourable = ItemStack.EMPTY;
|
||||||
|
|
||||||
var tracker = new ColourTracker();
|
var tracker = new ColourTracker();
|
||||||
@ -52,7 +56,7 @@ public final class ColourableRecipe extends CustomRecipe {
|
|||||||
|
|
||||||
if (stack.isEmpty()) continue;
|
if (stack.isEmpty()) continue;
|
||||||
|
|
||||||
if (stack.getItem() instanceof IColouredItem) {
|
if (stack.is(ComputerCraftTags.Items.DYEABLE)) {
|
||||||
colourable = stack;
|
colourable = stack;
|
||||||
} else {
|
} else {
|
||||||
var dye = ColourUtils.getStackColour(stack);
|
var dye = ColourUtils.getStackColour(stack);
|
||||||
@ -60,11 +64,10 @@ public final class ColourableRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colourable.isEmpty()) return ItemStack.EMPTY;
|
return colourable.isEmpty()
|
||||||
|
? ItemStack.EMPTY
|
||||||
|
: DataComponentUtil.createResult(colourable, DataComponents.DYED_COLOR, new DyedItemColor(tracker.getColour(), false));
|
||||||
|
|
||||||
var stack = ((IColouredItem) colourable.getItem()).withColour(colourable, tracker.getColour());
|
|
||||||
stack.setCount(1);
|
|
||||||
return stack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -6,12 +6,9 @@ package dan200.computercraft.shared.common;
|
|||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.Containers;
|
import net.minecraft.world.Containers;
|
||||||
import net.minecraft.world.InteractionHand;
|
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.BaseEntityBlock;
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
@ -24,7 +21,6 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
|||||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A block which has a container and can be placed in a horizontal direction.
|
* A block which has a container and can be placed in a horizontal direction.
|
||||||
@ -44,20 +40,17 @@ public abstract class HorizontalContainerBlock extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected final BlockState mirror(BlockState state, Mirror mirrorIn) {
|
||||||
public final BlockState mirror(BlockState state, Mirror mirrorIn) {
|
|
||||||
return state.rotate(mirrorIn.getRotation(state.getValue(FACING)));
|
return state.rotate(mirrorIn.getRotation(state.getValue(FACING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected final BlockState rotate(BlockState state, Rotation rot) {
|
||||||
public final BlockState rotate(BlockState state, Rotation rot) {
|
|
||||||
return state.setValue(FACING, rot.rotate(state.getValue(FACING)));
|
return state.setValue(FACING, rot.rotate(state.getValue(FACING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
|
||||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
|
||||||
if (level.isClientSide) return InteractionResult.SUCCESS;
|
if (level.isClientSide) return InteractionResult.SUCCESS;
|
||||||
|
|
||||||
if (level.getBlockEntity(pos) instanceof BaseContainerBlockEntity container) {
|
if (level.getBlockEntity(pos) instanceof BaseContainerBlockEntity container) {
|
||||||
@ -68,8 +61,7 @@ public abstract class HorizontalContainerBlock extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||||
public final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
|
||||||
if (state.is(newState.getBlock())) return;
|
if (state.is(newState.getBlock())) return;
|
||||||
|
|
||||||
if (level.getBlockEntity(pos) instanceof BaseContainerBlockEntity container) {
|
if (level.getBlockEntity(pos) instanceof BaseContainerBlockEntity container) {
|
||||||
@ -81,27 +73,17 @@ public abstract class HorizontalContainerBlock extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
protected final boolean hasAnalogOutputSignal(BlockState pState) {
|
||||||
if (stack.hasCustomHoverName() && world.getBlockEntity(pos) instanceof BaseContainerBlockEntity container) {
|
|
||||||
container.setCustomName(stack.getHoverName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public final boolean hasAnalogOutputSignal(BlockState pState) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected final int getAnalogOutputSignal(BlockState pBlockState, Level pLevel, BlockPos pPos) {
|
||||||
public final int getAnalogOutputSignal(BlockState pBlockState, Level pLevel, BlockPos pPos) {
|
|
||||||
return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(pLevel.getBlockEntity(pPos));
|
return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(pLevel.getBlockEntity(pPos));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected RenderShape getRenderShape(BlockState state) {
|
||||||
public RenderShape getRenderShape(BlockState state) {
|
|
||||||
return RenderShape.MODEL;
|
return RenderShape.MODEL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.common;
|
|
||||||
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
public interface IColouredItem {
|
|
||||||
String NBT_COLOUR = "Color";
|
|
||||||
|
|
||||||
default int getColour(ItemStack stack) {
|
|
||||||
return getColourBasic(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
default ItemStack withColour(ItemStack stack, int colour) {
|
|
||||||
var copy = stack.copy();
|
|
||||||
setColourBasic(copy, colour);
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getColourBasic(ItemStack stack) {
|
|
||||||
var tag = stack.getTag();
|
|
||||||
return tag != null && tag.contains(NBT_COLOUR) ? tag.getInt(NBT_COLOUR) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setColourBasic(ItemStack stack, int colour) {
|
|
||||||
if (colour == -1) {
|
|
||||||
var tag = stack.getTag();
|
|
||||||
if (tag != null) tag.remove(NBT_COLOUR);
|
|
||||||
} else {
|
|
||||||
stack.getOrCreateTag().putInt(NBT_COLOUR, colour);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -78,7 +78,7 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
var table = VanillaDetailRegistries.BLOCK_IN_WORLD.getDetails(block);
|
var table = VanillaDetailRegistries.BLOCK_IN_WORLD.getDetails(block);
|
||||||
|
|
||||||
var tile = block.blockEntity();
|
var tile = block.blockEntity();
|
||||||
if (tile != null) table.put("nbt", NBTUtil.toLua(tile.saveWithFullMetadata()));
|
if (tile != null) table.put("nbt", NBTUtil.toLua(tile.saveWithFullMetadata(world.registryAccess())));
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,14 @@ package dan200.computercraft.shared.computer.blocks;
|
|||||||
import dan200.computercraft.annotations.ForgeOverride;
|
import dan200.computercraft.annotations.ForgeOverride;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
||||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.stats.Stats;
|
|
||||||
import net.minecraft.world.InteractionHand;
|
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.MenuProvider;
|
import net.minecraft.world.MenuProvider;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
@ -27,16 +22,14 @@ import net.minecraft.world.level.Level;
|
|||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
|
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.storage.loot.LootParams;
|
|
||||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@ -52,8 +45,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {
|
||||||
public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {
|
|
||||||
super.onPlace(state, world, pos, oldState, isMoving);
|
super.onPlace(state, world, pos, oldState, isMoving);
|
||||||
|
|
||||||
var tile = world.getBlockEntity(pos);
|
var tile = world.getBlockEntity(pos);
|
||||||
@ -61,14 +53,12 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected boolean isSignalSource(BlockState state) {
|
||||||
public boolean isSignalSource(BlockState state) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected int getDirectSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {
|
||||||
public int getDirectSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {
|
|
||||||
var entity = world.getBlockEntity(pos);
|
var entity = world.getBlockEntity(pos);
|
||||||
if (!(entity instanceof AbstractComputerBlockEntity computerEntity)) return 0;
|
if (!(entity instanceof AbstractComputerBlockEntity computerEntity)) return 0;
|
||||||
|
|
||||||
@ -79,11 +69,14 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
return computer.getRedstoneOutput(localSide);
|
return computer.getRedstoneOutput(localSide);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract ItemStack getItem(AbstractComputerBlockEntity tile);
|
private ItemStack getItem(AbstractComputerBlockEntity tile) {
|
||||||
|
var stack = new ItemStack(this);
|
||||||
|
stack.applyComponents(tile.collectComponents());
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected int getSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {
|
||||||
public int getSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {
|
|
||||||
return getDirectSignal(state, world, pos, incomingSide);
|
return getDirectSignal(state, world, pos, incomingSide);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +93,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
|
||||||
public ItemStack getCloneItemStack(LevelReader world, BlockPos pos, BlockState state) {
|
public ItemStack getCloneItemStack(LevelReader world, BlockPos pos, BlockState state) {
|
||||||
var tile = world.getBlockEntity(pos);
|
var tile = world.getBlockEntity(pos);
|
||||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
if (tile instanceof AbstractComputerBlockEntity computer) {
|
||||||
@ -113,55 +105,21 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity tile, ItemStack tool) {
|
public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity tile, ItemStack tool) {
|
||||||
// Don't drop blocks here - see onBlockHarvested.
|
// Use the same trick as DoublePlantBlock, to skip dropping items. See playerWillDestroy.
|
||||||
player.awardStat(Stats.BLOCK_MINED.get(this));
|
super.playerDestroy(world, player, pos, Blocks.AIR.defaultBlockState(), tile, tool);
|
||||||
player.causeFoodExhaustion(0.005F);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockState playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) {
|
public BlockState playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) {
|
||||||
var result = super.playerWillDestroy(world, pos, state, player);
|
|
||||||
if (!(world instanceof ServerLevel serverWorld)) return result;
|
|
||||||
|
|
||||||
// We drop the item here instead of doing it in the harvest method, as we should
|
// We drop the item here instead of doing it in the harvest method, as we should
|
||||||
// drop computers for creative players too.
|
// drop computers for creative players too.
|
||||||
|
Block.dropResources(state, world, pos, world.getBlockEntity(pos), player, player.getMainHandItem());
|
||||||
|
|
||||||
var tile = world.getBlockEntity(pos);
|
return super.playerWillDestroy(world, pos, state, player);
|
||||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
|
||||||
var context = new LootParams.Builder(serverWorld)
|
|
||||||
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
|
|
||||||
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
|
|
||||||
.withParameter(LootContextParams.THIS_ENTITY, player)
|
|
||||||
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
|
|
||||||
.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
|
||||||
for (var item : state.getDrops(context)) {
|
|
||||||
popResource(world, pos, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.spawnAfterBreak(serverWorld, pos, player.getMainHandItem(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
|
||||||
super.setPlacedBy(world, pos, state, placer, stack);
|
|
||||||
|
|
||||||
var tile = world.getBlockEntity(pos);
|
|
||||||
if (!world.isClientSide && tile instanceof IComputerBlockEntity computer && stack.getItem() instanceof IComputerItem item) {
|
|
||||||
|
|
||||||
var id = item.getComputerID(stack);
|
|
||||||
if (id != -1) computer.setComputerID(id);
|
|
||||||
|
|
||||||
var label = item.getLabel(stack);
|
|
||||||
if (label != null) computer.setLabel(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
|
||||||
if (!player.isCrouching() && level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) {
|
if (!player.isCrouching() && level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) {
|
||||||
// Regular right click to activate computer
|
// Regular right click to activate computer
|
||||||
if (!level.isClientSide && computer.isUsable(player)) {
|
if (!level.isClientSide && computer.isUsable(player)) {
|
||||||
@ -173,12 +131,11 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.use(state, level, pos, player, hand, hit);
|
return super.useWithoutItem(state, level, pos, player, hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected final void neighborChanged(BlockState state, Level world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos, boolean isMoving) {
|
||||||
public final void neighborChanged(BlockState state, Level world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos, boolean isMoving) {
|
|
||||||
var be = world.getBlockEntity(pos);
|
var be = world.getBlockEntity(pos);
|
||||||
if (be instanceof AbstractComputerBlockEntity computer) computer.neighborChanged(neighbourPos);
|
if (be instanceof AbstractComputerBlockEntity computer) computer.neighborChanged(neighbourPos);
|
||||||
}
|
}
|
||||||
@ -190,8 +147,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||||
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
|
||||||
var be = level.getBlockEntity(pos);
|
var be = level.getBlockEntity(pos);
|
||||||
if (be instanceof AbstractComputerBlockEntity computer) computer.neighbourShapeChanged(direction);
|
if (be instanceof AbstractComputerBlockEntity computer) computer.neighbourShapeChanged(direction);
|
||||||
|
|
||||||
@ -200,8 +156,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
protected MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) {
|
||||||
public MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) {
|
|
||||||
return level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer ? computer : null;
|
return level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer ? computer : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,18 +9,19 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.impl.BundledRedstone;
|
import dan200.computercraft.impl.BundledRedstone;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.platform.ComponentAccess;
|
import dan200.computercraft.shared.platform.ComponentAccess;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
import dan200.computercraft.shared.util.*;
|
||||||
import dan200.computercraft.shared.util.DirectionUtil;
|
|
||||||
import dan200.computercraft.shared.util.IDAssigner;
|
|
||||||
import dan200.computercraft.shared.util.RedstoneUtil;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.component.DataComponentMap;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
@ -76,8 +77,8 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
unload();
|
unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getInteractRange() {
|
protected float getInteractRange() {
|
||||||
return Container.DEFAULT_DISTANCE_LIMIT;
|
return Container.DEFAULT_DISTANCE_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUsable(Player player) {
|
public boolean isUsable(Player player) {
|
||||||
@ -137,7 +138,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
protected abstract void updateBlockState(ComputerState newState);
|
protected abstract void updateBlockState(ComputerState newState);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveAdditional(CompoundTag nbt) {
|
public void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||||
// Save ID, label and power state
|
// Save ID, label and power state
|
||||||
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
|
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
|
||||||
if (label != null) nbt.putString(NBT_LABEL, label);
|
if (label != null) nbt.putString(NBT_LABEL, label);
|
||||||
@ -145,20 +146,20 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
|
|
||||||
lockCode.addToTag(nbt);
|
lockCode.addToTag(nbt);
|
||||||
|
|
||||||
super.saveAdditional(nbt);
|
super.saveAdditional(nbt, registries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void load(CompoundTag nbt) {
|
public final void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||||
super.load(nbt);
|
super.loadAdditional(nbt, registries);
|
||||||
if (level != null && level.isClientSide) {
|
if (level != null && level.isClientSide) {
|
||||||
loadClient(nbt);
|
loadClient(nbt, registries);
|
||||||
} else {
|
} else {
|
||||||
loadServer(nbt);
|
loadServer(nbt, registries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadServer(CompoundTag nbt) {
|
protected void loadServer(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||||
// Load ID, label and power state
|
// Load ID, label and power state
|
||||||
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
|
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
|
||||||
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
|
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
|
||||||
@ -167,6 +168,31 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
lockCode = LockCode.fromTag(nbt);
|
lockCode = LockCode.fromTag(nbt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyImplicitComponents(DataComponentInput component) {
|
||||||
|
super.applyImplicitComponents(component);
|
||||||
|
label = DataComponentUtil.getCustomName(component.get(DataComponents.CUSTOM_NAME));
|
||||||
|
computerID = NonNegativeId.getId(component.get(ModRegistry.DataComponents.COMPUTER_ID.get()));
|
||||||
|
lockCode = component.getOrDefault(DataComponents.LOCK, LockCode.NO_LOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void collectImplicitComponents(DataComponentMap.Builder builder) {
|
||||||
|
super.collectImplicitComponents(builder);
|
||||||
|
builder.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.of(computerID));
|
||||||
|
builder.set(DataComponents.CUSTOM_NAME, label == null ? null : Component.literal(label));
|
||||||
|
if (lockCode != LockCode.NO_LOCK) builder.set(DataComponents.LOCK, lockCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void removeComponentsFromTag(CompoundTag tag) {
|
||||||
|
super.removeComponentsFromTag(tag);
|
||||||
|
tag.remove(NBT_ID);
|
||||||
|
tag.remove(NBT_LABEL);
|
||||||
|
tag.remove(LockCode.TAG_LOCK);
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isPeripheralBlockedOnSide(ComputerSide localSide) {
|
protected boolean isPeripheralBlockedOnSide(ComputerSide localSide) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -370,15 +396,15 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag getUpdateTag() {
|
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
|
||||||
// We need this for pick block on the client side.
|
// We need this for pick block on the client side.
|
||||||
var nbt = super.getUpdateTag();
|
var nbt = super.getUpdateTag(registries);
|
||||||
if (label != null) nbt.putString(NBT_LABEL, label);
|
if (label != null) nbt.putString(NBT_LABEL, label);
|
||||||
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
|
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
|
||||||
return nbt;
|
return nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadClient(CompoundTag nbt) {
|
protected void loadClient(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||||
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
|
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
|
||||||
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
|
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,9 @@ package dan200.computercraft.shared.computer.blocks;
|
|||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
|
||||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||||
import dan200.computercraft.shared.util.BlockCodecs;
|
import dan200.computercraft.shared.util.BlockCodecs;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
@ -55,12 +53,4 @@ public class ComputerBlock<T extends ComputerBlockEntity> extends AbstractComput
|
|||||||
public BlockState getStateForPlacement(BlockPlaceContext placement) {
|
public BlockState getStateForPlacement(BlockPlaceContext placement) {
|
||||||
return defaultBlockState().setValue(FACING, placement.getHorizontalDirection().getOpposite());
|
return defaultBlockState().setValue(FACING, placement.getHorizontalDirection().getOpposite());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
|
|
||||||
if (!(tile instanceof ComputerBlockEntity computer)) return ItemStack.EMPTY;
|
|
||||||
if (!(asItem() instanceof ComputerItem item)) return ItemStack.EMPTY;
|
|
||||||
|
|
||||||
return item.create(computer.getComputerID(), computer.getLabel());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,30 +7,32 @@ package dan200.computercraft.shared.computer.items;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.filesystem.Mount;
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
import dan200.computercraft.api.media.IMedia;
|
import dan200.computercraft.api.media.IMedia;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
|
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.item.BlockItem;
|
import net.minecraft.world.item.BlockItem;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class AbstractComputerItem extends BlockItem implements IComputerItem, IMedia {
|
public class AbstractComputerItem extends BlockItem implements IMedia {
|
||||||
public AbstractComputerItem(AbstractComputerBlock<?> block, Properties settings) {
|
public AbstractComputerItem(AbstractComputerBlock<?> block, Properties settings) {
|
||||||
super(block, settings);
|
super(block, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag options) {
|
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||||
if (options.isAdvanced() || getLabel(stack) == null) {
|
if (options.isAdvanced() || !stack.has(DataComponents.CUSTOM_NAME)) {
|
||||||
var id = getComputerID(stack);
|
var id = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
|
||||||
if (id >= 0) {
|
if (id != null) {
|
||||||
list.add(Component.translatable("gui.computercraft.tooltip.computer_id", id)
|
list.add(Component.translatable("gui.computercraft.tooltip.computer_id", id.id())
|
||||||
.withStyle(ChatFormatting.GRAY));
|
.withStyle(ChatFormatting.GRAY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,22 +40,18 @@ public abstract class AbstractComputerItem extends BlockItem implements ICompute
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getLabel(ItemStack stack) {
|
public @Nullable String getLabel(ItemStack stack) {
|
||||||
return IComputerItem.super.getLabel(stack);
|
return DataComponentUtil.getCustomName(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setLabel(ItemStack stack, @Nullable String label) {
|
public boolean setLabel(ItemStack stack, @Nullable String label) {
|
||||||
if (label != null) {
|
DataComponentUtil.setCustomName(stack, label);
|
||||||
stack.setHoverName(Component.literal(label));
|
|
||||||
} else {
|
|
||||||
stack.resetHoverName();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
|
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||||
var id = getComputerID(stack);
|
var id = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
|
||||||
return id >= 0 ? ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit) : null;
|
return id != null ? ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id.id(), Config.computerSpaceLimit) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,10 @@
|
|||||||
package dan200.computercraft.shared.computer.items;
|
package dan200.computercraft.shared.computer.items;
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.world.item.Item;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public class ComputerItem extends AbstractComputerItem {
|
public class ComputerItem extends AbstractComputerItem {
|
||||||
|
// TODO: Do we deprecate this?
|
||||||
public ComputerItem(ComputerBlock<?> block, Properties settings) {
|
public ComputerItem(ComputerBlock<?> block, Properties settings) {
|
||||||
super(block, settings);
|
super(block, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack create(int id, @Nullable String label) {
|
|
||||||
var result = new ItemStack(this);
|
|
||||||
if (id >= 0) result.getOrCreateTag().putInt(NBT_ID, id);
|
|
||||||
if (label != null) result.setHoverName(Component.literal(label));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemStack changeItem(ItemStack stack, Item newItem) {
|
|
||||||
return newItem instanceof ComputerItem computer
|
|
||||||
? computer.create(getComputerID(stack), getLabel(stack))
|
|
||||||
: ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.computer.items;
|
|
||||||
|
|
||||||
import net.minecraft.world.item.Item;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public interface IComputerItem {
|
|
||||||
String NBT_ID = "ComputerId";
|
|
||||||
|
|
||||||
default int getComputerID(ItemStack stack) {
|
|
||||||
var nbt = stack.getTag();
|
|
||||||
return nbt != null && nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable String getLabel(ItemStack stack) {
|
|
||||||
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new stack, changing the underlying item.
|
|
||||||
* <p>
|
|
||||||
* This should copy the computer's data to a different item of the same type (for instance, converting a normal
|
|
||||||
* computer to an advanced one).
|
|
||||||
*
|
|
||||||
* @param stack The current computer stack.
|
|
||||||
* @param newItem The new item.
|
|
||||||
* @return The new stack, possibly {@linkplain ItemStack#EMPTY empty} if {@code newItem} is of the same type.
|
|
||||||
*/
|
|
||||||
ItemStack changeItem(ItemStack stack, Item newItem);
|
|
||||||
}
|
|
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.computer.items;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||||
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.core.component.DataComponentHolder;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to a {@link ServerComputer}.
|
||||||
|
*
|
||||||
|
* @param session The current {@linkplain ServerComputerRegistry#getSessionID() session id}.
|
||||||
|
* @param instance The computer's {@linkplain ServerComputer#getInstanceUUID() instance id}.
|
||||||
|
*/
|
||||||
|
public record ServerComputerReference(int session, UUID instance) {
|
||||||
|
public static final Codec<ServerComputerReference> CODEC = RecordCodecBuilder.create(i -> i.group(
|
||||||
|
Codec.INT.fieldOf("session").forGetter(ServerComputerReference::session),
|
||||||
|
UUIDUtil.CODEC.fieldOf("instance").forGetter(ServerComputerReference::instance)
|
||||||
|
).apply(i, ServerComputerReference::new));
|
||||||
|
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, ServerComputerReference> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.VAR_INT, ServerComputerReference::session,
|
||||||
|
UUIDUtil.STREAM_CODEC, ServerComputerReference::instance,
|
||||||
|
ServerComputerReference::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public @Nullable ServerComputer get(ServerComputerRegistry registry) {
|
||||||
|
return registry.get(session, this.instance());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable ServerComputer get(DataComponentHolder holder, ServerComputerRegistry registry) {
|
||||||
|
var reference = holder.get(ModRegistry.DataComponents.COMPUTER.get());
|
||||||
|
return reference == null ? null : reference.get(registry);
|
||||||
|
}
|
||||||
|
}
|
@ -4,43 +4,50 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.computer.recipe;
|
package dan200.computercraft.shared.computer.recipe;
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.recipe.CustomShapedRecipe;
|
import dan200.computercraft.shared.recipe.CustomShapedRecipe;
|
||||||
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
|
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.world.inventory.CraftingContainer;
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recipe which converts a computer from one form into another.
|
* A recipe which converts a computer from one form into another.
|
||||||
*/
|
*/
|
||||||
public abstract class ComputerConvertRecipe extends CustomShapedRecipe {
|
public final class ComputerConvertRecipe extends CustomShapedRecipe {
|
||||||
|
private final Item result;
|
||||||
|
|
||||||
public ComputerConvertRecipe(ShapedRecipeSpec recipe) {
|
public ComputerConvertRecipe(ShapedRecipeSpec recipe) {
|
||||||
super(recipe);
|
super(recipe);
|
||||||
}
|
this.result = recipe.result().getItem();
|
||||||
|
|
||||||
protected abstract ItemStack convert(IComputerItem item, ItemStack stack);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(CraftingContainer inventory, Level world) {
|
|
||||||
if (!super.matches(inventory, world)) return false;
|
|
||||||
|
|
||||||
for (var i = 0; i < inventory.getContainerSize(); i++) {
|
|
||||||
if (inventory.getItem(i).getItem() instanceof IComputerItem) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAccess) {
|
public ItemStack assemble(CraftingContainer inventory, HolderLookup.Provider registryAccess) {
|
||||||
// Find our computer item and convert it.
|
// Find our computer item and copy the components across.
|
||||||
for (var i = 0; i < inventory.getContainerSize(); i++) {
|
for (var i = 0; i < inventory.getContainerSize(); i++) {
|
||||||
var stack = inventory.getItem(i);
|
var stack = inventory.getItem(i);
|
||||||
if (stack.getItem() instanceof IComputerItem) return convert((IComputerItem) stack.getItem(), stack);
|
if (isComputerItem(stack.getItem())) {
|
||||||
|
var newStack = new ItemStack(result);
|
||||||
|
newStack.applyComponents(stack.getComponentsPatch());
|
||||||
|
return newStack;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ItemStack.EMPTY;
|
return ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecipeSerializer<ComputerConvertRecipe> getSerializer() {
|
||||||
|
return ModRegistry.RecipeSerializers.COMPUTER_CONVERT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isComputerItem(Item item) {
|
||||||
|
// TODO: Make this a little more general. Either with a tag, or a predicate on the recipe itself?
|
||||||
|
return item instanceof AbstractComputerItem || item instanceof PocketComputerItem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.computer.recipe;
|
|
||||||
|
|
||||||
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.world.item.Item;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting to it a new item (for instance a normal
|
|
||||||
* turtle to an advanced one).
|
|
||||||
*
|
|
||||||
* @see IComputerItem#changeItem(ItemStack, Item)
|
|
||||||
*/
|
|
||||||
public final class ComputerUpgradeRecipe extends ComputerConvertRecipe {
|
|
||||||
private final Item result;
|
|
||||||
|
|
||||||
public ComputerUpgradeRecipe(ShapedRecipeSpec recipe) {
|
|
||||||
super(recipe);
|
|
||||||
this.result = recipe.result().getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataResult<ComputerUpgradeRecipe> 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(recipe));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ItemStack convert(IComputerItem item, ItemStack stack) {
|
|
||||||
return item.changeItem(stack, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RecipeSerializer<ComputerUpgradeRecipe> getSerializer() {
|
|
||||||
return ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.computer.terminal;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -19,6 +20,8 @@ import javax.annotation.Nullable;
|
|||||||
* states, etc...
|
* states, etc...
|
||||||
*/
|
*/
|
||||||
public class TerminalState {
|
public class TerminalState {
|
||||||
|
public static final StreamCodec<FriendlyByteBuf, TerminalState> STREAM_CODEC = StreamCodec.ofMember(TerminalState::write, TerminalState::new);
|
||||||
|
|
||||||
private final boolean colour;
|
private final boolean colour;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
@ -38,7 +41,7 @@ public class TerminalState {
|
|||||||
return terminal == null ? null : new TerminalState(terminal);
|
return terminal == null ? null : new TerminalState(terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TerminalState(FriendlyByteBuf buf) {
|
private TerminalState(FriendlyByteBuf buf) {
|
||||||
colour = buf.readBoolean();
|
colour = buf.readBoolean();
|
||||||
width = buf.readVarInt();
|
width = buf.readVarInt();
|
||||||
height = buf.readVarInt();
|
height = buf.readVarInt();
|
||||||
@ -47,7 +50,7 @@ public class TerminalState {
|
|||||||
buffer = buf.readBytes(length);
|
buffer = buf.readBytes(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
private void write(FriendlyByteBuf buf) {
|
||||||
buf.writeBoolean(colour);
|
buf.writeBoolean(colour);
|
||||||
buf.writeVarInt(width);
|
buf.writeVarInt(width);
|
||||||
buf.writeVarInt(height);
|
buf.writeVarInt(height);
|
||||||
|
@ -10,19 +10,19 @@ import net.minecraft.world.ContainerHelper;
|
|||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic implementation of {@link Container} which operates on a {@linkplain #getContents() list of stacks}.
|
* A basic implementation of {@link Container} which operates on a {@linkplain #getItems() list of stacks}.
|
||||||
*/
|
*/
|
||||||
public interface BasicContainer extends Container {
|
public interface BasicContainer extends Container {
|
||||||
NonNullList<ItemStack> getContents();
|
NonNullList<ItemStack> getItems();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default int getContainerSize() {
|
default int getContainerSize() {
|
||||||
return getContents().size();
|
return getItems().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean isEmpty() {
|
default boolean isEmpty() {
|
||||||
for (var stack : getContents()) {
|
for (var stack : getItems()) {
|
||||||
if (!stack.isEmpty()) return false;
|
if (!stack.isEmpty()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,27 +31,33 @@ public interface BasicContainer extends Container {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default ItemStack getItem(int slot) {
|
default ItemStack getItem(int slot) {
|
||||||
var contents = getContents();
|
var contents = getItems();
|
||||||
return slot >= 0 && slot < contents.size() ? contents.get(slot) : ItemStack.EMPTY;
|
return slot >= 0 && slot < contents.size() ? contents.get(slot) : ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default ItemStack removeItemNoUpdate(int slot) {
|
default ItemStack removeItemNoUpdate(int slot) {
|
||||||
return ContainerHelper.takeItem(getContents(), slot);
|
return ContainerHelper.takeItem(getItems(), slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default ItemStack removeItem(int slot, int count) {
|
default ItemStack removeItem(int slot, int count) {
|
||||||
return ContainerHelper.removeItem(getContents(), slot, count);
|
return ContainerHelper.removeItem(getItems(), slot, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void setItem(int slot, ItemStack itemStack) {
|
default void setItem(int slot, ItemStack itemStack) {
|
||||||
getContents().set(slot, itemStack);
|
getItems().set(slot, itemStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void clearContent() {
|
default void clearContent() {
|
||||||
getContents().clear();
|
getItems().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void defaultSetItems(NonNullList<ItemStack> inventory, NonNullList<ItemStack> items) {
|
||||||
|
var i = 0;
|
||||||
|
for (; i < items.size(); i++) inventory.set(i, items.get(i));
|
||||||
|
for (; i < inventory.size(); i++) inventory.set(i, ItemStack.EMPTY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic implementation of {@link WorldlyContainer} which operates on a {@linkplain #getContents() list of stacks}.
|
* A basic implementation of {@link WorldlyContainer} which operates on a {@linkplain #getItems() list of stacks}.
|
||||||
*/
|
*/
|
||||||
public interface BasicWorldlyContainer extends BasicContainer, WorldlyContainer {
|
public interface BasicWorldlyContainer extends BasicContainer, WorldlyContainer {
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,7 +27,7 @@ public final class PlayerCreativeLootCondition implements LootItemCondition {
|
|||||||
@Override
|
@Override
|
||||||
public boolean test(LootContext lootContext) {
|
public boolean test(LootContext lootContext) {
|
||||||
var entity = lootContext.getParamOrNull(LootContextParams.THIS_ENTITY);
|
var entity = lootContext.getParamOrNull(LootContextParams.THIS_ENTITY);
|
||||||
return entity instanceof Player player && player.getAbilities().instabuild;
|
return entity instanceof Player player && player.isCreative();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -4,20 +4,23 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.details;
|
package dan200.computercraft.shared.details;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
|
||||||
import dan200.computercraft.shared.util.NBTUtil;
|
import dan200.computercraft.shared.util.NBTUtil;
|
||||||
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.NbtOps;
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.CreativeModeTabs;
|
import net.minecraft.world.item.CreativeModeTabs;
|
||||||
import net.minecraft.world.item.EnchantedBookItem;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||||
|
import net.minecraft.world.item.enchantment.ItemEnchantments;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data providers for items.
|
* Data providers for items.
|
||||||
@ -26,7 +29,9 @@ public class ItemDetails {
|
|||||||
public static void fillBasic(Map<? super String, Object> data, ItemStack stack) {
|
public static void fillBasic(Map<? super String, Object> data, ItemStack stack) {
|
||||||
data.put("name", DetailHelpers.getId(BuiltInRegistries.ITEM, stack.getItem()));
|
data.put("name", DetailHelpers.getId(BuiltInRegistries.ITEM, stack.getItem()));
|
||||||
data.put("count", stack.getCount());
|
data.put("count", stack.getCount());
|
||||||
var hash = NBTUtil.getNBTHash(stack.getTag());
|
|
||||||
|
var components = stack.getComponentsPatch();
|
||||||
|
var hash = components.isEmpty() ? null : NBTUtil.getNBTHash(DataComponentPatch.CODEC.encodeStart(NbtOps.INSTANCE, components).result().orElse(null));
|
||||||
if (hash != null) data.put("nbt", hash);
|
if (hash != null) data.put("nbt", hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,41 +51,14 @@ public class ItemDetails {
|
|||||||
data.put("tags", DetailHelpers.getTags(stack.getTags()));
|
data.put("tags", DetailHelpers.getTags(stack.getTags()));
|
||||||
data.put("itemGroups", getItemGroups(stack));
|
data.put("itemGroups", getItemGroups(stack));
|
||||||
|
|
||||||
var tag = stack.getTag();
|
var lore = stack.get(DataComponents.LORE);
|
||||||
if (tag != null && tag.contains("display", Tag.TAG_COMPOUND)) {
|
if (lore != null) data.put("lore", lore.lines().stream().map(Component::getString).toList());
|
||||||
var displayTag = tag.getCompound("display");
|
|
||||||
if (displayTag.contains("Lore", Tag.TAG_LIST)) {
|
|
||||||
var loreTag = displayTag.getList("Lore", Tag.TAG_STRING);
|
|
||||||
data.put("lore", loreTag.stream()
|
|
||||||
.map(ItemDetails::parseTextComponent)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(Component::getString)
|
|
||||||
.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
var enchants = getAllEnchants(stack);
|
||||||
* Used to hide some data from ItemStack tooltip.
|
|
||||||
* @see https://minecraft.wiki/w/Tutorials/Command_NBT_tags
|
|
||||||
* @see ItemStack#getTooltip
|
|
||||||
*/
|
|
||||||
var hideFlags = tag != null ? tag.getInt("HideFlags") : 0;
|
|
||||||
|
|
||||||
var enchants = getAllEnchants(stack, hideFlags);
|
|
||||||
if (!enchants.isEmpty()) data.put("enchantments", enchants);
|
if (!enchants.isEmpty()) data.put("enchantments", enchants);
|
||||||
|
|
||||||
if (tag != null && tag.getBoolean("Unbreakable") && (hideFlags & 4) == 0) {
|
var unbreakable = stack.get(DataComponents.UNBREAKABLE);
|
||||||
data.put("unbreakable", true);
|
if (unbreakable != null && unbreakable.showInTooltip()) data.put("unbreakable", true);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static Component parseTextComponent(Tag x) {
|
|
||||||
try {
|
|
||||||
return Component.Serializer.fromJson(x.getAsString());
|
|
||||||
} catch (JsonParseException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,26 +85,13 @@ public class ItemDetails {
|
|||||||
/**
|
/**
|
||||||
* Retrieve all visible enchantments from given stack. Try to follow all tooltip rules : order and visibility.
|
* Retrieve all visible enchantments from given stack. Try to follow all tooltip rules : order and visibility.
|
||||||
*
|
*
|
||||||
* @param stack Stack to analyse
|
* @param stack Stack to analyse
|
||||||
* @param hideFlags An int used as bit field to provide visibility rules.
|
|
||||||
* @return A filled list that contain all visible enchantments.
|
* @return A filled list that contain all visible enchantments.
|
||||||
*/
|
*/
|
||||||
private static List<Map<String, Object>> getAllEnchants(ItemStack stack, int hideFlags) {
|
private static List<Map<String, Object>> getAllEnchants(ItemStack stack) {
|
||||||
var enchants = new ArrayList<Map<String, Object>>(0);
|
var enchants = new ArrayList<Map<String, Object>>(0);
|
||||||
|
addEnchantments(stack.get(DataComponents.STORED_ENCHANTMENTS), enchants);
|
||||||
if (stack.getItem() instanceof EnchantedBookItem && (hideFlags & 32) == 0) {
|
addEnchantments(stack.get(DataComponents.ENCHANTMENTS), enchants);
|
||||||
addEnchantments(EnchantedBookItem.getEnchantments(stack), enchants);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack.isEnchanted() && (hideFlags & 1) == 0) {
|
|
||||||
/*
|
|
||||||
* Mimic the EnchantmentHelper.getEnchantments(ItemStack stack) behavior without special case for Enchanted book.
|
|
||||||
* I'll do that to have the same data than ones displayed in tooltip.
|
|
||||||
* @see EnchantmentHelper.getEnchantments(ItemStack stack)
|
|
||||||
*/
|
|
||||||
addEnchantments(stack.getEnchantmentTags(), enchants);
|
|
||||||
}
|
|
||||||
|
|
||||||
return enchants;
|
return enchants;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,18 +103,18 @@ public class ItemDetails {
|
|||||||
* @see EnchantmentHelper
|
* @see EnchantmentHelper
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("NonApiType")
|
@SuppressWarnings("NonApiType")
|
||||||
private static void addEnchantments(ListTag rawEnchants, ArrayList<Map<String, Object>> enchants) {
|
private static void addEnchantments(@Nullable ItemEnchantments rawEnchants, ArrayList<Map<String, Object>> enchants) {
|
||||||
if (rawEnchants.isEmpty()) return;
|
if (rawEnchants == null || rawEnchants.isEmpty()) return;
|
||||||
|
|
||||||
enchants.ensureCapacity(enchants.size() + rawEnchants.size());
|
enchants.ensureCapacity(enchants.size() + rawEnchants.size());
|
||||||
|
|
||||||
for (var entry : EnchantmentHelper.deserializeEnchantments(rawEnchants).entrySet()) {
|
for (var entry : rawEnchants.entrySet()) {
|
||||||
var enchantment = entry.getKey();
|
var enchantment = entry.getKey();
|
||||||
var level = entry.getValue();
|
var level = entry.getIntValue();
|
||||||
var enchant = new HashMap<String, Object>(3);
|
var enchant = new HashMap<String, Object>(3);
|
||||||
enchant.put("name", DetailHelpers.getId(BuiltInRegistries.ENCHANTMENT, enchantment));
|
enchant.put("name", DetailHelpers.getId(BuiltInRegistries.ENCHANTMENT, enchantment.value()));
|
||||||
enchant.put("level", level);
|
enchant.put("level", level);
|
||||||
enchant.put("displayName", enchantment.getFullname(level).getString());
|
enchant.put("displayName", enchantment.value().getFullname(level).getString());
|
||||||
enchants.add(enchant);
|
enchants.add(enchant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.impl.TurtleUpgrades;
|
|||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@ -54,14 +55,14 @@ public final class RecipeModHelpers {
|
|||||||
for (var turtleSupplier : TURTLES) {
|
for (var turtleSupplier : TURTLES) {
|
||||||
var turtle = turtleSupplier.get();
|
var turtle = turtleSupplier.get();
|
||||||
for (var upgrade : TurtleUpgrades.instance().getUpgrades()) {
|
for (var upgrade : TurtleUpgrades.instance().getUpgrades()) {
|
||||||
upgradeItems.add(turtle.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), 0, null));
|
upgradeItems.add(DataComponentUtil.createStack(turtle, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var pocketSupplier : POCKET_COMPUTERS) {
|
for (var pocketSupplier : POCKET_COMPUTERS) {
|
||||||
var pocket = pocketSupplier.get();
|
var pocket = pocketSupplier.get();
|
||||||
for (var upgrade : PocketUpgrades.instance().getUpgrades()) {
|
for (var upgrade : PocketUpgrades.instance().getUpgrades()) {
|
||||||
upgradeItems.add(pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade)));
|
upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,10 @@ import dan200.computercraft.api.upgrades.UpgradeBase;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
import dan200.computercraft.impl.TurtleUpgrades;
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import net.minecraft.core.NonNullList;
|
import net.minecraft.core.NonNullList;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@ -105,10 +107,10 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
public List<T> findRecipesWithInput(ItemStack stack) {
|
public List<T> findRecipesWithInput(ItemStack stack) {
|
||||||
setupCache();
|
setupCache();
|
||||||
|
|
||||||
if (stack.getItem() instanceof TurtleItem item) {
|
if (stack.getItem() instanceof TurtleItem) {
|
||||||
// Suggest possible upgrades which can be applied to this turtle
|
// Suggest possible upgrades which can be applied to this turtle
|
||||||
var left = item.getUpgradeWithData(stack, TurtleSide.LEFT);
|
var left = TurtleItem.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||||
var right = item.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
var right = TurtleItem.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||||
if (left != null && right != null) return List.of();
|
if (left != null && right != null) return List.of();
|
||||||
|
|
||||||
List<T> recipes = new ArrayList<>();
|
List<T> recipes = new ArrayList<>();
|
||||||
@ -176,11 +178,11 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
*/
|
*/
|
||||||
public List<T> findRecipesWithOutput(ItemStack stack) {
|
public List<T> findRecipesWithOutput(ItemStack stack) {
|
||||||
// Find which upgrade this item currently has, and so how we could build it.
|
// Find which upgrade this item currently has, and so how we could build it.
|
||||||
if (stack.getItem() instanceof TurtleItem item) {
|
if (stack.getItem() instanceof TurtleItem) {
|
||||||
List<T> recipes = new ArrayList<>(0);
|
List<T> recipes = new ArrayList<>(0);
|
||||||
|
|
||||||
var left = item.getUpgradeWithData(stack, TurtleSide.LEFT);
|
var left = TurtleItem.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||||
var right = item.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
var right = TurtleItem.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||||
|
|
||||||
// The turtle is facing towards us, so upgrades on the left are actually crafted on the right.
|
// The turtle is facing towards us, so upgrades on the left are actually crafted on the right.
|
||||||
if (left != null) {
|
if (left != null) {
|
||||||
@ -215,18 +217,16 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ItemStack turtleWith(ItemStack stack, @Nullable UpgradeData<ITurtleUpgrade> left, @Nullable UpgradeData<ITurtleUpgrade> right) {
|
private static ItemStack turtleWith(ItemStack stack, @Nullable UpgradeData<ITurtleUpgrade> left, @Nullable UpgradeData<ITurtleUpgrade> right) {
|
||||||
var item = (TurtleItem) stack.getItem();
|
var newStack = stack.copyWithCount(1);
|
||||||
return item.create(
|
newStack.set(ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get(), left);
|
||||||
item.getComputerID(stack), item.getLabel(stack), item.getColour(stack),
|
newStack.set(ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), right);
|
||||||
left, right, item.getFuelLevel(stack), item.getOverlay(stack)
|
return stack;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) {
|
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) {
|
||||||
var item = (PocketComputerItem) stack.getItem();
|
var newStack = stack.copyWithCount(1);
|
||||||
return item.create(
|
newStack.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), back);
|
||||||
item.getComputerID(stack), item.getLabel(stack), item.getColour(stack), back
|
return stack;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) {
|
private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) {
|
||||||
@ -278,8 +278,8 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
var turtleItem = turtleSupplier.get();
|
var turtleItem = turtleSupplier.get();
|
||||||
recipes.add(turtle(
|
recipes.add(turtle(
|
||||||
ingredient, // Right upgrade, recipe on left
|
ingredient, // Right upgrade, recipe on left
|
||||||
Ingredient.of(turtleItem.create(-1, null, -1, null, null, 0, null)),
|
Ingredient.of(new ItemStack(turtleItem)),
|
||||||
turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(turtle), 0, null)
|
DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(turtle))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,8 +289,8 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
var pocketItem = pocketSupplier.get();
|
var pocketItem = pocketSupplier.get();
|
||||||
recipes.add(pocket(
|
recipes.add(pocket(
|
||||||
ingredient,
|
ingredient,
|
||||||
Ingredient.of(pocketItem.create(-1, null, -1, null)),
|
Ingredient.of(pocketItem),
|
||||||
pocketItem.create(-1, null, -1, UpgradeData.ofDefault(pocket))
|
DataComponentUtil.createStack(pocketItem, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(pocket))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,14 +70,11 @@ public class JEIComputerCraft implements IModPlugin {
|
|||||||
* Distinguishes turtles by upgrades and family.
|
* Distinguishes turtles by upgrades and family.
|
||||||
*/
|
*/
|
||||||
private static final IIngredientSubtypeInterpreter<ItemStack> turtleSubtype = (stack, ctx) -> {
|
private static final IIngredientSubtypeInterpreter<ItemStack> turtleSubtype = (stack, ctx) -> {
|
||||||
var item = stack.getItem();
|
|
||||||
if (!(item instanceof TurtleItem turtle)) return IIngredientSubtypeInterpreter.NONE;
|
|
||||||
|
|
||||||
var name = new StringBuilder("turtle:");
|
var name = new StringBuilder("turtle:");
|
||||||
|
|
||||||
// Add left and right upgrades to the identifier
|
// Add left and right upgrades to the identifier
|
||||||
var left = turtle.getUpgrade(stack, TurtleSide.LEFT);
|
var left = TurtleItem.getUpgrade(stack, TurtleSide.LEFT);
|
||||||
var right = turtle.getUpgrade(stack, TurtleSide.RIGHT);
|
var right = TurtleItem.getUpgrade(stack, TurtleSide.RIGHT);
|
||||||
if (left != null) name.append(left.getUpgradeID());
|
if (left != null) name.append(left.getUpgradeID());
|
||||||
if (left != null && right != null) name.append('|');
|
if (left != null && right != null) name.append('|');
|
||||||
if (right != null) name.append(right.getUpgradeID());
|
if (right != null) name.append(right.getUpgradeID());
|
||||||
@ -89,9 +86,6 @@ public class JEIComputerCraft implements IModPlugin {
|
|||||||
* Distinguishes pocket computers by upgrade and family.
|
* Distinguishes pocket computers by upgrade and family.
|
||||||
*/
|
*/
|
||||||
private static final IIngredientSubtypeInterpreter<ItemStack> pocketSubtype = (stack, ctx) -> {
|
private static final IIngredientSubtypeInterpreter<ItemStack> pocketSubtype = (stack, ctx) -> {
|
||||||
var item = stack.getItem();
|
|
||||||
if (!(item instanceof PocketComputerItem)) return IIngredientSubtypeInterpreter.NONE;
|
|
||||||
|
|
||||||
var name = new StringBuilder("pocket:");
|
var name = new StringBuilder("pocket:");
|
||||||
|
|
||||||
// Add the upgrade to the identifier
|
// Add the upgrade to the identifier
|
||||||
@ -104,11 +98,5 @@ public class JEIComputerCraft implements IModPlugin {
|
|||||||
/**
|
/**
|
||||||
* Distinguishes disks by colour.
|
* Distinguishes disks by colour.
|
||||||
*/
|
*/
|
||||||
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> {
|
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DiskItem.getColour(stack));
|
||||||
var item = stack.getItem();
|
|
||||||
if (!(item instanceof DiskItem disk)) return IIngredientSubtypeInterpreter.NONE;
|
|
||||||
|
|
||||||
var colour = disk.getColour(stack);
|
|
||||||
return colour == -1 ? IIngredientSubtypeInterpreter.NONE : String.format("%06x", colour);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -10,43 +10,34 @@ import dan200.computercraft.api.filesystem.Mount;
|
|||||||
import dan200.computercraft.api.media.IMedia;
|
import dan200.computercraft.api.media.IMedia;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
|
import dan200.computercraft.shared.util.NonNegativeId;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DiskItem extends Item implements IMedia, IColouredItem {
|
public class DiskItem extends Item implements IMedia {
|
||||||
private static final String NBT_ID = "DiskId";
|
|
||||||
|
|
||||||
public DiskItem(Properties settings) {
|
public DiskItem(Properties settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemStack createFromIDAndColour(int id, @Nullable String label, int colour) {
|
|
||||||
var stack = new ItemStack(ModRegistry.Items.DISK.get());
|
|
||||||
setDiskID(stack, id);
|
|
||||||
ModRegistry.Items.DISK.get().setLabel(stack, label);
|
|
||||||
IColouredItem.setColourBasic(stack, colour);
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag options) {
|
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||||
if (options.isAdvanced()) {
|
if (options.isAdvanced()) {
|
||||||
var id = getDiskID(stack);
|
var id = stack.get(ModRegistry.DataComponents.DISK_ID.get());
|
||||||
if (id >= 0) {
|
if (id != null) {
|
||||||
list.add(Component.translatable("gui.computercraft.tooltip.disk_id", id)
|
list.add(Component.translatable("gui.computercraft.tooltip.disk_id", id.id())
|
||||||
.withStyle(ChatFormatting.GRAY));
|
.withStyle(ChatFormatting.GRAY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,41 +50,27 @@ public class DiskItem extends Item implements IMedia, IColouredItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getLabel(ItemStack stack) {
|
public @Nullable String getLabel(ItemStack stack) {
|
||||||
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
|
var label = stack.get(DataComponents.CUSTOM_NAME);
|
||||||
|
return label != null ? label.getString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setLabel(ItemStack stack, @Nullable String label) {
|
public boolean setLabel(ItemStack stack, @Nullable String label) {
|
||||||
if (label != null) {
|
stack.set(DataComponents.CUSTOM_NAME, label != null ? Component.literal(label) : null);
|
||||||
stack.setHoverName(Component.literal(label));
|
|
||||||
} else {
|
|
||||||
stack.resetHoverName();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
|
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||||
var diskID = getDiskID(stack);
|
var diskID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.DISK_ID.get(), "disk");
|
||||||
if (diskID < 0) {
|
|
||||||
diskID = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), "disk");
|
|
||||||
setDiskID(stack, diskID);
|
|
||||||
}
|
|
||||||
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "disk/" + diskID, Config.floppySpaceLimit);
|
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "disk/" + diskID, Config.floppySpaceLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getDiskID(ItemStack stack) {
|
public static int getDiskID(ItemStack stack) {
|
||||||
var nbt = stack.getTag();
|
return NonNegativeId.getId(stack.get(ModRegistry.DataComponents.DISK_ID.get()));
|
||||||
return nbt != null && nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setDiskID(ItemStack stack, int id) {
|
public static int getColour(ItemStack stack) {
|
||||||
if (id >= 0) stack.getOrCreateTag().putInt(NBT_ID, id);
|
return DyedItemColor.getOrDefault(stack, Colour.WHITE.getARGB());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColour(ItemStack stack) {
|
|
||||||
var colour = IColouredItem.getColourBasic(stack);
|
|
||||||
return colour == -1 ? Colour.WHITE.getHex() : colour;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.media.items;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contents of a printout.
|
||||||
|
*
|
||||||
|
* @param title The title of this printout.
|
||||||
|
* @param lines A list of lines for this printout.
|
||||||
|
* @see PrintoutItem
|
||||||
|
* @see dan200.computercraft.shared.ModRegistry.DataComponents#PRINTOUT
|
||||||
|
*/
|
||||||
|
public record PrintoutData(String title, List<Line> lines) {
|
||||||
|
public static final int LINE_LENGTH = 25;
|
||||||
|
public static final int LINES_PER_PAGE = 21;
|
||||||
|
public static final int MAX_PAGES = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An empty printout. This has no title, and is a single page of empty lines.
|
||||||
|
*/
|
||||||
|
public static final PrintoutData EMPTY;
|
||||||
|
|
||||||
|
static {
|
||||||
|
var lines = new Line[LINES_PER_PAGE];
|
||||||
|
Arrays.fill(lines, Line.EMPTY);
|
||||||
|
EMPTY = new PrintoutData("", List.of(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Codec<String> LINE_TEXT = Codec.STRING.validate(x -> x.length() == LINE_LENGTH
|
||||||
|
? DataResult.success(x)
|
||||||
|
: DataResult.error(() -> "Expected string of length " + LINE_LENGTH));
|
||||||
|
|
||||||
|
private static final Codec<Line> LINE_CODEC = RecordCodecBuilder.<Line>create(s -> s.group(
|
||||||
|
LINE_TEXT.fieldOf("text").forGetter(Line::text),
|
||||||
|
LINE_TEXT.fieldOf("foreground").forGetter(Line::foreground)
|
||||||
|
).apply(s, Line::new));
|
||||||
|
|
||||||
|
private static final StreamCodec<ByteBuf, Line> LINE_STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.STRING_UTF8, Line::text,
|
||||||
|
ByteBufCodecs.STRING_UTF8, Line::foreground,
|
||||||
|
Line::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final Codec<PrintoutData> CODEC = RecordCodecBuilder.<PrintoutData>create(s -> s.group(
|
||||||
|
Codec.STRING.optionalFieldOf("title", "").forGetter(PrintoutData::title),
|
||||||
|
LINE_CODEC.listOf(1, MAX_PAGES * LINES_PER_PAGE)
|
||||||
|
.validate(PrintoutData::validateLines)
|
||||||
|
.fieldOf("lines").forGetter(PrintoutData::lines)
|
||||||
|
).apply(s, PrintoutData::new));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PrintoutData> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.STRING_UTF8, PrintoutData::title,
|
||||||
|
LINE_STREAM_CODEC.apply(ByteBufCodecs.list(MAX_PAGES * LINES_PER_PAGE)), PrintoutData::lines,
|
||||||
|
PrintoutData::new
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single line on our printed pages.
|
||||||
|
*
|
||||||
|
* @param text The text for this line.
|
||||||
|
* @param foreground The foreground colour of this line, in a format equivalent to {@link Terminal#getTextColourLine(int)}.
|
||||||
|
*/
|
||||||
|
public record Line(String text, String foreground) {
|
||||||
|
public static final Line EMPTY = new Line(" ".repeat(LINE_LENGTH), "f".repeat(LINE_LENGTH));
|
||||||
|
|
||||||
|
public Line {
|
||||||
|
if (text.length() != LINE_LENGTH) throw new IllegalArgumentException("text is of wrong length");
|
||||||
|
if (foreground.length() != LINE_LENGTH) throw new IllegalArgumentException("foreground is of wrong length");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintoutData {
|
||||||
|
validateLines(lines).getOrThrow(IllegalArgumentException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataResult<List<Line>> validateLines(List<Line> lines) {
|
||||||
|
if (lines.isEmpty()) return DataResult.error(() -> "Expected non-empty list of lines");
|
||||||
|
if ((lines.size() % LINES_PER_PAGE) != 0) return DataResult.error(() -> "Not enough lines for a page");
|
||||||
|
if (lines.size() > LINES_PER_PAGE * MAX_PAGES) return DataResult.error(() -> "Too many pages");
|
||||||
|
return DataResult.success(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of pages in this printout.
|
||||||
|
*
|
||||||
|
* @return The number of pages.
|
||||||
|
*/
|
||||||
|
public int pages() {
|
||||||
|
return Math.ceilDiv(lines.size(), LINES_PER_PAGE);
|
||||||
|
}
|
||||||
|
}
|
@ -17,19 +17,9 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PrintoutItem extends Item {
|
public class PrintoutItem extends Item {
|
||||||
private static final String NBT_TITLE = "Title";
|
|
||||||
private static final String NBT_PAGES = "Pages";
|
|
||||||
private static final String NBT_LINE_TEXT = "Text";
|
|
||||||
private static final String NBT_LINE_COLOUR = "Color";
|
|
||||||
|
|
||||||
public static final int LINES_PER_PAGE = 21;
|
|
||||||
public static final int LINE_MAX_LENGTH = 25;
|
|
||||||
public static final int MAX_PAGES = 16;
|
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
PAGE,
|
PAGE,
|
||||||
PAGES,
|
PAGES,
|
||||||
@ -44,9 +34,9 @@ public class PrintoutItem extends Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag options) {
|
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||||
var title = getTitle(stack);
|
var title = getTitle(stack);
|
||||||
if (title != null && !title.isEmpty()) list.add(Component.literal(title));
|
if (!title.isEmpty()) list.add(Component.literal(title));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -58,70 +48,17 @@ public class PrintoutItem extends Item {
|
|||||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), player.getItemInHand(hand));
|
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), player.getItemInHand(hand));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemStack createFromTitleAndText(@Nullable String title, @Nullable String[] text, @Nullable String[] colours) {
|
|
||||||
var stack = new ItemStack(this);
|
|
||||||
|
|
||||||
// Build NBT
|
|
||||||
if (title != null) stack.getOrCreateTag().putString(NBT_TITLE, title);
|
|
||||||
if (text != null) {
|
|
||||||
var tag = stack.getOrCreateTag();
|
|
||||||
tag.putInt(NBT_PAGES, text.length / LINES_PER_PAGE);
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
if (text[i] != null) tag.putString(NBT_LINE_TEXT + i, text[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (colours != null) {
|
|
||||||
var tag = stack.getOrCreateTag();
|
|
||||||
for (var i = 0; i < colours.length; i++) {
|
|
||||||
if (colours[i] != null) tag.putString(NBT_LINE_COLOUR + i, colours[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ItemStack createSingleFromTitleAndText(@Nullable String title, @Nullable String[] text, @Nullable String[] colours) {
|
|
||||||
return ModRegistry.Items.PRINTED_PAGE.get().createFromTitleAndText(title, text, colours);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ItemStack createMultipleFromTitleAndText(@Nullable String title, @Nullable String[] text, @Nullable String[] colours) {
|
|
||||||
return ModRegistry.Items.PRINTED_PAGES.get().createFromTitleAndText(title, text, colours);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ItemStack createBookFromTitleAndText(@Nullable String title, @Nullable String[] text, @Nullable String[] colours) {
|
|
||||||
return ModRegistry.Items.PRINTED_BOOK.get().createFromTitleAndText(title, text, colours);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getTitle(ItemStack stack) {
|
public static String getTitle(ItemStack stack) {
|
||||||
var nbt = stack.getTag();
|
var nbt = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||||
return nbt != null && nbt.contains(NBT_TITLE) ? nbt.getString(NBT_TITLE) : "";
|
return nbt == null ? "" : nbt.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getPageCount(ItemStack stack) {
|
public static int getPageCount(ItemStack stack) {
|
||||||
var nbt = stack.getTag();
|
var nbt = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||||
return nbt != null && nbt.contains(NBT_PAGES) ? nbt.getInt(NBT_PAGES) : 1;
|
return nbt == null ? 1 : nbt.pages();
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] getText(ItemStack stack) {
|
|
||||||
return getLines(stack, NBT_LINE_TEXT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] getColours(ItemStack stack) {
|
|
||||||
return getLines(stack, NBT_LINE_COLOUR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] getLines(ItemStack stack, String prefix) {
|
|
||||||
var nbt = stack.getTag();
|
|
||||||
var numLines = getPageCount(stack) * LINES_PER_PAGE;
|
|
||||||
var lines = new String[numLines];
|
|
||||||
for (var i = 0; i < lines.length; i++) {
|
|
||||||
lines[i] = nbt != null ? nbt.getString(prefix + i) : "";
|
|
||||||
}
|
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.media.items;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import net.minecraft.core.component.DataComponentHolder;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores information about a {@linkplain TreasureDiskItem treasure disk's} mount.
|
||||||
|
*
|
||||||
|
* @param name The name/title of the disk.
|
||||||
|
* @param path The subpath to the resource
|
||||||
|
* @see ModRegistry.DataComponents#TREASURE_DISK
|
||||||
|
*/
|
||||||
|
public record TreasureDisk(String name, String path) {
|
||||||
|
public static final Codec<TreasureDisk> CODEC = RecordCodecBuilder.create(i -> i.group(
|
||||||
|
Codec.STRING.fieldOf("name").forGetter(TreasureDisk::name),
|
||||||
|
Codec.STRING.fieldOf("path").forGetter(TreasureDisk::path)
|
||||||
|
).apply(i, TreasureDisk::new));
|
||||||
|
|
||||||
|
public static final StreamCodec<FriendlyByteBuf, TreasureDisk> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.STRING_UTF8, TreasureDisk::name,
|
||||||
|
ByteBufCodecs.STRING_UTF8, TreasureDisk::path,
|
||||||
|
TreasureDisk::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public static String getTitle(DataComponentHolder holder) {
|
||||||
|
var nbt = holder.get(ModRegistry.DataComponents.TREASURE_DISK.get());
|
||||||
|
return nbt != null ? nbt.name() : "'missingno' by how did you get this anyway?";
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.api.filesystem.Mount;
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
import dan200.computercraft.api.media.IMedia;
|
import dan200.computercraft.api.media.IMedia;
|
||||||
import dan200.computercraft.core.filesystem.SubMount;
|
import dan200.computercraft.core.filesystem.SubMount;
|
||||||
import dan200.computercraft.core.util.Colour;
|
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@ -18,7 +17,6 @@ import net.minecraft.world.entity.player.Player;
|
|||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -26,18 +24,13 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TreasureDiskItem extends Item implements IMedia {
|
public class TreasureDiskItem extends Item implements IMedia {
|
||||||
private static final String NBT_TITLE = "Title";
|
|
||||||
private static final String NBT_COLOUR = "Colour";
|
|
||||||
private static final String NBT_SUB_PATH = "SubPath";
|
|
||||||
|
|
||||||
public TreasureDiskItem(Properties settings) {
|
public TreasureDiskItem(Properties settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag tooltipOptions) {
|
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag tooltipOptions) {
|
||||||
var label = getTitle(stack);
|
list.add(Component.literal(TreasureDisk.getTitle(stack)));
|
||||||
if (!label.isEmpty()) list.add(Component.literal(label));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ForgeOverride
|
@ForgeOverride
|
||||||
@ -47,7 +40,7 @@ public class TreasureDiskItem extends Item implements IMedia {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLabel(ItemStack stack) {
|
public String getLabel(ItemStack stack) {
|
||||||
return getTitle(stack);
|
return TreasureDisk.getTitle(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,7 +48,10 @@ public class TreasureDiskItem extends Item implements IMedia {
|
|||||||
var rootTreasure = ComputerCraftAPI.createResourceMount(level.getServer(), "computercraft", "lua/treasure");
|
var rootTreasure = ComputerCraftAPI.createResourceMount(level.getServer(), "computercraft", "lua/treasure");
|
||||||
if (rootTreasure == null) return null;
|
if (rootTreasure == null) return null;
|
||||||
|
|
||||||
var subPath = getSubPath(stack);
|
var treasureDisk = stack.get(ModRegistry.DataComponents.TREASURE_DISK.get());
|
||||||
|
if (treasureDisk == null) return null;
|
||||||
|
|
||||||
|
var subPath = treasureDisk.path();
|
||||||
try {
|
try {
|
||||||
if (rootTreasure.exists(subPath)) {
|
if (rootTreasure.exists(subPath)) {
|
||||||
return new SubMount(rootTreasure, subPath);
|
return new SubMount(rootTreasure, subPath);
|
||||||
@ -68,37 +64,4 @@ public class TreasureDiskItem extends Item implements IMedia {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemStack create(String subPath, int colourIndex) {
|
|
||||||
var result = new ItemStack(ModRegistry.Items.TREASURE_DISK.get());
|
|
||||||
var nbt = result.getOrCreateTag();
|
|
||||||
nbt.putString(NBT_SUB_PATH, subPath);
|
|
||||||
|
|
||||||
var slash = subPath.indexOf('/');
|
|
||||||
if (slash >= 0) {
|
|
||||||
var author = subPath.substring(0, slash);
|
|
||||||
var title = subPath.substring(slash + 1);
|
|
||||||
nbt.putString(NBT_TITLE, "\"" + title + "\" by " + author);
|
|
||||||
} else {
|
|
||||||
nbt.putString(NBT_TITLE, "untitled");
|
|
||||||
}
|
|
||||||
nbt.putInt(NBT_COLOUR, Colour.values()[colourIndex].getHex());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getTitle(ItemStack stack) {
|
|
||||||
var nbt = stack.getTag();
|
|
||||||
return nbt != null && nbt.contains(NBT_TITLE) ? nbt.getString(NBT_TITLE) : "'missingno' by how did you get this anyway?";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getSubPath(ItemStack stack) {
|
|
||||||
var nbt = stack.getTag();
|
|
||||||
return nbt != null && nbt.contains(NBT_SUB_PATH) ? nbt.getString(NBT_SUB_PATH) : "dan200/alongtimeago";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getColour(ItemStack stack) {
|
|
||||||
var nbt = stack.getTag();
|
|
||||||
return nbt != null && nbt.contains(NBT_COLOUR) ? nbt.getInt(NBT_COLOUR) : Colour.BLUE.getHex();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,16 @@ package dan200.computercraft.shared.media.recipes;
|
|||||||
|
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.util.ColourTracker;
|
import dan200.computercraft.shared.util.ColourTracker;
|
||||||
import dan200.computercraft.shared.util.ColourUtils;
|
import dan200.computercraft.shared.util.ColourUtils;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.world.inventory.CraftingContainer;
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||||
import net.minecraft.world.item.crafting.Ingredient;
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
@ -53,7 +55,7 @@ public class DiskRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack assemble(CraftingContainer inv, RegistryAccess registryAccess) {
|
public ItemStack assemble(CraftingContainer inv, HolderLookup.Provider registryAccess) {
|
||||||
var tracker = new ColourTracker();
|
var tracker = new ColourTracker();
|
||||||
|
|
||||||
for (var i = 0; i < inv.getContainerSize(); i++) {
|
for (var i = 0; i < inv.getContainerSize(); i++) {
|
||||||
@ -67,7 +69,7 @@ public class DiskRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DiskItem.createFromIDAndColour(-1, null, tracker.hasColour() ? tracker.getColour() : Colour.BLUE.getHex());
|
return DataComponentUtil.createStack(ModRegistry.Items.DISK.get(), DataComponents.DYED_COLOR, new DyedItemColor(tracker.hasColour() ? tracker.getColour() : Colour.BLUE.getHex(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -76,8 +78,8 @@ public class DiskRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack getResultItem(RegistryAccess registryAccess) {
|
public ItemStack getResultItem(HolderLookup.Provider registryAccess) {
|
||||||
return DiskItem.createFromIDAndColour(-1, null, Colour.BLUE.getHex());
|
return DataComponentUtil.createStack(ModRegistry.Items.DISK.get(), DataComponents.DYED_COLOR, new DyedItemColor(Colour.BLUE.getHex(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
package dan200.computercraft.shared.media.recipes;
|
package dan200.computercraft.shared.media.recipes;
|
||||||
|
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.world.inventory.CraftingContainer;
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
@ -17,6 +19,8 @@ import net.minecraft.world.item.crafting.Ingredient;
|
|||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class PrintoutRecipe extends CustomRecipe {
|
public final class PrintoutRecipe extends CustomRecipe {
|
||||||
private final Ingredient leather;
|
private final Ingredient leather;
|
||||||
private final Ingredient string;
|
private final Ingredient string;
|
||||||
@ -35,8 +39,8 @@ public final class PrintoutRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack getResultItem(RegistryAccess registryAccess) {
|
public ItemStack getResultItem(HolderLookup.Provider registryAccess) {
|
||||||
return PrintoutItem.createMultipleFromTitleAndText(null, null, null);
|
return new ItemStack(ModRegistry.Items.PRINTED_PAGES.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -45,7 +49,7 @@ public final class PrintoutRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAccess) {
|
public ItemStack assemble(CraftingContainer inventory, HolderLookup.Provider registryAccess) {
|
||||||
// See if we match the recipe, and extract the input disk ID and dye colour
|
// See if we match the recipe, and extract the input disk ID and dye colour
|
||||||
var numPages = 0;
|
var numPages = 0;
|
||||||
var numPrintouts = 0;
|
var numPrintouts = 0;
|
||||||
@ -82,43 +86,30 @@ public final class PrintoutRecipe extends CustomRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build some pages with what was passed in
|
// Build some pages with what was passed in
|
||||||
if (numPages <= PrintoutItem.MAX_PAGES && stringFound && printoutFound && numPrintouts >= (leatherFound ? 1 : 2)) {
|
if (numPages <= PrintoutData.MAX_PAGES && stringFound && printoutFound && numPrintouts >= (leatherFound ? 1 : 2)) {
|
||||||
if (printouts == null) throw new IllegalStateException("Printouts must be non-null");
|
if (printouts == null) throw new IllegalStateException("Printouts must be non-null");
|
||||||
var text = new String[numPages * PrintoutItem.LINES_PER_PAGE];
|
var lines = new PrintoutData.Line[numPages * PrintoutData.LINES_PER_PAGE];
|
||||||
var colours = new String[numPages * PrintoutItem.LINES_PER_PAGE];
|
|
||||||
var line = 0;
|
var line = 0;
|
||||||
|
|
||||||
for (var printout = 0; printout < numPrintouts; printout++) {
|
for (var printout = 0; printout < numPrintouts; printout++) {
|
||||||
var stack = printouts[printout];
|
var pageText = printouts[printout].get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||||
if (stack.getItem() instanceof PrintoutItem) {
|
if (pageText != null) {
|
||||||
// Add a printout
|
// Add a printout
|
||||||
var pageText = PrintoutItem.getText(printouts[printout]);
|
for (var pageLine : pageText.lines()) lines[line++] = pageLine;
|
||||||
var pageColours = PrintoutItem.getColours(printouts[printout]);
|
|
||||||
for (var pageLine = 0; pageLine < pageText.length; pageLine++) {
|
|
||||||
text[line] = pageText[pageLine];
|
|
||||||
colours[line] = pageColours[pageLine];
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Add a blank page
|
// Add a blank page
|
||||||
for (var pageLine = 0; pageLine < PrintoutItem.LINES_PER_PAGE; pageLine++) {
|
for (var pageLine = 0; pageLine < PrintoutData.LINES_PER_PAGE; pageLine++) {
|
||||||
text[line] = "";
|
lines[line++] = PrintoutData.Line.EMPTY;
|
||||||
colours[line] = "";
|
|
||||||
line++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String title = null;
|
var title = PrintoutItem.getTitle(printouts[0]);
|
||||||
if (printouts[0].getItem() instanceof PrintoutItem) {
|
|
||||||
title = PrintoutItem.getTitle(printouts[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leatherFound) {
|
return DataComponentUtil.createStack(
|
||||||
return PrintoutItem.createBookFromTitleAndText(title, text, colours);
|
leatherFound ? ModRegistry.Items.PRINTED_BOOK.get() : ModRegistry.Items.PRINTED_PAGES.get(),
|
||||||
} else {
|
ModRegistry.DataComponents.PRINTOUT.get(), new PrintoutData(title, List.of(lines))
|
||||||
return PrintoutItem.createMultipleFromTitleAndText(title, text, colours);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ItemStack.EMPTY;
|
return ItemStack.EMPTY;
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
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.
|
|
||||||
* <p>
|
|
||||||
* Much like recipe or argument serialisers, each type of {@link NetworkMessage} should have a unique type associated
|
|
||||||
* with it. This holds platform-specific information about how the packet should be sent over the network.
|
|
||||||
*
|
|
||||||
* @param <T> The type of message to send
|
|
||||||
* @see NetworkMessages
|
|
||||||
* @see NetworkMessage#type()
|
|
||||||
*/
|
|
||||||
public interface MessageType<T extends NetworkMessage<?>> {
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
@ -6,8 +6,7 @@ package dan200.computercraft.shared.network;
|
|||||||
|
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base interface for any message which will be sent to the client or server.
|
* The base interface for any message which will be sent to the client or server.
|
||||||
@ -16,23 +15,7 @@ import net.minecraft.network.FriendlyByteBuf;
|
|||||||
* @see ClientNetworkContext
|
* @see ClientNetworkContext
|
||||||
* @see ServerNetworkContext
|
* @see ServerNetworkContext
|
||||||
*/
|
*/
|
||||||
public interface NetworkMessage<T> {
|
public interface NetworkMessage<T> extends CustomPacketPayload {
|
||||||
/**
|
|
||||||
* Get the type of this message.
|
|
||||||
*
|
|
||||||
* @return The type of this message.
|
|
||||||
*/
|
|
||||||
MessageType<?> type();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write this packet to a buffer.
|
|
||||||
* <p>
|
|
||||||
* This may be called on any thread, so this should be a pure operation.
|
|
||||||
*
|
|
||||||
* @param buf The buffer to write data to.
|
|
||||||
*/
|
|
||||||
void write(FriendlyByteBuf buf);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle this {@link NetworkMessage}.
|
* Handle this {@link NetworkMessage}.
|
||||||
*
|
*
|
||||||
|
@ -8,59 +8,61 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.shared.network.client.*;
|
import dan200.computercraft.shared.network.client.*;
|
||||||
import dan200.computercraft.shared.network.server.*;
|
import dan200.computercraft.shared.network.server.*;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of all {@link MessageType}s provided by CC: Tweaked.
|
* List of all {@link CustomPacketPayload.Type}s provided by CC: Tweaked.
|
||||||
*
|
*
|
||||||
* @see PlatformHelper The platform helper is used to send packets.
|
* @see PlatformHelper The platform helper is used to send packets.
|
||||||
*/
|
*/
|
||||||
public final class NetworkMessages {
|
public final class NetworkMessages {
|
||||||
private static final Set<String> seenChannel = new HashSet<>();
|
private static final Set<String> seenChannel = new HashSet<>();
|
||||||
private static final List<MessageType<? extends NetworkMessage<ServerNetworkContext>>> serverMessages = new ArrayList<>();
|
private static final List<CustomPacketPayload.TypeAndCodec<RegistryFriendlyByteBuf, ? extends NetworkMessage<ServerNetworkContext>>> serverMessages = new ArrayList<>();
|
||||||
private static final List<MessageType<? extends NetworkMessage<ClientNetworkContext>>> clientMessages = new ArrayList<>();
|
private static final List<CustomPacketPayload.TypeAndCodec<RegistryFriendlyByteBuf, ? extends NetworkMessage<ClientNetworkContext>>> clientMessages = new ArrayList<>();
|
||||||
|
|
||||||
public static final MessageType<ComputerActionServerMessage> COMPUTER_ACTION = registerServerbound("computer_action", ComputerActionServerMessage::new);
|
public static final CustomPacketPayload.Type<ComputerActionServerMessage> COMPUTER_ACTION = registerServerbound("computer_action", ComputerActionServerMessage.STREAM_CODEC);
|
||||||
public static final MessageType<QueueEventServerMessage> QUEUE_EVENT = registerServerbound("queue_event", QueueEventServerMessage::new);
|
public static final CustomPacketPayload.Type<QueueEventServerMessage> QUEUE_EVENT = register(serverMessages, "queue_event", QueueEventServerMessage.STREAM_CODEC);
|
||||||
public static final MessageType<KeyEventServerMessage> KEY_EVENT = registerServerbound("key_event", KeyEventServerMessage::new);
|
public static final CustomPacketPayload.Type<KeyEventServerMessage> KEY_EVENT = registerServerbound("key_event", KeyEventServerMessage.STREAM_CODEC);
|
||||||
public static final MessageType<MouseEventServerMessage> MOUSE_EVENT = registerServerbound("mouse_event", MouseEventServerMessage::new);
|
public static final CustomPacketPayload.Type<MouseEventServerMessage> MOUSE_EVENT = registerServerbound("mouse_event", MouseEventServerMessage.STREAM_CODEC);
|
||||||
public static final MessageType<UploadFileMessage> UPLOAD_FILE = registerServerbound("upload_file", UploadFileMessage::new);
|
public static final CustomPacketPayload.Type<UploadFileMessage> UPLOAD_FILE = register(serverMessages, "upload_file", UploadFileMessage.STREAM_CODEC);
|
||||||
|
|
||||||
public static final MessageType<ChatTableClientMessage> CHAT_TABLE = registerClientbound("chat_table", ChatTableClientMessage::new);
|
public static final CustomPacketPayload.Type<ChatTableClientMessage> CHAT_TABLE = registerClientbound("chat_table", ChatTableClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<PocketComputerDataMessage> POCKET_COMPUTER_DATA = registerClientbound("pocket_computer_data", PocketComputerDataMessage::new);
|
public static final CustomPacketPayload.Type<PocketComputerDataMessage> POCKET_COMPUTER_DATA = registerClientbound("pocket_computer_data", PocketComputerDataMessage.STREAM_CODEC);
|
||||||
public static final MessageType<PocketComputerDeletedClientMessage> POCKET_COMPUTER_DELETED = registerClientbound("pocket_computer_deleted", PocketComputerDeletedClientMessage::new);
|
public static final CustomPacketPayload.Type<PocketComputerDeletedClientMessage> POCKET_COMPUTER_DELETED = registerClientbound("pocket_computer_deleted", PocketComputerDeletedClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<ComputerTerminalClientMessage> COMPUTER_TERMINAL = registerClientbound("computer_terminal", ComputerTerminalClientMessage::new);
|
public static final CustomPacketPayload.Type<ComputerTerminalClientMessage> COMPUTER_TERMINAL = registerClientbound("computer_terminal", ComputerTerminalClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<PlayRecordClientMessage> PLAY_RECORD = registerClientbound("play_record", PlayRecordClientMessage::new);
|
public static final CustomPacketPayload.Type<PlayRecordClientMessage> PLAY_RECORD = registerClientbound("play_record", PlayRecordClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<MonitorClientMessage> MONITOR_CLIENT = registerClientbound("monitor_client", MonitorClientMessage::new);
|
public static final CustomPacketPayload.Type<MonitorClientMessage> MONITOR_CLIENT = registerClientbound("monitor_client", MonitorClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<SpeakerAudioClientMessage> SPEAKER_AUDIO = registerClientbound("speaker_audio", SpeakerAudioClientMessage::new);
|
public static final CustomPacketPayload.Type<SpeakerAudioClientMessage> SPEAKER_AUDIO = registerClientbound("speaker_audio", SpeakerAudioClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<SpeakerMoveClientMessage> SPEAKER_MOVE = registerClientbound("speaker_move", SpeakerMoveClientMessage::new);
|
public static final CustomPacketPayload.Type<SpeakerMoveClientMessage> SPEAKER_MOVE = registerClientbound("speaker_move", SpeakerMoveClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<SpeakerPlayClientMessage> SPEAKER_PLAY = registerClientbound("speaker_play", SpeakerPlayClientMessage::new);
|
public static final CustomPacketPayload.Type<SpeakerPlayClientMessage> SPEAKER_PLAY = registerClientbound("speaker_play", SpeakerPlayClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<SpeakerStopClientMessage> SPEAKER_STOP = registerClientbound("speaker_stop", SpeakerStopClientMessage::new);
|
public static final CustomPacketPayload.Type<SpeakerStopClientMessage> SPEAKER_STOP = registerClientbound("speaker_stop", SpeakerStopClientMessage.STREAM_CODEC);
|
||||||
public static final MessageType<UploadResultMessage> UPLOAD_RESULT = registerClientbound("upload_result", UploadResultMessage::new);
|
public static final CustomPacketPayload.Type<UploadResultMessage> UPLOAD_RESULT = registerClientbound("upload_result", UploadResultMessage.STREAM_CODEC);
|
||||||
public static final MessageType<UpgradesLoadedMessage> UPGRADES_LOADED = registerClientbound("upgrades_loaded", UpgradesLoadedMessage::new);
|
public static final CustomPacketPayload.Type<UpgradesLoadedMessage> UPGRADES_LOADED = registerClientbound("upgrades_loaded", UpgradesLoadedMessage.STREAM_CODEC);
|
||||||
|
|
||||||
private NetworkMessages() {
|
private NetworkMessages() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <C, T extends NetworkMessage<C>> MessageType<T> register(
|
private static <C, T extends NetworkMessage<C>> CustomPacketPayload.Type<T> register(
|
||||||
List<MessageType<? extends NetworkMessage<C>>> messages,
|
List<CustomPacketPayload.TypeAndCodec<RegistryFriendlyByteBuf, ? extends NetworkMessage<C>>> messages,
|
||||||
String channel, FriendlyByteBuf.Reader<T> reader
|
String channel, StreamCodec<RegistryFriendlyByteBuf, T> codec
|
||||||
) {
|
) {
|
||||||
if (!seenChannel.add(channel)) throw new IllegalArgumentException("Duplicate channel " + channel);
|
if (!seenChannel.add(channel)) throw new IllegalArgumentException("Duplicate channel " + channel);
|
||||||
var type = PlatformHelper.get().createMessageType(new ResourceLocation(ComputerCraftAPI.MOD_ID, channel), reader);
|
var type = new CustomPacketPayload.Type<T>(new ResourceLocation(ComputerCraftAPI.MOD_ID, channel));
|
||||||
messages.add(type);
|
messages.add(new CustomPacketPayload.TypeAndCodec<>(type, codec));
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends NetworkMessage<ServerNetworkContext>> MessageType<T> registerServerbound(String id, FriendlyByteBuf.Reader<T> reader) {
|
private static <T extends NetworkMessage<ServerNetworkContext>> CustomPacketPayload.Type<T> registerServerbound(String id, StreamCodec<RegistryFriendlyByteBuf, T> codec) {
|
||||||
return register(serverMessages, id, reader);
|
return register(serverMessages, id, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends NetworkMessage<ClientNetworkContext>> MessageType<T> registerClientbound(String id, FriendlyByteBuf.Reader<T> reader) {
|
private static <T extends NetworkMessage<ClientNetworkContext>> CustomPacketPayload.Type<T> registerClientbound(String id, StreamCodec<RegistryFriendlyByteBuf, T> codec) {
|
||||||
return register(clientMessages, id, reader);
|
return register(clientMessages, id, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +70,7 @@ public final class NetworkMessages {
|
|||||||
*
|
*
|
||||||
* @return An unmodifiable sequence of all serverbound message types.
|
* @return An unmodifiable sequence of all serverbound message types.
|
||||||
*/
|
*/
|
||||||
public static Collection<MessageType<? extends NetworkMessage<ServerNetworkContext>>> getServerbound() {
|
public static Collection<CustomPacketPayload.TypeAndCodec<RegistryFriendlyByteBuf, ? extends NetworkMessage<ServerNetworkContext>>> getServerbound() {
|
||||||
return Collections.unmodifiableCollection(serverMessages);
|
return Collections.unmodifiableCollection(serverMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ public final class NetworkMessages {
|
|||||||
*
|
*
|
||||||
* @return An unmodifiable sequence of all clientbound message types.
|
* @return An unmodifiable sequence of all clientbound message types.
|
||||||
*/
|
*/
|
||||||
public static Collection<MessageType<? extends NetworkMessage<ClientNetworkContext>>> getClientbound() {
|
public static Collection<CustomPacketPayload.TypeAndCodec<RegistryFriendlyByteBuf, ? extends NetworkMessage<ClientNetworkContext>>> getClientbound() {
|
||||||
return Collections.unmodifiableCollection(clientMessages);
|
return Collections.unmodifiableCollection(clientMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,18 @@
|
|||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.command.text.TableBuilder;
|
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.ComponentSerialization;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
|
|
||||||
public class ChatTableClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public class ChatTableClientMessage implements NetworkMessage<ClientNetworkContext> {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, ChatTableClientMessage> STREAM_CODEC = StreamCodec.ofMember(ChatTableClientMessage::write, ChatTableClientMessage::new);
|
||||||
|
|
||||||
private static final int MAX_LEN = 16;
|
private static final int MAX_LEN = 16;
|
||||||
private final TableBuilder table;
|
private final TableBuilder table;
|
||||||
|
|
||||||
@ -21,13 +25,13 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
|
|||||||
this.table = table;
|
this.table = table;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatTableClientMessage(FriendlyByteBuf buf) {
|
private ChatTableClientMessage(RegistryFriendlyByteBuf buf) {
|
||||||
var id = buf.readUtf(MAX_LEN);
|
var id = buf.readUtf(MAX_LEN);
|
||||||
var columns = buf.readVarInt();
|
var columns = buf.readVarInt();
|
||||||
TableBuilder table;
|
TableBuilder table;
|
||||||
if (buf.readBoolean()) {
|
if (buf.readBoolean()) {
|
||||||
var headers = new Component[columns];
|
var headers = new Component[columns];
|
||||||
for (var i = 0; i < columns; i++) headers[i] = buf.readComponent();
|
for (var i = 0; i < columns; i++) headers[i] = ComponentSerialization.STREAM_CODEC.decode(buf);
|
||||||
table = new TableBuilder(id, headers);
|
table = new TableBuilder(id, headers);
|
||||||
} else {
|
} else {
|
||||||
table = new TableBuilder(id);
|
table = new TableBuilder(id);
|
||||||
@ -36,7 +40,7 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
|
|||||||
var rows = buf.readVarInt();
|
var rows = buf.readVarInt();
|
||||||
for (var i = 0; i < rows; i++) {
|
for (var i = 0; i < rows; i++) {
|
||||||
var row = new Component[columns];
|
var row = new Component[columns];
|
||||||
for (var j = 0; j < columns; j++) row[j] = buf.readComponent();
|
for (var j = 0; j < columns; j++) row[j] = ComponentSerialization.STREAM_CODEC.decode(buf);
|
||||||
table.row(row);
|
table.row(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,18 +48,17 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
|
|||||||
this.table = table;
|
this.table = table;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void write(RegistryFriendlyByteBuf buf) {
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUtf(table.getId(), MAX_LEN);
|
buf.writeUtf(table.getId(), MAX_LEN);
|
||||||
buf.writeVarInt(table.getColumns());
|
buf.writeVarInt(table.getColumns());
|
||||||
buf.writeBoolean(table.getHeaders() != null);
|
buf.writeBoolean(table.getHeaders() != null);
|
||||||
if (table.getHeaders() != null) {
|
if (table.getHeaders() != null) {
|
||||||
for (var header : table.getHeaders()) buf.writeComponent(header);
|
for (var header : table.getHeaders()) ComponentSerialization.STREAM_CODEC.encode(buf, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.writeVarInt(table.getRows().size());
|
buf.writeVarInt(table.getRows().size());
|
||||||
for (var row : table.getRows()) {
|
for (var row : table.getRows()) {
|
||||||
for (var column : row) buf.writeComponent(column);
|
for (var column : row) ComponentSerialization.STREAM_CODEC.encode(buf, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.writeVarInt(table.getAdditional());
|
buf.writeVarInt(table.getAdditional());
|
||||||
@ -67,7 +70,7 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<ChatTableClientMessage> type() {
|
public CustomPacketPayload.Type<ChatTableClientMessage> type() {
|
||||||
return NetworkMessages.CHAT_TABLE;
|
return NetworkMessages.CHAT_TABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,32 +4,33 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
public class ComputerTerminalClientMessage implements NetworkMessage<ClientNetworkContext> {
|
* Update the terminal for the currently opened {@link ComputerMenu}.
|
||||||
private final int containerId;
|
*
|
||||||
private final TerminalState terminal;
|
* @param containerId The currently opened container id.
|
||||||
|
* @param terminal The new terminal data.
|
||||||
|
*/
|
||||||
|
public record ComputerTerminalClientMessage(
|
||||||
|
int containerId, TerminalState terminal
|
||||||
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, ComputerTerminalClientMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.VAR_INT, ComputerTerminalClientMessage::containerId,
|
||||||
|
TerminalState.STREAM_CODEC, ComputerTerminalClientMessage::terminal,
|
||||||
|
ComputerTerminalClientMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
public ComputerTerminalClientMessage(AbstractContainerMenu menu, TerminalState terminal) {
|
public ComputerTerminalClientMessage(AbstractContainerMenu menu, TerminalState terminal) {
|
||||||
containerId = menu.containerId;
|
this(menu.containerId, terminal);
|
||||||
this.terminal = terminal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComputerTerminalClientMessage(FriendlyByteBuf buf) {
|
|
||||||
containerId = buf.readVarInt();
|
|
||||||
terminal = new TerminalState(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeVarInt(containerId);
|
|
||||||
terminal.write(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -38,7 +39,7 @@ public class ComputerTerminalClientMessage implements NetworkMessage<ClientNetwo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<ComputerTerminalClientMessage> type() {
|
public CustomPacketPayload.Type<ComputerTerminalClientMessage> type() {
|
||||||
return NetworkMessages.COMPUTER_TERMINAL;
|
return NetworkMessages.COMPUTER_TERMINAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,41 +5,38 @@
|
|||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class MonitorClientMessage implements NetworkMessage<ClientNetworkContext> {
|
/**
|
||||||
private final BlockPos pos;
|
* Update the terminal contents of a monitor.
|
||||||
private final @Nullable TerminalState state;
|
*
|
||||||
|
* @param pos The position of the origin monitor.
|
||||||
public MonitorClientMessage(BlockPos pos, @Nullable TerminalState state) {
|
* @param terminal The current monitor terminal.
|
||||||
this.pos = pos;
|
*/
|
||||||
this.state = state;
|
public record MonitorClientMessage(
|
||||||
}
|
BlockPos pos, Optional<TerminalState> terminal
|
||||||
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
public MonitorClientMessage(FriendlyByteBuf buf) {
|
public static final StreamCodec<RegistryFriendlyByteBuf, MonitorClientMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
pos = buf.readBlockPos();
|
BlockPos.STREAM_CODEC, MonitorClientMessage::pos,
|
||||||
state = buf.readNullable(TerminalState::new);
|
ByteBufCodecs.optional(TerminalState.STREAM_CODEC), MonitorClientMessage::terminal,
|
||||||
}
|
MonitorClientMessage::new
|
||||||
|
);
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeBlockPos(pos);
|
|
||||||
buf.writeNullable(state, (b, t) -> t.write(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
context.handleMonitorData(pos, state);
|
context.handleMonitorData(pos, terminal.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<MonitorClientMessage> type() {
|
public CustomPacketPayload.Type<MonitorClientMessage> type() {
|
||||||
return NetworkMessages.MONITOR_CLIENT;
|
return NetworkMessages.MONITOR_CLIENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,60 +4,50 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts or stops a record on the client, depending on if {@link #soundEvent} is {@code null}.
|
* Starts or stops a record on the client, depending on if {@link #soundEvent} is {@code null}.
|
||||||
* <p>
|
* <p>
|
||||||
* Used by disk drives to play record items.
|
* Used by disk drives to play record items.
|
||||||
*
|
*
|
||||||
|
* @param pos The position of the speaker, where we should play this sound.
|
||||||
|
* @param soundEvent The sound to play, or {@link Optional#empty()} if we should stop playing.
|
||||||
|
* @param name The title of the audio to play.
|
||||||
* @see DiskDriveBlockEntity
|
* @see DiskDriveBlockEntity
|
||||||
*/
|
*/
|
||||||
public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public record PlayRecordClientMessage(
|
||||||
private final BlockPos pos;
|
BlockPos pos, Optional<Holder<SoundEvent>> soundEvent, Optional<String> name
|
||||||
private final @Nullable String name;
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final @Nullable SoundEvent soundEvent;
|
public static final StreamCodec<RegistryFriendlyByteBuf, PlayRecordClientMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
BlockPos.STREAM_CODEC, PlayRecordClientMessage::pos,
|
||||||
public PlayRecordClientMessage(BlockPos pos, SoundEvent event, @Nullable String name) {
|
ByteBufCodecs.optional(SoundEvent.STREAM_CODEC), PlayRecordClientMessage::soundEvent,
|
||||||
this.pos = pos;
|
ByteBufCodecs.optional(ByteBufCodecs.STRING_UTF8), PlayRecordClientMessage::name,
|
||||||
this.name = name;
|
PlayRecordClientMessage::new
|
||||||
soundEvent = event;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
public PlayRecordClientMessage(BlockPos pos) {
|
public PlayRecordClientMessage(BlockPos pos) {
|
||||||
this.pos = pos;
|
this(pos, Optional.empty(), Optional.empty());
|
||||||
name = null;
|
|
||||||
soundEvent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayRecordClientMessage(FriendlyByteBuf buf) {
|
|
||||||
pos = buf.readBlockPos();
|
|
||||||
soundEvent = buf.readNullable(SoundEvent::readFromNetwork);
|
|
||||||
name = buf.readNullable(FriendlyByteBuf::readUtf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeBlockPos(pos);
|
|
||||||
buf.writeNullable(soundEvent, (b, e) -> e.writeToNetwork(b));
|
|
||||||
buf.writeNullable(name, FriendlyByteBuf::writeUtf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
context.handlePlayRecord(pos, soundEvent, name);
|
context.handlePlayRecord(pos, soundEvent.map(Holder::value).orElse(null), name.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<PlayRecordClientMessage> type() {
|
public CustomPacketPayload.Type<PlayRecordClientMessage> type() {
|
||||||
return NetworkMessages.PLAY_RECORD;
|
return NetworkMessages.PLAY_RECORD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,54 +5,56 @@
|
|||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
|
import dan200.computercraft.shared.network.codec.MoreStreamCodecs;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides additional data about a client computer, such as its ID and current state.
|
* Provides additional data about a client computer, such as its ID and current state.
|
||||||
|
*
|
||||||
|
* @param id The {@linkplain ServerComputer#getInstanceUUID() instance id} of the pocket computer.
|
||||||
|
* @param state Whether the computer is on, off, or blinking.
|
||||||
|
* @param lightState The colour of the light, or {@code -1} if off.
|
||||||
|
* @param terminal The computer's terminal. This may be absent, in which case the terminal will not be updated on.
|
||||||
*/
|
*/
|
||||||
public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkContext> {
|
public record PocketComputerDataMessage(
|
||||||
private final UUID clientId;
|
UUID id, ComputerState state, int lightState, Optional<TerminalState> terminal
|
||||||
private final ComputerState state;
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final int lightState;
|
public static final StreamCodec<RegistryFriendlyByteBuf, PocketComputerDataMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
private final @Nullable TerminalState terminal;
|
UUIDUtil.STREAM_CODEC, PocketComputerDataMessage::id,
|
||||||
|
MoreStreamCodecs.ofEnum(ComputerState.class), PocketComputerDataMessage::state,
|
||||||
|
ByteBufCodecs.VAR_INT, PocketComputerDataMessage::lightState,
|
||||||
|
ByteBufCodecs.optional(TerminalState.STREAM_CODEC), PocketComputerDataMessage::terminal,
|
||||||
|
PocketComputerDataMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
||||||
clientId = computer.getInstanceUUID();
|
this(
|
||||||
state = computer.getState();
|
computer.getInstanceUUID(),
|
||||||
lightState = computer.getLight();
|
computer.getState(),
|
||||||
terminal = sendTerminal ? computer.getTerminalState() : null;
|
computer.getLight(),
|
||||||
}
|
sendTerminal ? Optional.of(computer.getTerminalState()) : Optional.empty()
|
||||||
|
);
|
||||||
public PocketComputerDataMessage(FriendlyByteBuf buf) {
|
|
||||||
clientId = buf.readUUID();
|
|
||||||
state = buf.readEnum(ComputerState.class);
|
|
||||||
lightState = buf.readVarInt();
|
|
||||||
terminal = buf.readNullable(TerminalState::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUUID(clientId);
|
|
||||||
buf.writeEnum(state);
|
|
||||||
buf.writeVarInt(lightState);
|
|
||||||
buf.writeNullable(terminal, (b, t) -> t.write(b));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
context.handlePocketComputerData(clientId, state, lightState, terminal);
|
context.handlePocketComputerData(id, state, lightState, terminal.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<PocketComputerDataMessage> type() {
|
public CustomPacketPayload.Type<PocketComputerDataMessage> type() {
|
||||||
return NetworkMessages.POCKET_COMPUTER_DATA;
|
return NetworkMessages.POCKET_COMPUTER_DATA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,24 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
public class PocketComputerDeletedClientMessage implements NetworkMessage<ClientNetworkContext> {
|
* Delete any client-side pocket computer state.
|
||||||
private final UUID instanceId;
|
*
|
||||||
|
* @param instanceId The pocket computer's instance id.
|
||||||
public PocketComputerDeletedClientMessage(UUID instanceId) {
|
*/
|
||||||
this.instanceId = instanceId;
|
public record PocketComputerDeletedClientMessage(UUID instanceId) implements NetworkMessage<ClientNetworkContext> {
|
||||||
}
|
public static final StreamCodec<RegistryFriendlyByteBuf, PocketComputerDeletedClientMessage> STREAM_CODEC = UUIDUtil.STREAM_CODEC
|
||||||
|
.map(PocketComputerDeletedClientMessage::new, PocketComputerDeletedClientMessage::instanceId)
|
||||||
public PocketComputerDeletedClientMessage(FriendlyByteBuf buffer) {
|
.cast();
|
||||||
instanceId = buffer.readUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUUID(instanceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
@ -34,7 +29,7 @@ public class PocketComputerDeletedClientMessage implements NetworkMessage<Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<PocketComputerDeletedClientMessage> type() {
|
public CustomPacketPayload.Type<PocketComputerDeletedClientMessage> type() {
|
||||||
return NetworkMessages.POCKET_COMPUTER_DELETED;
|
return NetworkMessages.POCKET_COMPUTER_DELETED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,17 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -19,34 +23,28 @@ import java.util.UUID;
|
|||||||
* <p>
|
* <p>
|
||||||
* Used by speakers to play sounds.
|
* Used by speakers to play sounds.
|
||||||
*
|
*
|
||||||
|
* @param source The {@linkplain SpeakerPeripheral#getSource() id} of the speaker playing audio.
|
||||||
|
* @param pos The position of the speaker.
|
||||||
|
* @param content The audio to play.
|
||||||
|
* @param volume The volume to play the audio at.
|
||||||
* @see SpeakerBlockEntity
|
* @see SpeakerBlockEntity
|
||||||
*/
|
*/
|
||||||
public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public record SpeakerAudioClientMessage(
|
||||||
private final UUID source;
|
UUID source,
|
||||||
private final SpeakerPosition.Message pos;
|
SpeakerPosition.Message pos,
|
||||||
private final EncodedAudio content;
|
EncodedAudio content,
|
||||||
private final float volume;
|
float volume
|
||||||
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, SpeakerAudioClientMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
UUIDUtil.STREAM_CODEC, SpeakerAudioClientMessage::source,
|
||||||
|
SpeakerPosition.Message.STREAM_CODEC, SpeakerAudioClientMessage::pos,
|
||||||
|
EncodedAudio.STREAM_CODEC, SpeakerAudioClientMessage::content,
|
||||||
|
ByteBufCodecs.FLOAT, SpeakerAudioClientMessage::volume,
|
||||||
|
SpeakerAudioClientMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, EncodedAudio content) {
|
public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, EncodedAudio content) {
|
||||||
this.source = source;
|
this(source, pos.asMessage(), content, volume);
|
||||||
this.pos = pos.asMessage();
|
|
||||||
this.content = content;
|
|
||||||
this.volume = volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpeakerAudioClientMessage(FriendlyByteBuf buf) {
|
|
||||||
source = buf.readUUID();
|
|
||||||
pos = SpeakerPosition.Message.read(buf);
|
|
||||||
volume = buf.readFloat();
|
|
||||||
content = EncodedAudio.read(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUUID(source);
|
|
||||||
pos.write(buf);
|
|
||||||
buf.writeFloat(volume);
|
|
||||||
content.write(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,7 +53,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<SpeakerAudioClientMessage> type() {
|
public CustomPacketPayload.Type<SpeakerAudioClientMessage> type() {
|
||||||
return NetworkMessages.SPEAKER_AUDIO;
|
return NetworkMessages.SPEAKER_AUDIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,15 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -18,26 +21,21 @@ import java.util.UUID;
|
|||||||
* <p>
|
* <p>
|
||||||
* Used by speakers to play sounds.
|
* Used by speakers to play sounds.
|
||||||
*
|
*
|
||||||
|
* @param source The {@linkplain SpeakerPeripheral#getSource() id} of the speaker playing audio.
|
||||||
|
* @param pos The new position of the speaker.
|
||||||
* @see SpeakerBlockEntity
|
* @see SpeakerBlockEntity
|
||||||
*/
|
*/
|
||||||
public class SpeakerMoveClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public record SpeakerMoveClientMessage(
|
||||||
private final UUID source;
|
UUID source, SpeakerPosition.Message pos
|
||||||
private final SpeakerPosition.Message pos;
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, SpeakerMoveClientMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
UUIDUtil.STREAM_CODEC, SpeakerMoveClientMessage::source,
|
||||||
|
SpeakerPosition.Message.STREAM_CODEC, SpeakerMoveClientMessage::pos,
|
||||||
|
SpeakerMoveClientMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
public SpeakerMoveClientMessage(UUID source, SpeakerPosition pos) {
|
public SpeakerMoveClientMessage(UUID source, SpeakerPosition pos) {
|
||||||
this.source = source;
|
this(source, pos.asMessage());
|
||||||
this.pos = pos.asMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpeakerMoveClientMessage(FriendlyByteBuf buf) {
|
|
||||||
source = buf.readUUID();
|
|
||||||
pos = SpeakerPosition.Message.read(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUUID(source);
|
|
||||||
pos.write(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -46,7 +44,7 @@ public class SpeakerMoveClientMessage implements NetworkMessage<ClientNetworkCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<SpeakerMoveClientMessage> type() {
|
public CustomPacketPayload.Type<SpeakerMoveClientMessage> type() {
|
||||||
return NetworkMessages.SPEAKER_MOVE;
|
return NetworkMessages.SPEAKER_MOVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -19,38 +23,31 @@ import java.util.UUID;
|
|||||||
* <p>
|
* <p>
|
||||||
* Used by speakers to play sounds.
|
* Used by speakers to play sounds.
|
||||||
*
|
*
|
||||||
|
* @param source The {@linkplain SpeakerPeripheral#getSource() id} of the speaker playing audio.
|
||||||
|
* @param pos The position of the speaker.
|
||||||
|
* @param sound The sound to play.
|
||||||
|
* @param volume The volume to play the sound at.
|
||||||
|
* @param pitch The pitch to play the sound at.
|
||||||
* @see SpeakerBlockEntity
|
* @see SpeakerBlockEntity
|
||||||
*/
|
*/
|
||||||
public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public record SpeakerPlayClientMessage(
|
||||||
private final UUID source;
|
UUID source,
|
||||||
private final SpeakerPosition.Message pos;
|
SpeakerPosition.Message pos,
|
||||||
private final ResourceLocation sound;
|
ResourceLocation sound,
|
||||||
private final float volume;
|
float volume,
|
||||||
private final float pitch;
|
float pitch
|
||||||
|
) implements NetworkMessage<ClientNetworkContext> {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, SpeakerPlayClientMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
UUIDUtil.STREAM_CODEC, SpeakerPlayClientMessage::source,
|
||||||
|
SpeakerPosition.Message.STREAM_CODEC, SpeakerPlayClientMessage::pos,
|
||||||
|
ResourceLocation.STREAM_CODEC, SpeakerPlayClientMessage::sound,
|
||||||
|
ByteBufCodecs.FLOAT, SpeakerPlayClientMessage::volume,
|
||||||
|
ByteBufCodecs.FLOAT, SpeakerPlayClientMessage::pitch,
|
||||||
|
SpeakerPlayClientMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
public SpeakerPlayClientMessage(UUID source, SpeakerPosition pos, ResourceLocation sound, float volume, float pitch) {
|
public SpeakerPlayClientMessage(UUID source, SpeakerPosition pos, ResourceLocation sound, float volume, float pitch) {
|
||||||
this.source = source;
|
this(source, pos.asMessage(), sound, volume, pitch);
|
||||||
this.pos = pos.asMessage();
|
|
||||||
this.sound = sound;
|
|
||||||
this.volume = volume;
|
|
||||||
this.pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpeakerPlayClientMessage(FriendlyByteBuf buf) {
|
|
||||||
source = buf.readUUID();
|
|
||||||
pos = SpeakerPosition.Message.read(buf);
|
|
||||||
sound = buf.readResourceLocation();
|
|
||||||
volume = buf.readFloat();
|
|
||||||
pitch = buf.readFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUUID(source);
|
|
||||||
pos.write(buf);
|
|
||||||
buf.writeResourceLocation(sound);
|
|
||||||
buf.writeFloat(volume);
|
|
||||||
buf.writeFloat(pitch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -59,7 +56,7 @@ public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<SpeakerPlayClientMessage> type() {
|
public CustomPacketPayload.Type<SpeakerPlayClientMessage> type() {
|
||||||
return NetworkMessages.SPEAKER_PLAY;
|
return NetworkMessages.SPEAKER_PLAY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,14 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||||
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -17,23 +20,13 @@ import java.util.UUID;
|
|||||||
* <p>
|
* <p>
|
||||||
* Called when a speaker is broken.
|
* Called when a speaker is broken.
|
||||||
*
|
*
|
||||||
|
* @param source The {@linkplain SpeakerPeripheral#getSource() id} of the speaker playing audio.
|
||||||
* @see SpeakerBlockEntity
|
* @see SpeakerBlockEntity
|
||||||
*/
|
*/
|
||||||
public class SpeakerStopClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public record SpeakerStopClientMessage(UUID source) implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final UUID source;
|
public static final StreamCodec<RegistryFriendlyByteBuf, SpeakerStopClientMessage> STREAM_CODEC = UUIDUtil.STREAM_CODEC
|
||||||
|
.map(SpeakerStopClientMessage::new, SpeakerStopClientMessage::source)
|
||||||
public SpeakerStopClientMessage(UUID source) {
|
.cast();
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpeakerStopClientMessage(FriendlyByteBuf buf) {
|
|
||||||
source = buf.readUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeUUID(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
@ -41,7 +34,7 @@ public class SpeakerStopClientMessage implements NetworkMessage<ClientNetworkCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<SpeakerStopClientMessage> type() {
|
public CustomPacketPayload.Type<SpeakerStopClientMessage> type() {
|
||||||
return NetworkMessages.SPEAKER_STOP;
|
return NetworkMessages.SPEAKER_STOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,12 @@ import dan200.computercraft.impl.PocketUpgrades;
|
|||||||
import dan200.computercraft.impl.RegistryHelper;
|
import dan200.computercraft.impl.RegistryHelper;
|
||||||
import dan200.computercraft.impl.TurtleUpgrades;
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
import dan200.computercraft.impl.UpgradeManager;
|
import dan200.computercraft.impl.UpgradeManager;
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
@ -27,34 +28,36 @@ import java.util.Map;
|
|||||||
* Syncs turtle and pocket upgrades to the client.
|
* Syncs turtle and pocket upgrades to the client.
|
||||||
*/
|
*/
|
||||||
public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
|
public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final Map<String, UpgradeManager.UpgradeWrapper<ITurtleUpgrade>> turtleUpgrades;
|
public static final StreamCodec<RegistryFriendlyByteBuf, UpgradesLoadedMessage> STREAM_CODEC = StreamCodec.ofMember(UpgradesLoadedMessage::write, UpgradesLoadedMessage::new);
|
||||||
private final Map<String, UpgradeManager.UpgradeWrapper<IPocketUpgrade>> pocketUpgrades;
|
|
||||||
|
private final Map<ResourceLocation, UpgradeManager.UpgradeWrapper<ITurtleUpgrade>> turtleUpgrades;
|
||||||
|
private final Map<ResourceLocation, UpgradeManager.UpgradeWrapper<IPocketUpgrade>> pocketUpgrades;
|
||||||
|
|
||||||
public UpgradesLoadedMessage() {
|
public UpgradesLoadedMessage() {
|
||||||
turtleUpgrades = TurtleUpgrades.instance().getUpgradeWrappers();
|
turtleUpgrades = TurtleUpgrades.instance().getUpgradeWrappers();
|
||||||
pocketUpgrades = PocketUpgrades.instance().getUpgradeWrappers();
|
pocketUpgrades = PocketUpgrades.instance().getUpgradeWrappers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpgradesLoadedMessage(FriendlyByteBuf buf) {
|
private UpgradesLoadedMessage(RegistryFriendlyByteBuf buf) {
|
||||||
turtleUpgrades = fromBytes(buf, ITurtleUpgrade.serialiserRegistryKey());
|
turtleUpgrades = fromBytes(buf, ITurtleUpgrade.serialiserRegistryKey());
|
||||||
pocketUpgrades = fromBytes(buf, IPocketUpgrade.serialiserRegistryKey());
|
pocketUpgrades = fromBytes(buf, IPocketUpgrade.serialiserRegistryKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends UpgradeBase> Map<String, UpgradeManager.UpgradeWrapper<T>> fromBytes(
|
private <T extends UpgradeBase> Map<ResourceLocation, UpgradeManager.UpgradeWrapper<T>> fromBytes(
|
||||||
FriendlyByteBuf buf, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registryKey
|
RegistryFriendlyByteBuf buf, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registryKey
|
||||||
) {
|
) {
|
||||||
var registry = RegistryHelper.getRegistry(registryKey);
|
var registry = RegistryHelper.getRegistry(registryKey);
|
||||||
|
|
||||||
var size = buf.readVarInt();
|
var size = buf.readVarInt();
|
||||||
Map<String, UpgradeManager.UpgradeWrapper<T>> upgrades = new HashMap<>(size);
|
Map<ResourceLocation, UpgradeManager.UpgradeWrapper<T>> upgrades = new HashMap<>(size);
|
||||||
for (var i = 0; i < size; i++) {
|
for (var i = 0; i < size; i++) {
|
||||||
var id = buf.readUtf();
|
var id = buf.readResourceLocation();
|
||||||
|
|
||||||
var serialiserId = buf.readResourceLocation();
|
var serialiserId = buf.readResourceLocation();
|
||||||
var serialiser = registry.get(serialiserId);
|
var serialiser = registry.get(serialiserId);
|
||||||
if (serialiser == null) throw new IllegalStateException("Unknown serialiser " + serialiserId);
|
if (serialiser == null) throw new IllegalStateException("Unknown serialiser " + serialiserId);
|
||||||
|
|
||||||
var upgrade = serialiser.fromNetwork(new ResourceLocation(id), buf);
|
var upgrade = serialiser.fromNetwork(id, buf);
|
||||||
var modId = buf.readUtf();
|
var modId = buf.readUtf();
|
||||||
|
|
||||||
upgrades.put(id, new UpgradeManager.UpgradeWrapper<T>(id, upgrade, serialiser, modId));
|
upgrades.put(id, new UpgradeManager.UpgradeWrapper<T>(id, upgrade, serialiser, modId));
|
||||||
@ -63,20 +66,19 @@ public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetwork
|
|||||||
return upgrades;
|
return upgrades;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void write(RegistryFriendlyByteBuf buf) {
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
toBytes(buf, ITurtleUpgrade.serialiserRegistryKey(), turtleUpgrades);
|
toBytes(buf, ITurtleUpgrade.serialiserRegistryKey(), turtleUpgrades);
|
||||||
toBytes(buf, IPocketUpgrade.serialiserRegistryKey(), pocketUpgrades);
|
toBytes(buf, IPocketUpgrade.serialiserRegistryKey(), pocketUpgrades);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends UpgradeBase> void toBytes(
|
private <T extends UpgradeBase> void toBytes(
|
||||||
FriendlyByteBuf buf, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registryKey, Map<String, UpgradeManager.UpgradeWrapper<T>> upgrades
|
RegistryFriendlyByteBuf buf, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registryKey, Map<ResourceLocation, UpgradeManager.UpgradeWrapper<T>> upgrades
|
||||||
) {
|
) {
|
||||||
var registry = RegistryHelper.getRegistry(registryKey);
|
var registry = RegistryHelper.getRegistry(registryKey);
|
||||||
|
|
||||||
buf.writeVarInt(upgrades.size());
|
buf.writeVarInt(upgrades.size());
|
||||||
for (var entry : upgrades.entrySet()) {
|
for (var entry : upgrades.entrySet()) {
|
||||||
buf.writeUtf(entry.getKey());
|
buf.writeResourceLocation(entry.getKey());
|
||||||
|
|
||||||
var serialiser = entry.getValue().serialiser();
|
var serialiser = entry.getValue().serialiser();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -96,7 +98,7 @@ public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetwork
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<UpgradesLoadedMessage> type() {
|
public CustomPacketPayload.Type<UpgradesLoadedMessage> type() {
|
||||||
return NetworkMessages.UPGRADES_LOADED;
|
return NetworkMessages.UPGRADES_LOADED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,60 +4,57 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.network.client;
|
package dan200.computercraft.shared.network.client;
|
||||||
|
|
||||||
import dan200.computercraft.core.util.Nullability;
|
|
||||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||||
import dan200.computercraft.shared.network.MessageType;
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import dan200.computercraft.shared.network.codec.MoreStreamCodecs;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.ComponentSerialization;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class UploadResultMessage implements NetworkMessage<ClientNetworkContext> {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, UploadResultMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.VAR_INT, x -> x.containerId,
|
||||||
|
MoreStreamCodecs.ofEnum(UploadResult.class), x -> x.result,
|
||||||
|
ComponentSerialization.OPTIONAL_STREAM_CODEC, x -> x.errorMessage,
|
||||||
|
UploadResultMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
public class UploadResultMessage implements NetworkMessage<ClientNetworkContext> {
|
|
||||||
private final int containerId;
|
private final int containerId;
|
||||||
private final UploadResult result;
|
private final UploadResult result;
|
||||||
private final @Nullable Component errorMessage;
|
private final Optional<Component> errorMessage;
|
||||||
|
|
||||||
private UploadResultMessage(AbstractContainerMenu container, UploadResult result, @Nullable Component errorMessage) {
|
private UploadResultMessage(int containerId, UploadResult result, Optional<Component> errorMessage) {
|
||||||
containerId = container.containerId;
|
this.containerId = containerId;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UploadResultMessage queued(AbstractContainerMenu container) {
|
public static UploadResultMessage queued(AbstractContainerMenu container) {
|
||||||
return new UploadResultMessage(container, UploadResult.QUEUED, null);
|
return new UploadResultMessage(container.containerId, UploadResult.QUEUED, Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UploadResultMessage consumed(AbstractContainerMenu container) {
|
public static UploadResultMessage consumed(AbstractContainerMenu container) {
|
||||||
return new UploadResultMessage(container, UploadResult.CONSUMED, null);
|
return new UploadResultMessage(container.containerId, UploadResult.CONSUMED, Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UploadResultMessage error(AbstractContainerMenu container, Component errorMessage) {
|
public static UploadResultMessage error(AbstractContainerMenu container, Component errorMessage) {
|
||||||
return new UploadResultMessage(container, UploadResult.ERROR, errorMessage);
|
return new UploadResultMessage(container.containerId, UploadResult.ERROR, Optional.of(errorMessage));
|
||||||
}
|
|
||||||
|
|
||||||
public UploadResultMessage(FriendlyByteBuf buf) {
|
|
||||||
containerId = buf.readVarInt();
|
|
||||||
result = buf.readEnum(UploadResult.class);
|
|
||||||
errorMessage = result == UploadResult.ERROR ? buf.readComponent() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeVarInt(containerId);
|
|
||||||
buf.writeEnum(result);
|
|
||||||
if (result == UploadResult.ERROR) buf.writeComponent(Nullability.assertNonNull(errorMessage));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
context.handleUploadResult(containerId, result, errorMessage);
|
context.handleUploadResult(containerId, result, errorMessage.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType<UploadResultMessage> type() {
|
public CustomPacketPayload.Type<UploadResultMessage> type() {
|
||||||
return NetworkMessages.UPLOAD_RESULT;
|
return NetworkMessages.UPLOAD_RESULT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,107 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.network.codec;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.handler.codec.DecoderException;
|
||||||
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.VarInt;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional {@link StreamCodec}s.
|
||||||
|
*
|
||||||
|
* @see ByteBufCodecs
|
||||||
|
*/
|
||||||
|
public class MoreStreamCodecs {
|
||||||
|
public static <B extends FriendlyByteBuf, C extends Enum<C>> StreamCodec<B, C> ofEnum(Class<C> klass) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
@Override
|
||||||
|
public C decode(B buffer) {
|
||||||
|
return buffer.readEnum(klass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(B buffer, C value) {
|
||||||
|
buffer.writeEnum(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <B extends FriendlyByteBuf, C> StreamCodec<B, NonNullList<C>> nonNullList(StreamCodec<B, C> codec, C empty) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
@Override
|
||||||
|
public NonNullList<C> decode(B buffer) {
|
||||||
|
var count = buffer.readVarInt();
|
||||||
|
var result = NonNullList.withSize(count, empty);
|
||||||
|
for (var i = 0; i < result.size(); i++) result.set(i, codec.decode(buffer));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(B buffer, NonNullList<C> list) {
|
||||||
|
var count = buffer.writeVarInt(list.size());
|
||||||
|
for (var entry : list) codec.encode(buffer, entry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, Vec3> VEC3 = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.DOUBLE, Vec3::x,
|
||||||
|
ByteBufCodecs.DOUBLE, Vec3::y,
|
||||||
|
ByteBufCodecs.DOUBLE, Vec3::z,
|
||||||
|
Vec3::new
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A codec for {@link OptionalInt}. This uses the same wire format as {@link ByteBufCodecs#optional(StreamCodec)}
|
||||||
|
* and {@link ByteBufCodecs#VAR_INT}.
|
||||||
|
*/
|
||||||
|
public static final StreamCodec<ByteBuf, OptionalInt> OPTIONAL_INT = new StreamCodec<>() {
|
||||||
|
@Override
|
||||||
|
public OptionalInt decode(ByteBuf buf) {
|
||||||
|
return buf.readBoolean() ? OptionalInt.of(ByteBufCodecs.VAR_INT.decode(buf)) : OptionalInt.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf buf, OptionalInt optional) {
|
||||||
|
if (optional.isPresent()) {
|
||||||
|
buf.writeBoolean(true);
|
||||||
|
ByteBufCodecs.VAR_INT.encode(buf, optional.getAsInt());
|
||||||
|
} else {
|
||||||
|
buf.writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link ByteBufCodecs#BYTE_ARRAY}, but into an immutable {@link ByteBuffer}.
|
||||||
|
*/
|
||||||
|
public static final StreamCodec<ByteBuf, ByteBuffer> BYTE_BUFFER = new StreamCodec<>() {
|
||||||
|
@Override
|
||||||
|
public ByteBuffer decode(ByteBuf buf) {
|
||||||
|
var toRead = VarInt.read(buf);
|
||||||
|
if (toRead > buf.readableBytes()) {
|
||||||
|
throw new DecoderException("ByteArray with size " + toRead + " is bigger than allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = new byte[toRead];
|
||||||
|
buf.readBytes(bytes);
|
||||||
|
return ByteBuffer.wrap(bytes).asReadOnlyBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf buf, ByteBuffer buffer) {
|
||||||
|
VarInt.write(buf, buffer.remaining());
|
||||||
|
buf.writeBytes(buffer.duplicate());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -8,55 +8,37 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import dan200.computercraft.shared.network.codec.MoreStreamCodecs;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
public class ComputerContainerData implements ContainerData {
|
/**
|
||||||
private final ComputerFamily family;
|
* The data required to open a computer container.
|
||||||
private final TerminalState terminal;
|
*
|
||||||
private final ItemStack displayStack;
|
* @param family The computer family.
|
||||||
private final int uploadMaxSize;
|
* @param terminal The initial terminal contents.
|
||||||
|
* @param displayStack The stack associated with this menu. This may be displayed on the client.
|
||||||
|
* @param uploadMaxSize The maximum size of a file upload.
|
||||||
|
*/
|
||||||
|
public record ComputerContainerData(
|
||||||
|
ComputerFamily family, TerminalState terminal, ItemStack displayStack, int uploadMaxSize
|
||||||
|
) implements ContainerData {
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, ComputerContainerData> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
MoreStreamCodecs.ofEnum(ComputerFamily.class), ComputerContainerData::family,
|
||||||
|
TerminalState.STREAM_CODEC, ComputerContainerData::terminal,
|
||||||
|
ItemStack.OPTIONAL_STREAM_CODEC, ComputerContainerData::displayStack,
|
||||||
|
ByteBufCodecs.VAR_INT, ComputerContainerData::uploadMaxSize,
|
||||||
|
ComputerContainerData::new
|
||||||
|
);
|
||||||
|
|
||||||
public ComputerContainerData(ServerComputer computer, ItemStack displayStack) {
|
public ComputerContainerData(ServerComputer computer, ItemStack displayStack) {
|
||||||
family = computer.getFamily();
|
this(computer.getFamily(), computer.getTerminalState(), displayStack, Config.uploadMaxSize);
|
||||||
terminal = computer.getTerminalState();
|
|
||||||
this.displayStack = displayStack;
|
|
||||||
uploadMaxSize = Config.uploadMaxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComputerContainerData(FriendlyByteBuf buf) {
|
|
||||||
family = buf.readEnum(ComputerFamily.class);
|
|
||||||
terminal = new TerminalState(buf);
|
|
||||||
displayStack = buf.readItem();
|
|
||||||
uploadMaxSize = buf.readInt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toBytes(FriendlyByteBuf buf) {
|
public void toBytes(RegistryFriendlyByteBuf buf) {
|
||||||
buf.writeEnum(family);
|
STREAM_CODEC.encode(buf, this);
|
||||||
terminal.write(buf);
|
|
||||||
buf.writeItem(displayStack);
|
|
||||||
buf.writeInt(uploadMaxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComputerFamily family() {
|
|
||||||
return family;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerminalState terminal() {
|
|
||||||
return terminal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a stack associated with this menu. This may be displayed on the client.
|
|
||||||
*
|
|
||||||
* @return The stack associated with this menu.
|
|
||||||
*/
|
|
||||||
public ItemStack displayStack() {
|
|
||||||
return displayStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int uploadMaxSize() {
|
|
||||||
return uploadMaxSize;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user