mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-21 03:10:54 -04:00
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
This commit is contained in:
parent
2f977a43d9
commit
9d6d003f62
34 changed files with 586 additions and 385 deletions
fabric-transfer-api-v1/src
main/java/net/fabricmc/fabric
api/transfer/v1
impl/transfer
mixin/transfer
test/java/net/fabricmc/fabric/test/transfer/unittests
AbstractTransferApiTest.javaAttributeTests.javaBaseStorageTests.javaFluidItemTests.javaFluidTests.javaFluidVariantTests.javaItemTests.javaPlayerInventoryStorageTests.javaSingleVariantItemStorageTests.javaTransactionStateTests.javaUnderlyingViewTests.java
testmod
|
@ -18,13 +18,14 @@ package net.fabricmc.fabric.api.transfer.v1.fluid;
|
|||
|
||||
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 @@ public final class 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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>Do not extend this class. Use {@link #of(Fluid)} and {@link #of(Fluid, NbtCompound)} to create instances.
|
||||
* <p>Do not extend this class. Use {@link #of(Fluid)} and {@link #of(Fluid, ComponentChanges)} to create instances.
|
||||
*
|
||||
* <p>{@link net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering} can be used for client-side rendering of fluid variants.
|
||||
*
|
||||
* <p><b>Fluid variants must always be compared with {@code equals}, never by reference!</b>
|
||||
* {@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<Fluid> {
|
||||
Codec<FluidVariant> CODEC = VariantCodecs.FLUID_CODEC;
|
||||
PacketCodec<RegistryByteBuf, FluidVariant> PACKET_CODEC = VariantCodecs.FLUID_PACKET_CODEC;
|
||||
|
||||
/**
|
||||
* Retrieve a blank FluidVariant.
|
||||
*/
|
||||
|
@ -54,7 +60,7 @@ public interface FluidVariant extends TransferVariant<Fluid> {
|
|||
* {@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 @@ public interface FluidVariant extends TransferVariant<Fluid> {
|
|||
*
|
||||
* <p>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 @@ public interface FluidVariant extends TransferVariant<Fluid> {
|
|||
return getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a variant from an NBT compound tag, assuming it was serialized using {@link #toNbt}.
|
||||
*
|
||||
* <p>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<Fluid> getRegistryEntry() {
|
||||
return getFluid().getRegistryEntry();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public final class EmptyItemFluidStorage implements InsertionOnlyStorage<FluidVa
|
|||
* @param insertableAmount The amount of fluid that can be inserted.
|
||||
*/
|
||||
public EmptyItemFluidStorage(ContainerItemContext context, Item fullItem, Fluid insertableFluid, long insertableAmount) {
|
||||
this(context, emptyVariant -> ItemVariant.of(fullItem, emptyVariant.getNbt()), insertableFluid, insertableAmount);
|
||||
this(context, emptyVariant -> ItemVariant.of(fullItem, emptyVariant.getComponents()), insertableFluid, insertableAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,7 +52,7 @@ public final class FullItemFluidStorage implements ExtractionOnlyStorage<FluidVa
|
|||
* @param containedAmount How much of {@code containedFluid} is contained.
|
||||
*/
|
||||
public FullItemFluidStorage(ContainerItemContext context, Item emptyItem, FluidVariant containedFluid, long containedAmount) {
|
||||
this(context, fullVariant -> ItemVariant.of(emptyItem, fullVariant.getNbt()), containedFluid, containedAmount);
|
||||
this(context, fullVariant -> ItemVariant.of(emptyItem, fullVariant.getComponents()), containedFluid, containedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ package net.fabricmc.fabric.api.transfer.v1.fluid.base;
|
|||
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 @@ public abstract class SingleFluidStorage extends SingleVariantStorage<FluidVaria
|
|||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,26 +16,34 @@
|
|||
|
||||
package net.fabricmc.fabric.api.transfer.v1.item;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
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.
|
||||
*
|
||||
* <p>Do not implement, use the static {@code of(...)} functions instead.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ItemVariant extends TransferVariant<Item> {
|
||||
Codec<ItemVariant> CODEC = VariantCodecs.ITEM_CODEC;
|
||||
PacketCodec<RegistryByteBuf, ItemVariant> PACKET_CODEC = VariantCodecs.ITEM_PACKET_CODEC;
|
||||
|
||||
/**
|
||||
* Retrieve a blank ItemVariant.
|
||||
*/
|
||||
|
@ -47,28 +55,28 @@ public interface ItemVariant extends TransferVariant<Item> {
|
|||
* 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 @@ public interface ItemVariant extends TransferVariant<Item> {
|
|||
return getObject();
|
||||
}
|
||||
|
||||
default RegistryEntry<Item> getRegistryEntry() {
|
||||
return getItem().getRegistryEntry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new item stack with count 1 from this variant.
|
||||
*/
|
||||
|
@ -92,25 +104,6 @@ public interface ItemVariant extends TransferVariant<Item> {
|
|||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ public abstract class SingleItemStorage extends SingleVariantStorage<ItemVariant
|
|||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,10 @@ package net.fabricmc.fabric.api.transfer.v1.storage;
|
|||
|
||||
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.
|
||||
*
|
||||
* <p>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> {
|
|||
O getObject();
|
||||
|
||||
/**
|
||||
* Return the underlying tag.
|
||||
*
|
||||
* <p><b>NEVER MUTATE THIS NBT TAG</b>, 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 @@ public interface TransferVariant<O> {
|
|||
*
|
||||
* <p>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 @@ public interface TransferVariant<O> {
|
|||
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.
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>Implementation note: Objects are saved using their raw registry integer id.
|
||||
*/
|
||||
void toPacket(PacketByteBuf buf);
|
||||
}
|
||||
|
|
|
@ -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 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant;
|
|||
* @see net.fabricmc.fabric.api.transfer.v1.item.base.SingleItemStorage SingleItemStorage for item variants.
|
||||
*/
|
||||
public abstract class SingleVariantStorage<T extends TransferVariant<?>> extends SnapshotParticipant<ResourceAmount<T>> implements SingleSlotStorage<T> {
|
||||
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 @@ public abstract class SingleVariantStorage<T extends TransferVariant<?>> extends
|
|||
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 @@ public abstract class SingleVariantStorage<T extends TransferVariant<?>> extends
|
|||
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 <T> the type of the item variant
|
||||
*/
|
||||
public static <T extends TransferVariant<?>> void readNbt(SingleVariantStorage<T> storage, Codec<T> codec, Supplier<T> fallback, NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) {
|
||||
final RegistryOps<NbtElement> ops = wrapperLookup.getOps(NbtOps.INSTANCE);
|
||||
final DataResult<T> 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 <T> the type of the item variant
|
||||
*/
|
||||
public static <T extends TransferVariant<?>> void writeNbt(SingleVariantStorage<T> storage, Codec<T> codec, NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) {
|
||||
final RegistryOps<NbtElement> ops = wrapperLookup.getOps(NbtOps.INSTANCE);
|
||||
nbt.put("variant", Util.getResult(codec.encodeStart(ops, storage.variant), RuntimeException::new));
|
||||
nbt.putLong("amount", storage.amount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ItemVariant> 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<RegistryByteBuf, ItemVariant> ITEM_PACKET_CODEC = PacketCodec.tuple(
|
||||
PacketCodecs.registryEntry(RegistryKeys.ITEM), ItemVariant::getRegistryEntry,
|
||||
ComponentChanges.PACKET_CODEC, ItemVariant::getComponents,
|
||||
ItemVariantImpl::of
|
||||
);
|
||||
|
||||
public static final Codec<FluidVariant> 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<RegistryByteBuf, FluidVariant> FLUID_PACKET_CODEC = PacketCodec.tuple(
|
||||
PacketCodecs.registryEntry(RegistryKeys.FLUID), FluidVariant::getRegistryEntry,
|
||||
ComponentChanges.PACKET_CODEC, FluidVariant::getComponents,
|
||||
FluidVariantImpl::of
|
||||
);
|
||||
}
|
|
@ -55,7 +55,7 @@ public class EmptyBucketStorage implements InsertionOnlyStorage<FluidVariant> {
|
|||
// 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;
|
||||
|
|
|
@ -19,22 +19,21 @@ package net.fabricmc.fabric.impl.transfer.fluid;
|
|||
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 class FluidVariantImpl implements FluidVariant {
|
|||
}
|
||||
}
|
||||
|
||||
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> 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 class FluidVariantImpl implements FluidVariant {
|
|||
}
|
||||
|
||||
@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 class FluidVariantImpl implements FluidVariant {
|
|||
|
||||
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
|
||||
|
|
|
@ -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 class WaterPotionStorage implements ExtractionOnlyStorage<FluidVariant>,
|
|||
|
||||
private static boolean isWaterPotion(ContainerItemContext context) {
|
||||
ItemVariant variant = context.getItemVariant();
|
||||
|
||||
return variant.isOf(Items.POTION) && PotionUtil.getPotion(variant.getNbt()) == Potions.WATER;
|
||||
Optional<? extends PotionContentsComponent> potionContents = variant.getComponents().get(DataComponentTypes.POTION_CONTENTS);
|
||||
RegistryEntry<Potion> 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 @@ public class WaterPotionStorage implements ExtractionOnlyStorage<FluidVariant>,
|
|||
|
||||
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
|
||||
|
|
|
@ -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 @@ class InventorySlotWrapper extends SingleStackStorage {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -19,45 +19,44 @@ package net.fabricmc.fabric.impl.transfer.item;
|
|||
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> 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). <b>MAKE SURE IT IS NEVER MODIFIED!</b>
|
||||
*/
|
||||
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 class ItemVariantImpl implements ItemVariant {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public NbtCompound getNbt() {
|
||||
return nbt;
|
||||
public ComponentChanges getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,53 +75,9 @@ public class ItemVariantImpl implements ItemVariant {
|
|||
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 class ItemVariantImpl implements ItemVariant {
|
|||
|
||||
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
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
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 @@ import net.fabricmc.fabric.impl.transfer.fluid.FluidVariantImpl;
|
|||
@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() {
|
||||
|
|
|
@ -18,6 +18,7 @@ package net.fabricmc.fabric.mixin.transfer;
|
|||
|
||||
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 @@ import net.fabricmc.fabric.impl.transfer.item.ItemVariantImpl;
|
|||
@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() {
|
||||
|
|
|
@ -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 abstract class AbstractTransferApiTest {
|
||||
protected static void bootstrap() {
|
||||
SharedConstants.createGameVersion();
|
||||
Bootstrap.initialize();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
LoggerFactory.getLogger("fabric-transfer-api-v1 testmod").info("Transfer API unit tests successful.");
|
||||
protected static DynamicRegistryManager staticDrm() {
|
||||
return DynamicRegistryManager.of(Registries.REGISTRIES);
|
||||
}
|
||||
}
|
|
@ -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 @@ import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributes;
|
|||
/**
|
||||
* 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 @@ public class AttributeTests {
|
|||
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));
|
|
@ -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.FilteringStorage;
|
|||
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<FluidVariant> storage = new SingleVariantStorage<>() {
|
||||
@Override
|
||||
protected FluidVariant getBlankVariant() {
|
||||
|
@ -102,7 +106,8 @@ public class BaseStorageTests {
|
|||
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/3414">
|
||||
* {@code nonEmptyIterator} not handling views that become empty during iteration correctly</a>.
|
||||
*/
|
||||
private static void testNonEmptyIteratorWithModifiedView() {
|
||||
@Test
|
||||
public void testNonEmptyIteratorWithModifiedView() {
|
||||
SingleVariantStorage<FluidVariant> storage = SingleFluidStorage.withFixedCapacity(BUCKET, () -> { });
|
||||
storage.variant = FluidVariant.of(Fluids.WATER);
|
||||
|
|
@ -18,18 +18,25 @@ package net.fabricmc.fabric.test.transfer.unittests;
|
|||
|
||||
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.storage.base.SingleSlotStorage;
|
|||
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 @@ class FluidItemTests {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ class FluidItemTests {
|
|||
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<FluidVariant> waterBottleStorage = new InventoryContainerItem(testInventory, 0).find(FluidStorage.ITEM);
|
||||
|
@ -162,7 +167,7 @@ class FluidItemTests {
|
|||
}
|
||||
|
||||
// Make sure extraction nothing is returned for other potions
|
||||
PotionUtil.setPotion(testInventory.getStack(0), Potions.LUCK);
|
||||
setPotion(testInventory.getStack(0), Potions.LUCK);
|
||||
Storage<FluidVariant> luckyStorage = new InventoryContainerItem(testInventory, 0).find(FluidStorage.ITEM);
|
||||
|
||||
if (StorageUtil.findStoredResource(luckyStorage) != null) {
|
||||
|
@ -170,7 +175,8 @@ class FluidItemTests {
|
|||
}
|
||||
}
|
||||
|
||||
private static void testSimpleContentsQuery() {
|
||||
@Test
|
||||
public void testSimpleContentsQuery() {
|
||||
assertEquals(
|
||||
new ResourceAmount<>(FluidVariant.of(Fluids.WATER), BUCKET),
|
||||
StorageUtil.findExtractableContent(
|
||||
|
@ -183,9 +189,24 @@ class FluidItemTests {
|
|||
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<Potion> getPotion(ItemStack stack) {
|
||||
return stack.getOrDefault(DataComponentTypes.POTION_CONTENTS, PotionContentsComponent.DEFAULT).potion().orElse(null);
|
||||
}
|
||||
|
||||
public static void setPotion(ItemStack itemStack, RegistryEntry<Potion> potion) {
|
||||
itemStack.set(DataComponentTypes.POTION_CONTENTS, new PotionContentsComponent(potion));
|
||||
}
|
||||
}
|
|
@ -18,23 +18,33 @@ package net.fabricmc.fabric.test.transfer.unittests;
|
|||
|
||||
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<Integer> TEST;
|
||||
private static SingleSlotStorage<FluidVariant> createWaterStorage() {
|
||||
return new SingleVariantStorage<>() {
|
||||
@Override
|
||||
|
@ -59,16 +69,23 @@ class FluidTests {
|
|||
};
|
||||
}
|
||||
|
||||
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.<Integer>builder().codec(Codecs.NONNEGATIVE_INT).packetCodec(PacketCodecs.VAR_INT).build());
|
||||
}
|
||||
|
||||
private static void testFluidStorage() {
|
||||
@Test
|
||||
public void testFluidStorage() {
|
||||
SingleSlotStorage<FluidVariant> waterStorage = createWaterStorage();
|
||||
|
||||
// Test content
|
||||
|
@ -136,6 +153,17 @@ class FluidTests {
|
|||
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<FluidVariant> waterStorage, boolean doOuterCommit) {
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
if (waterStorage.getAmount() != 0) throw new AssertionError("Initial amount is wrong");
|
|
@ -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));
|
|
@ -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.item.ItemVariant;
|
|||
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<Integer> ENERGY;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
bootstrap();
|
||||
ENERGY = Registry.register(Registries.DATA_COMPONENT_TYPE, new Identifier(TransferTestInitializer.MOD_ID, "energy"),
|
||||
DataComponentType.<Integer>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 @@ class ItemTests {
|
|||
|
||||
// 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 @@ class ItemTests {
|
|||
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 @@ class ItemTests {
|
|||
}
|
||||
}
|
||||
|
||||
@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 @@ class ItemTests {
|
|||
/**
|
||||
* 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 @@ class ItemTests {
|
|||
/**
|
||||
* 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 @@ class ItemTests {
|
|||
/**
|
||||
* 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;
|
|
@ -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.item.PlayerInventoryStorage;
|
|||
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<PlayerInventoryStorage, InsertionFunction> inserterBuilder) {
|
||||
private void testStacking(Function<PlayerInventoryStorage, InsertionFunction> 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));
|
|
@ -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 {
|
||||
private static final FluidVariant LAVA = FluidVariant.of(Fluids.LAVA);
|
||||
public class SingleVariantItemStorageTests extends AbstractTransferApiTest {
|
||||
private static FluidVariant LAVA;
|
||||
public static DataComponentType<FluidData> FLUID;
|
||||
|
||||
public static void run() {
|
||||
testWaterTank();
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
bootstrap();
|
||||
|
||||
LAVA = FluidVariant.of(Fluids.LAVA);
|
||||
FLUID = Registry.register(
|
||||
Registries.DATA_COMPONENT_TYPE, new Identifier(TransferTestInitializer.MOD_ID, "fluid"),
|
||||
DataComponentType.<FluidData>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 @@ public class SingleVariantItemStorageTests {
|
|||
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 @@ public class SingleVariantItemStorageTests {
|
|||
|
||||
// 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 @@ public class SingleVariantItemStorageTests {
|
|||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readNbtTest() {
|
||||
SingleItemStorage storage = new SingleItemStorage() {
|
||||
@Override
|
||||
protected long getCapacity(ItemVariant variant) {
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private static long getAmount(ItemStack stack) {
|
||||
NbtCompound nbt = stack.getNbt();
|
||||
@Test
|
||||
public void readInvalidNbtTest() {
|
||||
SingleItemStorage storage = new SingleItemStorage() {
|
||||
@Override
|
||||
protected long getCapacity(ItemVariant variant) {
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
|
||||
if (nbt != null) {
|
||||
return nbt.getLong("amount");
|
||||
} else {
|
||||
return 0;
|
||||
// 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<FluidData> 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<RegistryByteBuf, FluidData> 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<FluidVariant> createTankStorage(ContainerItemContext ctx) {
|
||||
return new SingleVariantItemStorage<>(ctx) {
|
||||
@Override
|
|
@ -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;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
bootstrap();
|
||||
}
|
||||
|
||||
private static int callbacksInvoked = 0;
|
||||
|
||||
/**
|
||||
* 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 @@ class TransactionStateTests {
|
|||
}
|
||||
}
|
||||
|
||||
private static void testTransactionLifecycle() {
|
||||
@Test
|
||||
public void testTransactionLifecycle() {
|
||||
TestUtil.assertEquals(Transaction.Lifecycle.NONE, Transaction.getLifecycle());
|
||||
|
||||
try (Transaction transaction = Transaction.openOuter()) {
|
|
@ -21,6 +21,8 @@ import java.util.Set;
|
|||
|
||||
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.minecraft.util.math.Direction;
|
|||
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<StorageView<ItemVariant>> viewSet = Collections.newSetFromMap(new Reference2ReferenceOpenCustomHashMap<>(new Hash.Strategy<>() {
|
|
@ -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;
|
||||
|
|
@ -282,7 +282,7 @@ public class VanillaStorageTests {
|
|||
/**
|
||||
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/2810">double chest wrapper only updating modified halves</a>.
|
||||
*/
|
||||
@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<ItemVariant> storage = ItemStorage.SIDED.find(context.getWorld(), context.getAbsolutePos(chestPos), Direction.UP);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -60,12 +60,12 @@ public class FluidChuteBlockEntity extends BlockEntity {
|
|||
@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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue