From 9d6d003f6277b3f232b07a217a48147551ee259e Mon Sep 17 00:00:00 2001 From: modmuss Date: Fri, 1 Mar 2024 17:46:49 +0000 Subject: [PATCH] 24w09a - Transfer API (#3626) * First pass on transfer API * More fixes * Another fix * Small fixes * Move transfer API tests to junit * Fix client run * Small fixes * Copy stack when component changes * Small improvement * More tests and docs fixes * Mutate existing stack --- .../api/transfer/v1/fluid/FluidStorage.java | 7 +- .../api/transfer/v1/fluid/FluidVariant.java | 42 +++--- .../v1/fluid/base/EmptyItemFluidStorage.java | 2 +- .../v1/fluid/base/FullItemFluidStorage.java | 2 +- .../v1/fluid/base/SingleFluidStorage.java | 13 +- .../api/transfer/v1/item/ItemVariant.java | 51 +++---- .../v1/item/base/SingleItemStorage.java | 13 +- .../transfer/v1/storage/TransferVariant.java | 57 ++----- .../v1/storage/base/SingleVariantStorage.java | 62 ++++++-- .../fabric/impl/transfer/VariantCodecs.java | 56 +++++++ .../transfer/fluid/EmptyBucketStorage.java | 2 +- .../impl/transfer/fluid/FluidVariantImpl.java | 77 +++------- .../transfer/fluid/WaterPotionStorage.java | 16 +- .../transfer/item/InventorySlotWrapper.java | 16 +- .../impl/transfer/item/ItemVariantImpl.java | 79 +++------- .../fabric/mixin/transfer/FluidMixin.java | 3 +- .../fabric/mixin/transfer/ItemMixin.java | 3 +- .../unittests/AbstractTransferApiTest.java} | 28 ++-- .../transfer/unittests/AttributeTests.java | 19 ++- .../transfer/unittests/BaseStorageTests.java | 19 ++- .../transfer/unittests/FluidItemTests.java | 53 +++++-- .../test/transfer/unittests/FluidTests.java | 56 +++++-- .../transfer/unittests/FluidVariantTests.java | 15 +- .../test/transfer/unittests/ItemTests.java | 67 ++++++--- .../PlayerInventoryStorageTests.java | 21 ++- .../SingleVariantItemStorageTests.java | 140 +++++++++++++++--- .../unittests/TransactionStateTests.java | 22 ++- .../unittests/UnderlyingViewTests.java | 13 +- .../transfer/{unittests => }/TestUtil.java | 2 +- .../gametests/VanillaStorageTests.java | 2 +- .../WorldDependentAttributesTest.java | 2 +- .../ingame/FluidChuteBlockEntity.java | 4 +- .../src/testmod/resources/fabric.mod.json | 3 +- settings.gradle | 2 +- 34 files changed, 585 insertions(+), 384 deletions(-) create mode 100644 fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/VariantCodecs.java rename fabric-transfer-api-v1/src/{testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnitTestsInitializer.java => test/java/net/fabricmc/fabric/test/transfer/unittests/AbstractTransferApiTest.java} (54%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java (86%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java (92%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java (85%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java (78%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java (83%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java (82%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java (88%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java (55%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java (90%) rename fabric-transfer-api-v1/src/{testmod => test}/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java (87%) rename fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/{unittests => }/TestUtil.java (94%) diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorage.java index 1cc89bdaa1..701113d978 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorage.java @@ -18,13 +18,14 @@ import org.jetbrains.annotations.Nullable; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.PotionContentsComponent; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; import net.minecraft.item.BucketItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.potion.PotionUtil; import net.minecraft.potion.Potions; import net.minecraft.util.Identifier; import net.minecraft.util.math.Direction; @@ -161,8 +162,8 @@ private FluidStorage() { combinedItemApiProvider(Items.GLASS_BOTTLE).register(context -> { return new EmptyItemFluidStorage(context, emptyBottle -> { ItemStack newStack = emptyBottle.toStack(); - PotionUtil.setPotion(newStack, Potions.WATER); - return ItemVariant.of(Items.POTION, newStack.getNbt()); + newStack.set(DataComponentTypes.POTION_CONTENTS, new PotionContentsComponent(Potions.WATER)); + return ItemVariant.of(Items.POTION, newStack.getComponentChanges()); }, Fluids.WATER, FluidConstants.BOTTLE); }); // Register water potion storage diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariant.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariant.java index c13316aea5..9eaba31540 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariant.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariant.java @@ -16,29 +16,35 @@ package net.fabricmc.fabric.api.transfer.v1.fluid; +import com.mojang.serialization.Codec; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; +import net.minecraft.component.ComponentChanges; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.registry.entry.RegistryEntry; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; +import net.fabricmc.fabric.impl.transfer.VariantCodecs; import net.fabricmc.fabric.impl.transfer.fluid.FluidVariantImpl; /** - * An immutable association of a still fluid and an optional NBT tag. + * An immutable association of a still fluid and data components. * - *

Do not extend this class. Use {@link #of(Fluid)} and {@link #of(Fluid, NbtCompound)} to create instances. + *

Do not extend this class. Use {@link #of(Fluid)} and {@link #of(Fluid, ComponentChanges)} to create instances. * *

{@link net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering} can be used for client-side rendering of fluid variants. * *

Fluid variants must always be compared with {@code equals}, never by reference! - * {@code hashCode} is guaranteed to be correct and constant time independently of the size of the NBT. + * {@code hashCode} is guaranteed to be correct and constant time independently of the size of the components. */ @ApiStatus.NonExtendable public interface FluidVariant extends TransferVariant { + Codec CODEC = VariantCodecs.FLUID_CODEC; + PacketCodec PACKET_CODEC = VariantCodecs.FLUID_PACKET_CODEC; + /** * Retrieve a blank FluidVariant. */ @@ -54,7 +60,7 @@ static FluidVariant blank() { * {@code FluidVariant.of(Fluids.FLOWING_WATER).getFluid() == Fluids.WATER}. */ static FluidVariant of(Fluid fluid) { - return of(fluid, null); + return of(fluid, ComponentChanges.EMPTY); } /** @@ -62,10 +68,10 @@ static FluidVariant of(Fluid fluid) { * *

The flowing and still variations of {@linkplain net.minecraft.fluid.FlowableFluid flowable fluids} * are normalized to always refer to the still fluid. For example, - * {@code FluidVariant.of(Fluids.FLOWING_WATER, nbt).getFluid() == Fluids.WATER}. + * {@code FluidVariant.of(Fluids.FLOWING_WATER, ComponentChanges.EMPTY).getFluid() == Fluids.WATER}. */ - static FluidVariant of(Fluid fluid, @Nullable NbtCompound nbt) { - return FluidVariantImpl.of(fluid, nbt); + static FluidVariant of(Fluid fluid, ComponentChanges components) { + return FluidVariantImpl.of(fluid, components); } /** @@ -75,19 +81,7 @@ default Fluid getFluid() { return getObject(); } - /** - * Deserialize a variant from an NBT compound tag, assuming it was serialized using {@link #toNbt}. - * - *

If an error occurs during deserialization, it will be logged with the DEBUG level, and a blank variant will be returned. - */ - static FluidVariant fromNbt(NbtCompound nbt) { - return FluidVariantImpl.fromNbt(nbt); - } - - /** - * Read a variant from a packet byte buffer, assuming it was serialized using {@link #toPacket}. - */ - static FluidVariant fromPacket(PacketByteBuf buf) { - return FluidVariantImpl.fromPacket(buf); + default RegistryEntry getRegistryEntry() { + return getFluid().getRegistryEntry(); } } diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/EmptyItemFluidStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/EmptyItemFluidStorage.java index cb50e45a10..741791aeae 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/EmptyItemFluidStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/EmptyItemFluidStorage.java @@ -71,7 +71,7 @@ public final class EmptyItemFluidStorage implements InsertionOnlyStorage ItemVariant.of(fullItem, emptyVariant.getNbt()), insertableFluid, insertableAmount); + this(context, emptyVariant -> ItemVariant.of(fullItem, emptyVariant.getComponents()), insertableFluid, insertableAmount); } /** diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/FullItemFluidStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/FullItemFluidStorage.java index c97cecdc05..4ec9db661a 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/FullItemFluidStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/FullItemFluidStorage.java @@ -52,7 +52,7 @@ public final class FullItemFluidStorage implements ExtractionOnlyStorage ItemVariant.of(emptyItem, fullVariant.getNbt()), containedFluid, containedAmount); + this(context, fullVariant -> ItemVariant.of(emptyItem, fullVariant.getComponents()), containedFluid, containedAmount); } /** diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/SingleFluidStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/SingleFluidStorage.java index 1bd9f8f2bf..bbdd975613 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/SingleFluidStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/SingleFluidStorage.java @@ -19,6 +19,7 @@ import java.util.Objects; import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.RegistryWrapper; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions; @@ -66,8 +67,14 @@ protected final FluidVariant getBlankVariant() { * Simple implementation of reading from NBT, to match what is written by {@link #writeNbt}. * Other formats are allowed, this is just a suggestion. */ - public void readNbt(NbtCompound nbt) { - variant = FluidVariant.fromNbt(nbt.getCompound("variant")); - amount = nbt.getLong("amount"); + public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + SingleVariantStorage.readNbt(this, FluidVariant.CODEC, FluidVariant::blank, nbt, wrapperLookup); + } + + /** + * Simple implementation of writing to NBT. Other formats are allowed, this is just a convenient suggestion. + */ + public void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + SingleVariantStorage.writeNbt(this, FluidVariant.CODEC, nbt, wrapperLookup); } } diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/ItemVariant.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/ItemVariant.java index 416c42a505..72ea952fae 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/ItemVariant.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/ItemVariant.java @@ -16,26 +16,34 @@ package net.fabricmc.fabric.api.transfer.v1.item; +import java.util.Objects; + +import com.mojang.serialization.Codec; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; +import net.minecraft.component.ComponentChanges; import net.minecraft.item.Item; import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.registry.entry.RegistryEntry; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; +import net.fabricmc.fabric.impl.transfer.VariantCodecs; import net.fabricmc.fabric.impl.transfer.item.ItemVariantImpl; /** - * An immutable count-less ItemStack, i.e. an immutable association of an item and an optional NBT compound tag. + * An immutable count-less ItemStack, i.e. an immutable association of an item and its data components. * *

Do not implement, use the static {@code of(...)} functions instead. */ @ApiStatus.NonExtendable public interface ItemVariant extends TransferVariant { + Codec CODEC = VariantCodecs.ITEM_CODEC; + PacketCodec PACKET_CODEC = VariantCodecs.ITEM_PACKET_CODEC; + /** * Retrieve a blank ItemVariant. */ @@ -47,28 +55,28 @@ static ItemVariant blank() { * Retrieve an ItemVariant with the item and tag of a stack. */ static ItemVariant of(ItemStack stack) { - return of(stack.getItem(), stack.getNbt()); + return of(stack.getItem(), stack.getComponentChanges()); } /** * Retrieve an ItemVariant with an item and without a tag. */ static ItemVariant of(ItemConvertible item) { - return of(item, null); + return of(item, ComponentChanges.EMPTY); } /** * Retrieve an ItemVariant with an item and an optional tag. */ - static ItemVariant of(ItemConvertible item, @Nullable NbtCompound tag) { - return ItemVariantImpl.of(item.asItem(), tag); + static ItemVariant of(ItemConvertible item, ComponentChanges components) { + return ItemVariantImpl.of(item.asItem(), components); } /** * Return true if the item and tag of this variant match those of the passed stack, and false otherwise. */ default boolean matches(ItemStack stack) { - return isOf(stack.getItem()) && nbtMatches(stack.getNbt()); + return isOf(stack.getItem()) && Objects.equals(stack.getComponentChanges(), getComponents()); } /** @@ -78,6 +86,10 @@ default Item getItem() { return getObject(); } + default RegistryEntry getRegistryEntry() { + return getItem().getRegistryEntry(); + } + /** * Create a new item stack with count 1 from this variant. */ @@ -92,25 +104,6 @@ default ItemStack toStack() { */ default ItemStack toStack(int count) { if (isBlank()) return ItemStack.EMPTY; - ItemStack stack = new ItemStack(getItem(), count); - stack.setNbt(copyNbt()); - return stack; - } - - /** - * Deserialize a variant from an NBT compound tag, assuming it was serialized using - * {@link #toNbt}. If an error occurs during deserialization, it will be logged - * with the DEBUG level, and a blank variant will be returned. - */ - static ItemVariant fromNbt(NbtCompound nbt) { - return ItemVariantImpl.fromNbt(nbt); - } - - /** - * Write a variant from a packet byte buffer, assuming it was serialized using - * {@link #toPacket}. - */ - static ItemVariant fromPacket(PacketByteBuf buf) { - return ItemVariantImpl.fromPacket(buf); + return new ItemStack(getRegistryEntry(), count, getComponents()); } } diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/base/SingleItemStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/base/SingleItemStorage.java index a6d83b903c..221a645cbb 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/base/SingleItemStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/base/SingleItemStorage.java @@ -17,6 +17,7 @@ package net.fabricmc.fabric.api.transfer.v1.item.base; import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.RegistryWrapper; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; @@ -40,8 +41,14 @@ protected final ItemVariant getBlankVariant() { * Simple implementation of reading from NBT, to match what is written by {@link #writeNbt}. * Other formats are allowed, this is just a suggestion. */ - public void readNbt(NbtCompound nbt) { - variant = ItemVariant.fromNbt(nbt.getCompound("variant")); - amount = nbt.getLong("amount"); + public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + SingleVariantStorage.readNbt(this, ItemVariant.CODEC, ItemVariant::blank, nbt, wrapperLookup); + } + + /** + * Simple implementation of writing to NBT. Other formats are allowed, this is just a convenient suggestion. + */ + public void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + SingleVariantStorage.writeNbt(this, ItemVariant.CODEC, nbt, wrapperLookup); } } diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/TransferVariant.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/TransferVariant.java index 508ffd5b7f..a5f1c32814 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/TransferVariant.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/TransferVariant.java @@ -18,13 +18,10 @@ import java.util.Objects; -import org.jetbrains.annotations.Nullable; - -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.component.ComponentChanges; /** - * An immutable association of an immutable object instance (for example {@code Item} or {@code Fluid}) and an optional NBT tag. + * An immutable association of an immutable object instance (for example {@code Item} or {@code Fluid}) and data components. * *

This is exposed for convenience for code that needs to be generic across multiple transfer variants, * but note that a {@link Storage} is not necessarily bound to {@code TransferVariant}. Its generic parameter can be any immutable object. @@ -46,18 +43,15 @@ public interface TransferVariant { O getObject(); /** - * Return the underlying tag. - * - *

NEVER MUTATE THIS NBT TAG, if you need to mutate it you can use {@link #copyNbt()} to retrieve a copy instead. + * @return The {@link ComponentChanges} of this variant. */ - @Nullable - NbtCompound getNbt(); + ComponentChanges getComponents(); /** - * Return true if this variant has a tag, false otherwise. + * Return true if this variant has a component changes. */ - default boolean hasNbt() { - return getNbt() != null; + default boolean hasComponents() { + return !getComponents().isEmpty(); } /** @@ -65,8 +59,8 @@ default boolean hasNbt() { * *

Note: True is returned if both tags are {@code null}. */ - default boolean nbtMatches(@Nullable NbtCompound other) { - return Objects.equals(getNbt(), other); + default boolean componentsMatches(ComponentChanges other) { + return Objects.equals(getComponents(), other); } /** @@ -75,37 +69,4 @@ default boolean nbtMatches(@Nullable NbtCompound other) { default boolean isOf(O object) { return getObject() == object; } - - /** - * Return a copy of the tag of this variant, or {@code null} if this variant doesn't have a tag. - * - *

Note: Use {@link #nbtMatches} if you only need to check for custom tag equality, or {@link #getNbt()} if you don't need to mutate the tag. - */ - @Nullable - default NbtCompound copyNbt() { - NbtCompound nbt = getNbt(); - return nbt == null ? null : nbt.copy(); - } - - /** - * Return a copy of the tag of this variant, or a new compound if this variant doesn't have a tag. - */ - default NbtCompound copyOrCreateNbt() { - NbtCompound nbt = getNbt(); - return nbt == null ? new NbtCompound() : nbt.copy(); - } - - /** - * Save this variant into an NBT compound tag. Subinterfaces should have a matching static {@code fromNbt}. - * - *

Note: This is safe to use for persisting data as objects are saved using their full Identifier. - */ - NbtCompound toNbt(); - - /** - * Write this variant into a packet byte buffer. Subinterfaces should have a matching static {@code fromPacket}. - * - *

Implementation note: Objects are saved using their raw registry integer id. - */ - void toPacket(PacketByteBuf buf); } diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/SingleVariantStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/SingleVariantStorage.java index 17e9996e3f..706e0effbb 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/SingleVariantStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/SingleVariantStorage.java @@ -16,7 +16,19 @@ package net.fabricmc.fabric.api.transfer.v1.storage.base; +import java.util.function.Supplier; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtOps; +import net.minecraft.registry.RegistryOps; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.Util; import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; @@ -36,6 +48,8 @@ * @see net.fabricmc.fabric.api.transfer.v1.item.base.SingleItemStorage SingleItemStorage for item variants. */ public abstract class SingleVariantStorage> extends SnapshotParticipant> implements SingleSlotStorage { + private static final Logger LOGGER = LoggerFactory.getLogger("fabric-transfer-api-v1/variant-storage"); + public T variant = getBlankVariant(); public long amount = 0; @@ -67,15 +81,6 @@ protected boolean canExtract(T variant) { return true; } - /** - * Simple implementation of writing to NBT. Other formats are allowed, this is just a convenient suggestion. - */ - // Reading from NBT is not provided because it would need to call the static FluidVariant/ItemVariant.fromNbt - public void writeNbt(NbtCompound nbt) { - nbt.put("variant", variant.toNbt()); - nbt.putLong("amount", amount); - } - @Override public long insert(T insertedVariant, long maxAmount, TransactionContext transaction) { StoragePreconditions.notBlankNotNegative(insertedVariant, maxAmount); @@ -157,4 +162,43 @@ protected void readSnapshot(ResourceAmount snapshot) { public String toString() { return "SingleVariantStorage[%d %s]".formatted(amount, variant); } + + /** + * Read a {@link SingleVariantStorage} from NBT. + * + * @param storage the {@link SingleVariantStorage} to read into + * @param codec the item variant codec + * @param fallback the fallback item variant, used when the NBT is invalid + * @param nbt the NBT to read from + * @param wrapperLookup the {@link RegistryWrapper.WrapperLookup} instance + * @param the type of the item variant + */ + public static > void readNbt(SingleVariantStorage storage, Codec codec, Supplier fallback, NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + final RegistryOps ops = wrapperLookup.getOps(NbtOps.INSTANCE); + final DataResult result = codec.parse(ops, nbt.getCompound("variant")); + + if (result.error().isPresent()) { + LOGGER.debug("Failed to load an ItemVariant from NBT: {}", result.error().get()); + storage.variant = fallback.get(); + } else { + storage.variant = result.result().get(); + } + + storage.amount = nbt.getLong("amount"); + } + + /** + * Write a {@link SingleVariantStorage} to NBT. + * + * @param storage the {@link SingleVariantStorage} to write from + * @param codec the item variant codec + * @param nbt the NBT to write to + * @param wrapperLookup the {@link RegistryWrapper.WrapperLookup} instance + * @param the type of the item variant + */ + public static > void writeNbt(SingleVariantStorage storage, Codec codec, NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + final RegistryOps ops = wrapperLookup.getOps(NbtOps.INSTANCE); + nbt.put("variant", Util.getResult(codec.encodeStart(ops, storage.variant), RuntimeException::new)); + nbt.putLong("amount", storage.amount); + } } diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/VariantCodecs.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/VariantCodecs.java new file mode 100644 index 0000000000..1fc49cded4 --- /dev/null +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/VariantCodecs.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.transfer; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.component.ComponentChanges; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKeys; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; +import net.fabricmc.fabric.impl.transfer.fluid.FluidVariantImpl; +import net.fabricmc.fabric.impl.transfer.item.ItemVariantImpl; + +public class VariantCodecs { + public static final Codec ITEM_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Registries.ITEM.getEntryCodec().fieldOf("item").forGetter(ItemVariant::getRegistryEntry), + ComponentChanges.CODEC.fieldOf("components").forGetter(ItemVariant::getComponents) + ).apply(instance, ItemVariantImpl::of) + ); + public static final PacketCodec ITEM_PACKET_CODEC = PacketCodec.tuple( + PacketCodecs.registryEntry(RegistryKeys.ITEM), ItemVariant::getRegistryEntry, + ComponentChanges.PACKET_CODEC, ItemVariant::getComponents, + ItemVariantImpl::of + ); + + public static final Codec FLUID_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Registries.FLUID.getEntryCodec().fieldOf("fluid").forGetter(FluidVariant::getRegistryEntry), + ComponentChanges.CODEC.fieldOf("components").forGetter(FluidVariant::getComponents) + ).apply(instance, FluidVariantImpl::of) + ); + public static final PacketCodec FLUID_PACKET_CODEC = PacketCodec.tuple( + PacketCodecs.registryEntry(RegistryKeys.FLUID), FluidVariant::getRegistryEntry, + ComponentChanges.PACKET_CODEC, FluidVariant::getComponents, + FluidVariantImpl::of + ); +} diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/EmptyBucketStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/EmptyBucketStorage.java index cadb2172b8..eb0df4d8f7 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/EmptyBucketStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/EmptyBucketStorage.java @@ -55,7 +55,7 @@ public long insert(FluidVariant resource, long maxAmount, TransactionContext tra // Make sure the resource is a correct fluid mapping: the fluid <-> bucket mapping must be bidirectional. if (fullBucket instanceof BucketItemAccessor accessor && resource.isOf(accessor.fabric_getFluid())) { if (maxAmount >= FluidConstants.BUCKET) { - ItemVariant newVariant = ItemVariant.of(fullBucket, context.getItemVariant().getNbt()); + ItemVariant newVariant = ItemVariant.of(fullBucket, context.getItemVariant().getComponents()); if (context.exchange(newVariant, 1, transaction) == 1) { return FluidConstants.BUCKET; diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/FluidVariantImpl.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/FluidVariantImpl.java index 9f2653d340..1366fa5f6b 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/FluidVariantImpl.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/FluidVariantImpl.java @@ -19,22 +19,21 @@ import java.util.Objects; import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import net.minecraft.component.ComponentChanges; import net.minecraft.fluid.FlowableFluid; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.PacketByteBuf; import net.minecraft.registry.Registries; +import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; public class FluidVariantImpl implements FluidVariant { - public static FluidVariant of(Fluid fluid, @Nullable NbtCompound nbt) { + public static FluidVariant of(Fluid fluid, ComponentChanges components) { Objects.requireNonNull(fluid, "Fluid may not be null."); + Objects.requireNonNull(components, "Components may not be null."); if (!fluid.isStill(fluid.getDefaultState()) && fluid != Fluids.EMPTY) { // Note: the empty fluid is not still, that's why we check for it specifically. @@ -49,25 +48,27 @@ public static FluidVariant of(Fluid fluid, @Nullable NbtCompound nbt) { } } - if (nbt == null || fluid == Fluids.EMPTY) { + if (components.isEmpty() || fluid == Fluids.EMPTY) { // Use the cached variant inside the fluid return ((FluidVariantCache) fluid).fabric_getCachedFluidVariant(); } else { // TODO explore caching fluid variants for non null tags. - return new FluidVariantImpl(fluid, nbt); + return new FluidVariantImpl(fluid, components); } } - private static final Logger LOGGER = LoggerFactory.getLogger("fabric-transfer-api-v1/fluid"); + public static FluidVariant of(RegistryEntry fluid, ComponentChanges components) { + return of(fluid.value(), components); + } private final Fluid fluid; - private final @Nullable NbtCompound nbt; + private final ComponentChanges components; private final int hashCode; - public FluidVariantImpl(Fluid fluid, NbtCompound nbt) { + public FluidVariantImpl(Fluid fluid, ComponentChanges components) { this.fluid = fluid; - this.nbt = nbt == null ? null : nbt.copy(); // defensive copy - this.hashCode = Objects.hash(fluid, nbt); + this.components = components; + this.hashCode = Objects.hash(fluid, components); } @Override @@ -81,57 +82,13 @@ public Fluid getObject() { } @Override - public @Nullable NbtCompound getNbt() { - return nbt; - } - - @Override - public NbtCompound toNbt() { - NbtCompound result = new NbtCompound(); - result.putString("fluid", Registries.FLUID.getId(fluid).toString()); - - if (nbt != null) { - result.put("tag", nbt.copy()); - } - - return result; - } - - public static FluidVariant fromNbt(NbtCompound compound) { - try { - Fluid fluid = Registries.FLUID.get(new Identifier(compound.getString("fluid"))); - NbtCompound nbt = compound.contains("tag") ? compound.getCompound("tag") : null; - return of(fluid, nbt); - } catch (RuntimeException runtimeException) { - LOGGER.debug("Tried to load an invalid FluidVariant from NBT: {}", compound, runtimeException); - return FluidVariant.blank(); - } - } - - @Override - public void toPacket(PacketByteBuf buf) { - if (isBlank()) { - buf.writeBoolean(false); - } else { - buf.writeBoolean(true); - buf.writeVarInt(Registries.FLUID.getRawId(fluid)); - buf.writeNbt(nbt); - } - } - - public static FluidVariant fromPacket(PacketByteBuf buf) { - if (!buf.readBoolean()) { - return FluidVariant.blank(); - } else { - Fluid fluid = Registries.FLUID.get(buf.readVarInt()); - NbtCompound nbt = buf.readNbt(); - return of(fluid, nbt); - } + public @Nullable ComponentChanges getComponents() { + return components; } @Override public String toString() { - return "FluidVariant{fluid=" + fluid + ", tag=" + nbt + '}'; + return "FluidVariant{fluid=" + fluid + ", components=" + components + '}'; } @Override @@ -142,7 +99,7 @@ public boolean equals(Object o) { FluidVariantImpl fluidVariant = (FluidVariantImpl) o; // fail fast with hash code - return hashCode == fluidVariant.hashCode && fluid == fluidVariant.fluid && nbtMatches(fluidVariant.nbt); + return hashCode == fluidVariant.hashCode && fluid == fluidVariant.fluid && componentsMatches(fluidVariant.components); } @Override diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/WaterPotionStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/WaterPotionStorage.java index beb505867b..922abf68e4 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/WaterPotionStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/fluid/WaterPotionStorage.java @@ -16,13 +16,18 @@ package net.fabricmc.fabric.impl.transfer.fluid; +import java.util.Optional; + import org.jetbrains.annotations.Nullable; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.PotionContentsComponent; import net.minecraft.fluid.Fluids; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.potion.PotionUtil; +import net.minecraft.potion.Potion; import net.minecraft.potion.Potions; +import net.minecraft.registry.entry.RegistryEntry; import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; @@ -47,8 +52,9 @@ public static WaterPotionStorage find(ContainerItemContext context) { private static boolean isWaterPotion(ContainerItemContext context) { ItemVariant variant = context.getItemVariant(); - - return variant.isOf(Items.POTION) && PotionUtil.getPotion(variant.getNbt()) == Potions.WATER; + Optional potionContents = variant.getComponents().get(DataComponentTypes.POTION_CONTENTS); + RegistryEntry potion = potionContents.map(PotionContentsComponent::potion).orElse(null).orElse(null); + return variant.isOf(Items.POTION) && potion == Potions.WATER; } private final ContainerItemContext context; @@ -63,8 +69,8 @@ private boolean isWaterPotion() { private ItemVariant mapToGlassBottle() { ItemStack newStack = context.getItemVariant().toStack(); - PotionUtil.setPotion(newStack, Potions.EMPTY); - return ItemVariant.of(Items.GLASS_BOTTLE, newStack.getNbt()); + newStack.set(DataComponentTypes.POTION_CONTENTS, PotionContentsComponent.DEFAULT); + return ItemVariant.of(Items.GLASS_BOTTLE, newStack.getComponentChanges()); } @Override diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/InventorySlotWrapper.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/InventorySlotWrapper.java index 2c8dd2f0e5..2c46b901c5 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/InventorySlotWrapper.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/InventorySlotWrapper.java @@ -16,12 +16,15 @@ package net.fabricmc.fabric.impl.transfer.item; +import java.util.Objects; + import net.minecraft.block.ChestBlock; import net.minecraft.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.block.entity.BrewingStandBlockEntity; import net.minecraft.block.entity.ChestBlockEntity; import net.minecraft.block.entity.ShulkerBoxBlockEntity; import net.minecraft.block.enums.ChestType; +import net.minecraft.component.DataComponentType; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.util.math.BlockPos; @@ -153,9 +156,18 @@ protected void onFinalCommit() { } if (!original.isEmpty() && original.getItem() == currentStack.getItem()) { - // None is empty and the items match: just update the amount and NBT, and reuse the original stack. + // Components have changed, we need to copy the stack. + if (!Objects.equals(original.getComponentChanges(), currentStack.getComponentChanges())) { + // Remove all the existing components and copy the new ones on top. + for (DataComponentType type : original.getComponents().getTypes()) { + original.set(type, null); + } + + original.copyComponentsFrom(currentStack.getComponents()); + } + + // None is empty and the items and components match: just update the amount, and reuse the original stack. original.setCount(currentStack.getCount()); - original.setNbt(currentStack.hasNbt() ? currentStack.getNbt().copy() : null); setStack(original); } else { // Otherwise assume everything was taken from original so empty it. diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ItemVariantImpl.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ItemVariantImpl.java index 8209d994c0..e6ae72e0fd 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ItemVariantImpl.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ItemVariantImpl.java @@ -19,45 +19,44 @@ import java.util.Objects; import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import net.minecraft.component.ComponentChanges; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.registry.Registries; -import net.minecraft.util.Identifier; +import net.minecraft.registry.entry.RegistryEntry; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; public class ItemVariantImpl implements ItemVariant { - public static ItemVariant of(Item item, @Nullable NbtCompound tag) { + public static ItemVariant of(Item item, ComponentChanges components) { Objects.requireNonNull(item, "Item may not be null."); + Objects.requireNonNull(components, "Components may not be null."); // Only tag-less or empty item variants are cached for now. - if (tag == null || item == Items.AIR) { + if (components.isEmpty() || item == Items.AIR) { return ((ItemVariantCache) item).fabric_getCachedItemVariant(); } else { - return new ItemVariantImpl(item, tag); + return new ItemVariantImpl(item, components); } } - private static final Logger LOGGER = LoggerFactory.getLogger("fabric-transfer-api-v1/item"); + public static ItemVariant of(RegistryEntry item, ComponentChanges components) { + return of(item.value(), components); + } private final Item item; - private final @Nullable NbtCompound nbt; + private final ComponentChanges components; private final int hashCode; /** * Lazily computed, equivalent to calling toStack(1). MAKE SURE IT IS NEVER MODIFIED! */ private volatile @Nullable ItemStack cachedStack = null; - public ItemVariantImpl(Item item, NbtCompound nbt) { + public ItemVariantImpl(Item item, ComponentChanges components) { this.item = item; - this.nbt = nbt == null ? null : nbt.copy(); // defensive copy - hashCode = Objects.hash(item, nbt); + this.components = components; + hashCode = Objects.hash(item, components); } @Override @@ -67,8 +66,8 @@ public Item getObject() { @Nullable @Override - public NbtCompound getNbt() { - return nbt; + public ComponentChanges getComponents() { + return components; } @Override @@ -76,53 +75,9 @@ public boolean isBlank() { return item == Items.AIR; } - @Override - public NbtCompound toNbt() { - NbtCompound result = new NbtCompound(); - result.putString("item", Registries.ITEM.getId(item).toString()); - - if (nbt != null) { - result.put("tag", nbt.copy()); - } - - return result; - } - - public static ItemVariant fromNbt(NbtCompound tag) { - try { - Item item = Registries.ITEM.get(new Identifier(tag.getString("item"))); - NbtCompound aTag = tag.contains("tag") ? tag.getCompound("tag") : null; - return of(item, aTag); - } catch (RuntimeException runtimeException) { - LOGGER.debug("Tried to load an invalid ItemVariant from NBT: {}", tag, runtimeException); - return ItemVariant.blank(); - } - } - - @Override - public void toPacket(PacketByteBuf buf) { - if (isBlank()) { - buf.writeBoolean(false); - } else { - buf.writeBoolean(true); - buf.writeVarInt(Item.getRawId(item)); - buf.writeNbt(nbt); - } - } - - public static ItemVariant fromPacket(PacketByteBuf buf) { - if (!buf.readBoolean()) { - return ItemVariant.blank(); - } else { - Item item = Item.byRawId(buf.readVarInt()); - NbtCompound nbt = buf.readNbt(); - return of(item, nbt); - } - } - @Override public String toString() { - return "ItemVariant{item=" + item + ", tag=" + nbt + '}'; + return "ItemVariant{item=" + item + ", components=" + components + '}'; } @Override @@ -133,7 +88,7 @@ public boolean equals(Object o) { ItemVariantImpl ItemVariant = (ItemVariantImpl) o; // fail fast with hash code - return hashCode == ItemVariant.hashCode && item == ItemVariant.item && nbtMatches(ItemVariant.nbt); + return hashCode == ItemVariant.hashCode && item == ItemVariant.item && componentsMatches(ItemVariant.components); } @Override diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/FluidMixin.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/FluidMixin.java index 0178c5a2b5..9db06bfb50 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/FluidMixin.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/FluidMixin.java @@ -23,6 +23,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraft.component.ComponentChanges; import net.minecraft.fluid.Fluid; import net.minecraft.sound.SoundEvent; @@ -43,7 +44,7 @@ @SuppressWarnings("unused") public class FluidMixin implements FluidVariantCache { @SuppressWarnings("ConstantConditions") - private final FluidVariant fabric_cachedFluidVariant = new FluidVariantImpl((Fluid) (Object) this, null); + private final FluidVariant fabric_cachedFluidVariant = new FluidVariantImpl((Fluid) (Object) this, ComponentChanges.EMPTY); @Override public FluidVariant fabric_getCachedFluidVariant() { diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/ItemMixin.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/ItemMixin.java index 008e8eb4c8..0d63053802 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/ItemMixin.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/mixin/transfer/ItemMixin.java @@ -18,6 +18,7 @@ import org.spongepowered.asm.mixin.Mixin; +import net.minecraft.component.ComponentChanges; import net.minecraft.item.Item; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; @@ -30,7 +31,7 @@ @Mixin(Item.class) public class ItemMixin implements ItemVariantCache { @SuppressWarnings("ConstantConditions") - private final ItemVariant fabric_cachedItemVariant = new ItemVariantImpl((Item) (Object) this, null); + private final ItemVariant fabric_cachedItemVariant = new ItemVariantImpl((Item) (Object) this, ComponentChanges.EMPTY); @Override public ItemVariant fabric_getCachedItemVariant() { diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnitTestsInitializer.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/AbstractTransferApiTest.java similarity index 54% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnitTestsInitializer.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/AbstractTransferApiTest.java index b91d4b2138..e16e6b1f17 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnitTestsInitializer.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/AbstractTransferApiTest.java @@ -16,24 +16,18 @@ package net.fabricmc.fabric.test.transfer.unittests; -import org.slf4j.LoggerFactory; +import net.minecraft.Bootstrap; +import net.minecraft.SharedConstants; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.Registries; -import net.fabricmc.api.ModInitializer; - -public class UnitTestsInitializer implements ModInitializer { - @Override - public void onInitialize() { - AttributeTests.run(); - BaseStorageTests.run(); - FluidItemTests.run(); - FluidTests.run(); - FluidVariantTests.run(); - ItemTests.run(); - PlayerInventoryStorageTests.run(); - SingleVariantItemStorageTests.run(); - TransactionStateTests.run(); - UnderlyingViewTests.run(); +public abstract class AbstractTransferApiTest { + protected static void bootstrap() { + SharedConstants.createGameVersion(); + Bootstrap.initialize(); + } - LoggerFactory.getLogger("fabric-transfer-api-v1 testmod").info("Transfer API unit tests successful."); + protected static DynamicRegistryManager staticDrm() { + return DynamicRegistryManager.of(Registries.REGISTRIES); } } diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java similarity index 86% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java index 2151afa313..1857621639 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/AttributeTests.java @@ -16,7 +16,10 @@ package net.fabricmc.fabric.test.transfer.unittests; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import net.minecraft.fluid.Fluids; import net.minecraft.sound.SoundEvents; @@ -28,13 +31,14 @@ /** * Test that fluid attributes for vanilla fluids have the correct values. */ -public class AttributeTests { - public static void run() { - testWater(); - testLava(); +public class AttributeTests extends AbstractTransferApiTest { + @BeforeAll + static void beforeAll() { + bootstrap(); } - private static void testWater() { + @Test + public void testWater() { FluidVariant water = FluidVariant.of(Fluids.WATER); assertEquals(SoundEvents.ITEM_BUCKET_FILL, FluidVariantAttributes.getFillSound(water)); @@ -45,7 +49,8 @@ private static void testWater() { assertEquals(false, FluidVariantAttributes.isLighterThanAir(water)); } - private static void testLava() { + @Test + public void testLava() { FluidVariant lava = FluidVariant.of(Fluids.LAVA); assertEquals(SoundEvents.ITEM_BUCKET_FILL_LAVA, FluidVariantAttributes.getFillSound(lava)); diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java similarity index 92% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java index 1f39a034f0..0935d1e627 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/BaseStorageTests.java @@ -17,10 +17,13 @@ package net.fabricmc.fabric.test.transfer.unittests; import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; import java.util.Iterator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import net.minecraft.fluid.Fluids; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; @@ -32,13 +35,14 @@ import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage; import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; -public class BaseStorageTests { - public static void run() { - testFilteringStorage(); - testNonEmptyIteratorWithModifiedView(); +public class BaseStorageTests extends AbstractTransferApiTest { + @BeforeAll + static void beforeAll() { + bootstrap(); } - private static void testFilteringStorage() { + @Test + public void testFilteringStorage() { SingleVariantStorage storage = new SingleVariantStorage<>() { @Override protected FluidVariant getBlankVariant() { @@ -102,7 +106,8 @@ protected boolean canInsert(FluidVariant resource) { * Regression test for * {@code nonEmptyIterator} not handling views that become empty during iteration correctly. */ - private static void testNonEmptyIteratorWithModifiedView() { + @Test + public void testNonEmptyIteratorWithModifiedView() { SingleVariantStorage storage = SingleFluidStorage.withFixedCapacity(BUCKET, () -> { }); storage.variant = FluidVariant.of(Fluids.WATER); diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java similarity index 85% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java index c84482e94c..2a1d8bbe79 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidItemTests.java @@ -18,18 +18,25 @@ import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BOTTLE; import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; import java.util.List; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.PotionContentsComponent; import net.minecraft.fluid.Fluids; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.potion.PotionUtil; +import net.minecraft.potion.Potion; import net.minecraft.potion.Potions; +import net.minecraft.registry.entry.RegistryEntry; import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage; @@ -43,17 +50,14 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; -class FluidItemTests { - public static void run() { - testFluidItemApi(); - testWaterPotion(); - testSimpleContentsQuery(); - - // Ensure this doesn't throw an error due to the empty stack. - assertEquals(null, ContainerItemContext.withConstant(ItemStack.EMPTY).find(FluidStorage.ITEM)); +class FluidItemTests extends AbstractTransferApiTest { + @BeforeAll + static void beforeAll() { + bootstrap(); } - private static void testFluidItemApi() { + @Test + public void testFluidItemApi() { FluidVariant water = FluidVariant.of(Fluids.WATER); ItemVariant waterBucket = ItemVariant.of(Items.WATER_BUCKET); Inventory testInventory = new FluidItemTestInventory(ItemStack.EMPTY, new ItemStack(Items.BUCKET), new ItemStack(Items.WATER_BUCKET)); @@ -139,7 +143,8 @@ public List> getAdditionalSlots() { } } - private static void testWaterPotion() { + @Test + public void testWaterPotion() { FluidVariant water = FluidVariant.of(Fluids.WATER); Inventory testInventory = new SimpleInventory(new ItemStack(Items.GLASS_BOTTLE)); @@ -151,7 +156,7 @@ private static void testWaterPotion() { transaction.commit(); } - if (PotionUtil.getPotion(testInventory.getStack(0)) != Potions.WATER) throw new AssertionError("Expected water potion."); + if (getPotion(testInventory.getStack(0)) != Potions.WATER) throw new AssertionError("Expected water potion."); // Try to empty from water potion Storage waterBottleStorage = new InventoryContainerItem(testInventory, 0).find(FluidStorage.ITEM); @@ -162,7 +167,7 @@ private static void testWaterPotion() { } // Make sure extraction nothing is returned for other potions - PotionUtil.setPotion(testInventory.getStack(0), Potions.LUCK); + setPotion(testInventory.getStack(0), Potions.LUCK); Storage luckyStorage = new InventoryContainerItem(testInventory, 0).find(FluidStorage.ITEM); if (StorageUtil.findStoredResource(luckyStorage) != null) { @@ -170,7 +175,8 @@ private static void testWaterPotion() { } } - private static void testSimpleContentsQuery() { + @Test + public void testSimpleContentsQuery() { assertEquals( new ResourceAmount<>(FluidVariant.of(Fluids.WATER), BUCKET), StorageUtil.findExtractableContent( @@ -183,9 +189,24 @@ private static void testSimpleContentsQuery() { null, StorageUtil.findExtractableContent( ContainerItemContext.withConstant(new ItemStack(Items.WATER_BUCKET)).find(FluidStorage.ITEM), - FluidVariant::hasNbt, // Only allow NBT -> won't match anything. + FluidVariant::hasComponents, // Only allow NBT -> won't match anything. null ) ); } + + @Test + public void testDoesNotThrow() { + // Ensure this doesn't throw an error due to the empty stack. + assertEquals(null, ContainerItemContext.withConstant(ItemStack.EMPTY).find(FluidStorage.ITEM)); + } + + @Nullable + public static RegistryEntry getPotion(ItemStack stack) { + return stack.getOrDefault(DataComponentTypes.POTION_CONTENTS, PotionContentsComponent.DEFAULT).potion().orElse(null); + } + + public static void setPotion(ItemStack itemStack, RegistryEntry potion) { + itemStack.set(DataComponentTypes.POTION_CONTENTS, new PotionContentsComponent(potion)); + } } diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java similarity index 78% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java index 42f7040efe..c02fc3f319 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidTests.java @@ -18,23 +18,33 @@ import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET; +import io.netty.buffer.Unpooled; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import net.minecraft.component.ComponentChanges; +import net.minecraft.component.DataComponentType; import net.minecraft.fluid.Fluids; -import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; +import net.minecraft.util.dynamic.Codecs; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil; import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage; import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage; import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.fabricmc.fabric.test.transfer.ingame.TransferTestInitializer; -class FluidTests { - public static void run() { - testFluidStorage(); - } - - private static final FluidVariant TAGGED_WATER, TAGGED_WATER_2, WATER, LAVA; +class FluidTests extends AbstractTransferApiTest { + private static FluidVariant TAGGED_WATER, TAGGED_WATER_2, WATER, LAVA; private static int finalCommitCount = 0; - + public static DataComponentType TEST; private static SingleSlotStorage createWaterStorage() { return new SingleVariantStorage<>() { @Override @@ -59,16 +69,23 @@ protected void onFinalCommit() { }; } - static { - NbtCompound tag = new NbtCompound(); - tag.putInt("test", 1); - TAGGED_WATER = FluidVariant.of(Fluids.WATER, tag); - TAGGED_WATER_2 = FluidVariant.of(Fluids.WATER, tag); + @BeforeAll + static void beforeAll() { + bootstrap(); + + ComponentChanges components = ComponentChanges.builder() + .add(TEST, 1) + .build(); + TAGGED_WATER = FluidVariant.of(Fluids.WATER, components); + TAGGED_WATER_2 = FluidVariant.of(Fluids.WATER, components); WATER = FluidVariant.of(Fluids.WATER); LAVA = FluidVariant.of(Fluids.LAVA); + TEST = Registry.register(Registries.DATA_COMPONENT_TYPE, new Identifier(TransferTestInitializer.MOD_ID, "test"), + DataComponentType.builder().codec(Codecs.NONNEGATIVE_INT).packetCodec(PacketCodecs.VAR_INT).build()); } - private static void testFluidStorage() { + @Test + public void testFluidStorage() { SingleSlotStorage waterStorage = createWaterStorage(); // Test content @@ -136,6 +153,17 @@ private static void testFluidStorage() { if (finalCommitCount != 1) throw new AssertionError("onFinalCommit() should have been called exactly once."); } + @Test + void testPacketCodec() { + FluidVariant variant = FluidVariant.of(Fluids.WATER, ComponentChanges.builder().add(TEST, 1).build()); + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + RegistryByteBuf rbuf = new RegistryByteBuf(buf, staticDrm()); + FluidVariant.PACKET_CODEC.encode(rbuf, variant); + + FluidVariant decoded = FluidVariant.PACKET_CODEC.decode(rbuf); + Assertions.assertTrue(variant.equals(decoded)); + } + private static void insertWaterWithNesting(SingleSlotStorage waterStorage, boolean doOuterCommit) { try (Transaction tx = Transaction.openOuter()) { if (waterStorage.getAmount() != 0) throw new AssertionError("Initial amount is wrong"); diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java similarity index 83% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java index dd3b21b442..b7e1aec4d0 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/FluidVariantTests.java @@ -16,19 +16,24 @@ package net.fabricmc.fabric.test.transfer.unittests; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; -class FluidVariantTests { - public static void run() { - testFlowing(); +class FluidVariantTests extends AbstractTransferApiTest { + @BeforeAll + static void beforeAll() { + bootstrap(); } - private static void testFlowing() { + @Test + public void testFlowing() { assertFluidEquals(Fluids.WATER, FluidVariant.of(Fluids.WATER), FluidVariant.of(Fluids.FLOWING_WATER)); assertFluidEquals(Fluids.LAVA, FluidVariant.of(Fluids.LAVA), FluidVariant.of(Fluids.FLOWING_LAVA)); assertEquals(FluidVariant.of(Fluids.WATER), FluidVariant.of(Fluids.FLOWING_WATER)); diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java similarity index 82% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java index 81e5bbd4d0..a2e6f345a7 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/ItemTests.java @@ -16,20 +16,34 @@ package net.fabricmc.fabric.test.transfer.unittests; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; import java.util.stream.IntStream; +import io.netty.buffer.Unpooled; import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import net.minecraft.component.ComponentChanges; +import net.minecraft.component.DataComponentType; +import net.minecraft.component.DataComponentTypes; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.SidedInventory; import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; import net.minecraft.screen.ScreenHandler; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.dynamic.Codecs; import net.minecraft.util.math.Direction; import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage; @@ -37,20 +51,23 @@ import net.fabricmc.fabric.api.transfer.v1.storage.Storage; import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil; import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.fabricmc.fabric.test.transfer.ingame.TransferTestInitializer; /** * Tests for the item transfer APIs. */ -class ItemTests { - public static void run() { - testStackReference(); - testInventoryWrappers(); - testLimitedStackCountInventory(); - testLimitedStackCountItem(); - testSimpleInventoryUpdates(); +class ItemTests extends AbstractTransferApiTest { + public static DataComponentType ENERGY; + + @BeforeAll + static void beforeAll() { + bootstrap(); + ENERGY = Registry.register(Registries.DATA_COMPONENT_TYPE, new Identifier(TransferTestInitializer.MOD_ID, "energy"), + DataComponentType.builder().codec(Codecs.NONNEGATIVE_INT).packetCodec(PacketCodecs.VAR_INT).build()); } - private static void testStackReference() { + @Test + public void testStackReference() { // Ensure that Inventory wrappers will try to mutate the backing stack as much as possible. // In many cases, MC code captures a reference to the ItemStack so we want to edit that stack directly // and not a copy whenever we can. Obviously this can't be perfect, but we try to cover as many cases as possible. @@ -75,9 +92,8 @@ private static void testStackReference() { // Also edit the stack when the item matches, even when the NBT and the count change. ItemVariant oldVariant = ItemVariant.of(Items.DIAMOND); - NbtCompound testTag = new NbtCompound(); - testTag.putInt("energy", 42); - ItemVariant newVariant = ItemVariant.of(Items.DIAMOND, testTag); + ComponentChanges components = ComponentChanges.builder().add(ENERGY, 42).build(); + ItemVariant newVariant = ItemVariant.of(Items.DIAMOND, components); try (Transaction tx = Transaction.openOuter()) { invWrapper.extract(oldVariant, 2, tx); @@ -89,7 +105,8 @@ private static void testStackReference() { if (!stackEquals(stack, newVariant, 5)) throw new AssertionError("Failed to update stack NBT or count."); } - private static void testInventoryWrappers() { + @Test + public void testInventoryWrappers() { ItemVariant emptyBucket = ItemVariant.of(Items.BUCKET); TestSidedInventory testInventory = new TestSidedInventory(); checkComparatorOutput(testInventory); @@ -141,6 +158,19 @@ private static void testInventoryWrappers() { } } + @Test + void testPacketCodec() { + ItemStack stack = new ItemStack(Items.DIAMOND_PICKAXE); + stack.set(DataComponentTypes.CUSTOM_NAME, Text.literal("Custom name")); + + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + RegistryByteBuf rbuf = new RegistryByteBuf(buf, staticDrm()); + ItemVariant.PACKET_CODEC.encode(rbuf, ItemVariant.of(stack)); + + ItemVariant decoded = ItemVariant.PACKET_CODEC.decode(rbuf); + Assertions.assertTrue(ItemStack.areEqual(stack, decoded.toStack())); + } + private static boolean stackEquals(ItemStack stack, Item item, int count) { return stackEquals(stack, ItemVariant.of(item), count); } @@ -180,7 +210,8 @@ public boolean canExtract(int slot, ItemStack stack, Direction dir) { /** * Test insertion when {@link Inventory#getMaxCountPerStack()} is the bottleneck. */ - private static void testLimitedStackCountInventory() { + @Test + public void testLimitedStackCountInventory() { ItemVariant diamond = ItemVariant.of(Items.DIAMOND); LimitedStackCountInventory inventory = new LimitedStackCountInventory(diamond.toStack(), diamond.toStack(), diamond.toStack()); InventoryStorage wrapper = InventoryStorage.of(inventory, null); @@ -198,7 +229,8 @@ private static void testLimitedStackCountInventory() { /** * Test insertion when {@link Item#getMaxCount()} is the bottleneck. */ - private static void testLimitedStackCountItem() { + @Test + public void testLimitedStackCountItem() { ItemVariant diamondPickaxe = ItemVariant.of(Items.DIAMOND_PICKAXE); LimitedStackCountInventory inventory = new LimitedStackCountInventory(5); InventoryStorage wrapper = InventoryStorage.of(inventory, null); @@ -247,7 +279,8 @@ private static void checkComparatorOutput(Inventory inventory) { /** * Ensure that SimpleInventory only calls markDirty at the end of a successful transaction. */ - private static void testSimpleInventoryUpdates() { + @Test + public void testSimpleInventoryUpdates() { var simpleInventory = new SimpleInventory(2) { boolean throwOnMarkDirty = true; boolean markDirtyCalled = false; diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java similarity index 88% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java index ea92943b67..31af285209 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/PlayerInventoryStorageTests.java @@ -16,10 +16,13 @@ package net.fabricmc.fabric.test.transfer.unittests; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; import java.util.function.Function; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; @@ -29,15 +32,25 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; -public class PlayerInventoryStorageTests { - public static void run() { +public class PlayerInventoryStorageTests extends AbstractTransferApiTest { + @BeforeAll + static void beforeAll() { + bootstrap(); + } + + @Test + public void testStackingOffer() { // Ensure that offer stacks as expected. testStacking(playerInv -> playerInv::offer); + } + + @Test + public void testStackingInser() { // Also test that the behavior of insert matches that of offer. testStacking(playerInv -> playerInv::insert); } - private static void testStacking(Function inserterBuilder) { + private void testStacking(Function inserterBuilder) { // A bit hacky... but nothing should try using the null player entity as long as we don't call drop. PlayerInventory inv = new PlayerInventory(null); InsertionFunction inserter = inserterBuilder.apply(PlayerInventoryStorage.of(inv)); diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java similarity index 55% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java index da948cd5dc..83fcefd865 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/SingleVariantItemStorageTests.java @@ -17,36 +17,60 @@ package net.fabricmc.fabric.test.transfer.unittests; import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; import java.util.List; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import net.minecraft.component.ComponentChanges; +import net.minecraft.component.DataComponentType; +import net.minecraft.component.DataComponentTypes; import net.minecraft.fluid.Fluids; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; +import net.fabricmc.fabric.api.transfer.v1.item.base.SingleItemStorage; import net.fabricmc.fabric.api.transfer.v1.storage.Storage; import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage; import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantItemStorage; import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; +import net.fabricmc.fabric.test.transfer.ingame.TransferTestInitializer; + +public class SingleVariantItemStorageTests extends AbstractTransferApiTest { + private static FluidVariant LAVA; + public static DataComponentType FLUID; -public class SingleVariantItemStorageTests { - private static final FluidVariant LAVA = FluidVariant.of(Fluids.LAVA); + @BeforeAll + static void beforeAll() { + bootstrap(); - public static void run() { - testWaterTank(); + LAVA = FluidVariant.of(Fluids.LAVA); + FLUID = Registry.register( + Registries.DATA_COMPONENT_TYPE, new Identifier(TransferTestInitializer.MOD_ID, "fluid"), + DataComponentType.builder().codec(FluidData.CODEC).packetCodec(FluidData.PACKET_CODEC).build()); } - private static void testWaterTank() { + @Test + public void testWaterTank() { SimpleInventory inv = new SimpleInventory(new ItemStack(Items.DIAMOND, 2), ItemStack.EMPTY); ContainerItemContext ctx = new InventoryContainerItemContext(inv); @@ -57,7 +81,7 @@ private static void testWaterTank() { assertEquals(BUCKET, storage.insert(LAVA, BUCKET, tx)); // Insertion should create a new stack. assertEquals(1, inv.getStack(0).getCount()); - assertEquals(null, inv.getStack(0).getNbt()); + assertEquals(ComponentChanges.EMPTY, inv.getStack(0).getComponentChanges()); assertEquals(1, inv.getStack(1).getCount()); assertEquals(LAVA, getFluid(inv.getStack(1))); assertEquals(BUCKET, getAmount(inv.getStack(1))); @@ -75,7 +99,7 @@ private static void testWaterTank() { // Make sure custom NBT is kept. Text customName = Text.literal("Lava-containing diamond!"); - inv.getStack(0).setCustomName(customName); + inv.getStack(0).set(DataComponentTypes.CUSTOM_NAME, customName); try (Transaction tx = Transaction.openOuter()) { // Test extract along the way. @@ -90,37 +114,105 @@ private static void testWaterTank() { assertEquals(0L, getAmount(inv.getStack(0))); } - private static FluidVariant getFluid(ItemStack stack) { - NbtCompound nbt = stack.getNbt(); + @Test + public void writeNbtTest() { + SingleItemStorage storage = new SingleItemStorage() { + @Override + protected long getCapacity(ItemVariant variant) { + return 10; + } + }; - if (nbt != null && nbt.contains("fluid")) { - return FluidVariant.fromNbt(nbt.getCompound("fluid")); - } else { - return FluidVariant.blank(); + try (Transaction tx = Transaction.openOuter()) { + storage.insert(ItemVariant.of(Items.DIAMOND), 1, tx); + tx.commit(); } + + NbtCompound nbt = new NbtCompound(); + storage.writeNbt(nbt, staticDrm()); + assertEquals("{amount:1L,variant:{components:{},item:\"minecraft:diamond\"}}", nbt.toString()); } - private static long getAmount(ItemStack stack) { - NbtCompound nbt = stack.getNbt(); + @Test + public void readNbtTest() { + SingleItemStorage storage = new SingleItemStorage() { + @Override + protected long getCapacity(ItemVariant variant) { + return 10; + } + }; - if (nbt != null) { - return nbt.getLong("amount"); - } else { - return 0; + NbtCompound variantNbt = new NbtCompound(); + variantNbt.putString("item", "minecraft:diamond"); + variantNbt.put("components", new NbtCompound()); + NbtCompound nbt = new NbtCompound(); + nbt.putLong("amount", 1); + nbt.put("variant", variantNbt); + + storage.readNbt(nbt, staticDrm()); + + try (Transaction tx = Transaction.openOuter()) { + assertEquals(1L, storage.extract(ItemVariant.of(Items.DIAMOND), 1, tx)); + tx.commit(); } } + @Test + public void readInvalidNbtTest() { + SingleItemStorage storage = new SingleItemStorage() { + @Override + protected long getCapacity(ItemVariant variant) { + return 10; + } + }; + + // Test that invalid NBT defaults to empty. + NbtCompound variantNbt = new NbtCompound(); + variantNbt.putString("id", "minecraft:diamond"); + NbtCompound nbt = new NbtCompound(); + nbt.putLong("amount", 1); + nbt.put("variant", variantNbt); + + storage.readNbt(nbt, staticDrm()); + + try (Transaction tx = Transaction.openOuter()) { + assertEquals(0L, storage.extract(ItemVariant.of(Items.DIAMOND), 1, tx)); + tx.commit(); + } + } + + private static FluidVariant getFluid(ItemStack stack) { + return stack.getOrDefault(FLUID, FluidData.DEFAULT).variant(); + } + + private static long getAmount(ItemStack stack) { + return stack.getOrDefault(FLUID, FluidData.DEFAULT).amount(); + } + private static void setContents(ItemStack stack, FluidVariant newResource, long newAmount) { if (newAmount > 0) { - stack.getOrCreateNbt().put("fluid", newResource.toNbt()); - stack.getOrCreateNbt().putLong("amount", newAmount); + FluidData fluidData = new FluidData(newResource, newAmount); + stack.set(FLUID, fluidData); } else { // Make sure emptied tanks can stack with tanks without NBT. - stack.removeSubNbt("fluid"); - stack.removeSubNbt("amount"); + stack.remove(FLUID); } } + public record FluidData(FluidVariant variant, long amount) { + public static Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + FluidVariant.CODEC.fieldOf("variant").forGetter(FluidData::variant), + Codec.LONG.fieldOf("amount").forGetter(FluidData::amount) + ).apply(instance, FluidData::new)); + public static PacketCodec PACKET_CODEC = PacketCodec.tuple( + FluidVariant.PACKET_CODEC, FluidData::variant, + PacketCodecs.VAR_LONG, FluidData::amount, + FluidData::new + ); + + public static FluidData DEFAULT = new FluidData(FluidVariant.blank(), 0); + } + private static Storage createTankStorage(ContainerItemContext ctx) { return new SingleVariantItemStorage<>(ctx) { @Override diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java similarity index 90% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java index 86a118b1e3..e9067027c4 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/TransactionStateTests.java @@ -16,20 +16,25 @@ package net.fabricmc.fabric.test.transfer.unittests; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.fabricmc.fabric.test.transfer.TestUtil; -class TransactionStateTests { - public static void run() { - testTransactionExceptions(); - testTransactionLifecycle(); - } +class TransactionStateTests extends AbstractTransferApiTest { + private int callbacksInvoked = 0; - private static int callbacksInvoked = 0; + @BeforeAll + static void beforeAll() { + bootstrap(); + } /** * Make sure that transaction global state stays valid in case of exceptions. */ - private static void testTransactionExceptions() { + @Test + public void testTransactionExceptions() { // Test exception inside the try. ensureException(() -> { try (Transaction tx = Transaction.openOuter()) { @@ -96,7 +101,8 @@ private static void ensureException(Runnable runnable, String message) { } } - private static void testTransactionLifecycle() { + @Test + public void testTransactionLifecycle() { TestUtil.assertEquals(Transaction.Lifecycle.NONE, Transaction.getLifecycle()); try (Transaction transaction = Transaction.openOuter()) { diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java similarity index 87% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java rename to fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java index 494dee84e0..d5b9c7cce0 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java +++ b/fabric-transfer-api-v1/src/test/java/net/fabricmc/fabric/test/transfer/unittests/UnderlyingViewTests.java @@ -21,6 +21,8 @@ import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenCustomHashMap; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import net.minecraft.block.Blocks; import net.minecraft.block.entity.FurnaceBlockEntity; @@ -30,16 +32,19 @@ import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; +import net.fabricmc.fabric.test.transfer.TestUtil; -public class UnderlyingViewTests { - public static void run() { - testFurnaceSides(); +public class UnderlyingViewTests extends AbstractTransferApiTest { + @BeforeAll + static void beforeAll() { + bootstrap(); } /** * Ensure that only 3 slots with different underlying view exist on all sides of a furnace combined. */ - private static void testFurnaceSides() { + @Test + public void testFurnaceSides() { FurnaceBlockEntity furnace = new FurnaceBlockEntity(BlockPos.ORIGIN, Blocks.FURNACE.getDefaultState()); Set> viewSet = Collections.newSetFromMap(new Reference2ReferenceOpenCustomHashMap<>(new Hash.Strategy<>() { diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/TestUtil.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/TestUtil.java similarity index 94% rename from fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/TestUtil.java rename to fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/TestUtil.java index 5bdc5389ff..1a9df0dd94 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/unittests/TestUtil.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/TestUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.fabricmc.fabric.test.transfer.unittests; +package net.fabricmc.fabric.test.transfer; import java.util.Objects; diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java index 94906ddbb2..6ceecae5f6 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java @@ -282,7 +282,7 @@ public void testBadBrewingStandIsValid(TestContext context) { /** * Regression test for double chest wrapper only updating modified halves. */ - @GameTest(templateName = "fabric-transfer-api-v1-testmod:double_chest_comparators", method_57098 = true) + @GameTest(templateName = "fabric-transfer-api-v1-testmod:double_chest_comparators", skyAccess = true) public void testDoubleChestComparator(TestContext context) { BlockPos chestPos = new BlockPos(2, 2, 2); Storage storage = ItemStorage.SIDED.find(context.getWorld(), context.getAbsolutePos(chestPos), Direction.UP); diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java index 0a5232153b..91eb2b57c5 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java @@ -16,7 +16,7 @@ package net.fabricmc.fabric.test.transfer.gametests; -import static net.fabricmc.fabric.test.transfer.unittests.TestUtil.assertEquals; +import static net.fabricmc.fabric.test.transfer.TestUtil.assertEquals; import net.minecraft.fluid.Fluids; import net.minecraft.server.world.ServerWorld; diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlockEntity.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlockEntity.java index 04794778e1..88b706907f 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlockEntity.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlockEntity.java @@ -60,12 +60,12 @@ public void tick() { @Override protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { super.writeNbt(nbt, wrapperLookup); - storage.writeNbt(nbt); + storage.writeNbt(nbt, wrapperLookup); } @Override public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { super.readNbt(nbt, wrapperLookup); - storage.readNbt(nbt); + storage.readNbt(nbt, wrapperLookup); } } diff --git a/fabric-transfer-api-v1/src/testmod/resources/fabric.mod.json b/fabric-transfer-api-v1/src/testmod/resources/fabric.mod.json index 23b69c62ac..1669d8904a 100644 --- a/fabric-transfer-api-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-transfer-api-v1/src/testmod/resources/fabric.mod.json @@ -10,8 +10,7 @@ }, "entrypoints": { "main": [ - "net.fabricmc.fabric.test.transfer.ingame.TransferTestInitializer", - "net.fabricmc.fabric.test.transfer.unittests.UnitTestsInitializer" + "net.fabricmc.fabric.test.transfer.ingame.TransferTestInitializer" ], "client": [ "net.fabricmc.fabric.test.transfer.ingame.client.FluidVariantRenderTest" diff --git a/settings.gradle b/settings.gradle index f161161bd3..3e984e1ee5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -55,7 +55,7 @@ include 'fabric-resource-loader-v0' include 'fabric-screen-api-v1' include 'fabric-screen-handler-api-v1' include 'fabric-sound-api-v1' -//include 'fabric-transfer-api-v1' +include 'fabric-transfer-api-v1' include 'fabric-transitive-access-wideners-v1' include 'deprecated'