diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/context/ContainerItemContext.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/context/ContainerItemContext.java index cdec47683..f8c3f6c6d 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/context/ContainerItemContext.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/context/ContainerItemContext.java @@ -88,7 +88,21 @@ import net.fabricmc.fabric.impl.transfer.context.SingleSlotContainerItemContext; @ApiStatus.Experimental public interface ContainerItemContext { /** - * Return a context for the passed player's hand. This is recommended for item use interactions. + * Returns a context for interaction with a player's hand. This is recommended for item use interactions. + * + * <p>In creative mode, {@link #withInitial(ItemStack)} is used to avoid modifying the item in hand. + * Otherwise, {@link #ofPlayerHand} is used. + */ + static ContainerItemContext forPlayerInteraction(PlayerEntity player, Hand hand) { + if (player.getAbilities().creativeMode) { + return withInitial(player.getStackInHand(hand)); + } else { + return ofPlayerHand(player, hand); + } + } + + /** + * Return a context for the passed player's hand. */ static ContainerItemContext ofPlayerHand(PlayerEntity player, Hand hand) { return new PlayerContainerItemContext(player, hand); 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 be3476277..d6ac5e77b 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 @@ -117,7 +117,7 @@ public final class FluidStorage { * This means that per-item combined providers registered through {@code combinedItemApiProvider} DO NOT prevent these general providers from running, * however regular providers registered through {@code ItemApiLookup#register...} that return a non-null API instance DO prevent it. */ - public static Event<CombinedItemApiProvider> GENERAL_COMBINED_PROVIDER = CombinedProvidersImpl.createEvent(false); + public static final Event<CombinedItemApiProvider> GENERAL_COMBINED_PROVIDER = CombinedProvidersImpl.createEvent(false); @FunctionalInterface public interface CombinedItemApiProvider { diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorageUtil.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorageUtil.java new file mode 100644 index 000000000..7abef4c65 --- /dev/null +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidStorageUtil.java @@ -0,0 +1,109 @@ +/* + * 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.api.transfer.v1.fluid; + +import org.jetbrains.annotations.ApiStatus; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvent; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.Hand; + +import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; +import net.fabricmc.fabric.api.transfer.v1.storage.Storage; +import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; + +/** + * Helper functions to work with fluid storages. + * + * <p><b>Experimental feature</b>, we reserve the right to remove or change it without further notice. + * The transfer API is a complex addition, and we want to be able to correct possible design mistakes. + */ +@ApiStatus.Experimental +public final class FluidStorageUtil { + /** + * Try to make the item in a player hand "interact" with a fluid storage. + * This can be used when a player right-clicks a tank, for example. + * + * <p>More specifically, this function tries to find a fluid storing item in the player's hand. + * Then, it tries to fill that item from the storage. If that fails, it tries to fill the storage from that item. + * + * <p>Only up to one fluid variant will be moved, and the corresponding emptying/filling sound will be played. + * In creative mode, the player's inventory will not be modified. + * + * @param storage The storage that the player is interacting with. + * @param player The player. + * @param hand The hand that the player used. + * @return True if some fluid was moved. + */ + public static boolean interactWithFluidStorage(Storage<FluidVariant> storage, PlayerEntity player, Hand hand) { + // Check if hand is a fluid container. + Storage<FluidVariant> handStorage = ContainerItemContext.forPlayerInteraction(player, hand).find(FluidStorage.ITEM); + if (handStorage == null) return false; + + // Try to fill hand first, otherwise try to empty it. + Item handItem = player.getStackInHand(hand).getItem(); + return moveWithSound(storage, handStorage, player, true, handItem) || moveWithSound(handStorage, storage, player, false, handItem); + } + + private static boolean moveWithSound(Storage<FluidVariant> from, Storage<FluidVariant> to, PlayerEntity player, boolean fill, Item handItem) { + for (StorageView<FluidVariant> view : from) { + if (view.isResourceBlank()) continue; + FluidVariant resource = view.getResource(); + long maxExtracted; + + // check how much can be extracted + try (Transaction extractionTestTransaction = Transaction.openOuter()) { + maxExtracted = view.extract(resource, Long.MAX_VALUE, extractionTestTransaction); + extractionTestTransaction.abort(); + } + + try (Transaction transferTransaction = Transaction.openOuter()) { + // check how much can be inserted + long accepted = to.insert(resource, maxExtracted, transferTransaction); + + // extract it, or rollback if the amounts don't match + if (accepted > 0 && view.extract(resource, accepted, transferTransaction) == accepted) { + transferTransaction.commit(); + + SoundEvent sound = fill ? FluidVariantAttributes.getFillSound(resource) : FluidVariantAttributes.getEmptySound(resource); + + // Temporary workaround to use the correct sound for water bottles. + // TODO: Look into providing a proper item-aware fluid sound API. + if (resource.isOf(Fluids.WATER)) { + if (fill && handItem == Items.GLASS_BOTTLE) sound = SoundEvents.ITEM_BOTTLE_FILL; + if (!fill && handItem == Items.POTION) sound = SoundEvents.ITEM_BOTTLE_EMPTY; + } + + player.playSound(sound, SoundCategory.BLOCKS, 1, 1); + + return true; + } + } + } + + return false; + } + + private FluidStorageUtil() { + } +} diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariantAttributes.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariantAttributes.java index f6bc8184f..49d49de23 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariantAttributes.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/FluidVariantAttributes.java @@ -21,12 +21,15 @@ import java.util.Optional; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import net.minecraft.block.Blocks; import net.minecraft.fluid.FlowableFluid; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Style; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; import net.minecraft.world.World; import net.fabricmc.fabric.api.lookup.v1.custom.ApiProviderMap; @@ -42,6 +45,7 @@ import net.fabricmc.fabric.impl.transfer.TransferApiImpl; public final class FluidVariantAttributes { private static final ApiProviderMap<Fluid, FluidVariantAttributeHandler> HANDLERS = ApiProviderMap.create(); private static final FluidVariantAttributeHandler DEFAULT_HANDLER = new FluidVariantAttributeHandler() { }; + private static volatile boolean coloredVanillaFluidNames = false; private FluidVariantAttributes() { } @@ -55,6 +59,13 @@ public final class FluidVariantAttributes { } } + /** + * Enable blue- and red-colored names for water and lava respectively. + */ + public static void enableColoredVanillaFluidNames() { + coloredVanillaFluidNames = true; + } + /** * Return the attribute handler for the passed fluid, if available, and {@code null} otherwise. */ @@ -157,12 +168,30 @@ public final class FluidVariantAttributes { static { register(Fluids.WATER, new FluidVariantAttributeHandler() { + @Override + public Text getName(FluidVariant fluidVariant) { + if (coloredVanillaFluidNames) { + return Blocks.WATER.getName().setStyle(Style.EMPTY.withColor(Formatting.BLUE)); + } else { + return FluidVariantAttributeHandler.super.getName(fluidVariant); + } + } + @Override public Optional<SoundEvent> getEmptySound(FluidVariant variant) { return Optional.of(SoundEvents.ITEM_BUCKET_EMPTY); } }); register(Fluids.LAVA, new FluidVariantAttributeHandler() { + @Override + public Text getName(FluidVariant fluidVariant) { + if (coloredVanillaFluidNames) { + return Blocks.LAVA.getName().setStyle(Style.EMPTY.withColor(Formatting.RED)); + } else { + return FluidVariantAttributeHandler.super.getName(fluidVariant); + } + } + @Override public Optional<SoundEvent> getFillSound(FluidVariant variant) { return Optional.of(SoundEvents.ITEM_BUCKET_FILL_LAVA); 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 new file mode 100644 index 000000000..e30742af5 --- /dev/null +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/fluid/base/SingleFluidStorage.java @@ -0,0 +1,78 @@ +/* + * 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.api.transfer.v1.fluid.base; + +import java.util.Objects; + +import org.jetbrains.annotations.ApiStatus; + +import net.minecraft.nbt.NbtCompound; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions; +import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage; + +/** + * A storage that can store a single fluid variant at any given time. + * Implementors should at least override {@link #getCapacity(FluidVariant)}, + * and probably {@link #onFinalCommit} as well for {@code markDirty()} and similar calls. + * + * <p>This is a convenient specialization of {@link SingleVariantStorage} for fluids that additionally offers methods + * to read the contents of the storage from NBT. + * + * <p><b>Experimental feature</b>, we reserve the right to remove or change it without further notice. + * The transfer API is a complex addition, and we want to be able to correct possible design mistakes. + */ +@ApiStatus.Experimental +public abstract class SingleFluidStorage extends SingleVariantStorage<FluidVariant> { + /** + * Create a fluid storage with a fixed capacity and a change handler. + * + * @param capacity Fixed capacity of the fluid storage. Must be nonnegative. + * @param onChange Change handler, generally for {@code markDirty()} or similar calls. May not be null. + */ + public static SingleFluidStorage withFixedCapacity(long capacity, Runnable onChange) { + StoragePreconditions.notNegative(capacity); + Objects.requireNonNull(onChange, "onChange may not be null"); + + return new SingleFluidStorage() { + @Override + protected long getCapacity(FluidVariant variant) { + return capacity; + } + + @Override + protected void onFinalCommit() { + onChange.run(); + } + }; + } + + @Override + protected final FluidVariant getBlankVariant() { + return FluidVariant.blank(); + } + + /** + * 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"); + } +} 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 new file mode 100644 index 000000000..c1141420f --- /dev/null +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/item/base/SingleItemStorage.java @@ -0,0 +1,52 @@ +/* + * 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.api.transfer.v1.item.base; + +import org.jetbrains.annotations.ApiStatus; + +import net.minecraft.nbt.NbtCompound; + +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage; + +/** + * A storage that can store a single item variant at any given time. + * Implementors should at least override {@link #getCapacity(ItemVariant)}, + * and probably {@link #onFinalCommit} as well for {@code markDirty()} and similar calls. + * + * <p>This is a convenient specialization of {@link SingleVariantStorage} for items that additionally offers methods + * to read the contents of the storage from NBT. + * + * <p><b>Experimental feature</b>, we reserve the right to remove or change it without further notice. + * The transfer API is a complex addition, and we want to be able to correct possible design mistakes. + */ +@ApiStatus.Experimental +public abstract class SingleItemStorage extends SingleVariantStorage<ItemVariant> { + @Override + protected final ItemVariant getBlankVariant() { + return ItemVariant.blank(); + } + + /** + * 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"); + } +} diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java index 2aa70bfaa..d0c3ee704 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java @@ -159,18 +159,23 @@ public interface Storage<T> extends Iterable<StorageView<T>> { * If returning the requested view would require iteration through a potentially large number of views, * {@code null} should be returned instead. * - * <p>The returned view is tied to the passed transaction, - * and may never be used once the passed transaction has been closed. - * - * @param transaction The transaction to which the scope of the returned storage view is tied. * @param resource The resource for which a storage view is requested. May be blank, for example to estimate capacity. * @return A view over this storage for the passed resource, or {@code null} if none is quickly available. */ @Nullable - default StorageView<T> exactView(TransactionContext transaction, T resource) { + default StorageView<T> exactView(T resource) { return null; } + /** + * @deprecated Use and implement the overload without the transaction parameter. + */ + @Deprecated(forRemoval = true) + @Nullable + default StorageView<T> exactView(TransactionContext transaction, T resource) { + return exactView(resource); + } + /** * Return an integer representing the current version of this storage instance to allow for fast change detection: * if the version hasn't changed since the last time, <b>and the storage instance is the same</b>, the storage has the same contents. diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/StorageView.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/StorageView.java index 11518d95c..56a5db98f 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/StorageView.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/StorageView.java @@ -23,8 +23,6 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; /** * A view of a single stored resource in a {@link Storage}, for use with {@link Storage#iterator} or {@link Storage#exactView}. * - * <p>A view is always tied to a specific transaction, and should not be accessed outside of it. - * * @param <T> The type of the stored resource. * * <b>Experimental feature</b>, we reserve the right to remove or change it without further notice. 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 67b0a03ce..9cd879522 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 @@ -92,6 +92,14 @@ public interface TransferVariant<O> { 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}. * diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/FilteringStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/FilteringStorage.java index 05bd7c4df..506388047 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/FilteringStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/FilteringStorage.java @@ -165,6 +165,19 @@ public abstract class FilteringStorage<T> implements Storage<T> { return Iterators.transform(backingStorage.get().iterator(), FilteringStorageView::new); } + @Override + @Nullable + public StorageView<T> exactView(T resource) { + StorageView<T> exact = backingStorage.get().exactView(resource); + + if (exact != null) { + return new FilteringStorageView(exact); + } else { + return null; + } + } + + @Deprecated(forRemoval = true) @Override @Nullable public StorageView<T> exactView(TransactionContext transaction, T resource) { diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/InsertionOnlyStorage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/InsertionOnlyStorage.java index 2fa5f9ad2..79b29d729 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/InsertionOnlyStorage.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/base/InsertionOnlyStorage.java @@ -16,13 +16,17 @@ package net.fabricmc.fabric.api.transfer.v1.storage.base; +import java.util.Collections; +import java.util.Iterator; + import org.jetbrains.annotations.ApiStatus; import net.fabricmc.fabric.api.transfer.v1.storage.Storage; +import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; /** - * A {@link Storage} that supports insertion, and not extraction. + * A {@link Storage} that supports insertion, and not extraction. By default, it doesn't have any storage view either. * * <p><b>Experimental feature</b>, we reserve the right to remove or change it without further notice. * The transfer API is a complex addition, and we want to be able to correct possible design mistakes. @@ -38,4 +42,9 @@ public interface InsertionOnlyStorage<T> extends Storage<T> { default long extract(T resource, long maxAmount, TransactionContext transaction) { return 0; } + + @Override + default Iterator<StorageView<T>> iterator() { + return Collections.emptyIterator(); + } } 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 6dc36355b..759de3086 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 @@ -18,6 +18,8 @@ package net.fabricmc.fabric.api.transfer.v1.storage.base; import org.jetbrains.annotations.ApiStatus; +import net.minecraft.nbt.NbtCompound; + import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; @@ -34,6 +36,9 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant; * * <p><b>Experimental feature</b>, we reserve the right to remove or change it without further notice. * The transfer API is a complex addition, and we want to be able to correct possible design mistakes. + * + * @see net.fabricmc.fabric.api.transfer.v1.fluid.base.SingleFluidStorage SingleFluidStorage for fluid variants. + * @see net.fabricmc.fabric.api.transfer.v1.item.base.SingleItemStorage SingleItemStorage for item variants. */ @ApiStatus.Experimental public abstract class SingleVariantStorage<T extends TransferVariant<?>> extends SnapshotParticipant<ResourceAmount<T>> implements SingleSlotStorage<T> { @@ -65,6 +70,15 @@ 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); diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ComposterWrapper.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ComposterWrapper.java index a0b10da6a..19b5b1fd4 100644 --- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ComposterWrapper.java +++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/item/ComposterWrapper.java @@ -18,8 +18,6 @@ package net.fabricmc.fabric.impl.transfer.item; import static net.minecraft.util.math.Direction.UP; -import java.util.Collections; -import java.util.Iterator; import java.util.Map; import java.util.Objects; @@ -39,7 +37,6 @@ import net.minecraft.world.WorldEvents; 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.StoragePreconditions; -import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; import net.fabricmc.fabric.api.transfer.v1.storage.base.ExtractionOnlyStorage; import net.fabricmc.fabric.api.transfer.v1.storage.base.InsertionOnlyStorage; import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage; @@ -152,11 +149,6 @@ public class ComposterWrapper extends SnapshotParticipant<Float> { increaseProbability = insertedIncreaseProbability; return 1; } - - @Override - public Iterator<StorageView<ItemVariant>> iterator() { - return Collections.emptyIterator(); - } } private class BottomStorage implements ExtractionOnlyStorage<ItemVariant>, SingleSlotStorage<ItemVariant> { diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlock.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlock.java index ed3f186c8..cfde70216 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlock.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/FluidChuteBlock.java @@ -26,12 +26,20 @@ import net.minecraft.block.ShapeContext; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityTicker; import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.BlockView; import net.minecraft.world.World; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorageUtil; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributes; + public class FluidChuteBlock extends Block implements BlockEntityProvider { public FluidChuteBlock() { super(Settings.of(Material.METAL)); @@ -55,4 +63,20 @@ public class FluidChuteBlock extends Block implements BlockEntityProvider { public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { return SHAPE; } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if (!world.isClient() && world.getBlockEntity(pos) instanceof FluidChuteBlockEntity chute) { + if (!FluidStorageUtil.interactWithFluidStorage(chute.storage, player, hand)) { + player.sendMessage( + Text.literal("Fluid: ") + .append(FluidVariantAttributes.getName(chute.storage.variant)) + .append(", amount: " + chute.storage.amount), + false + ); + } + } + + return ActionResult.success(world.isClient()); + } } 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 2cc6411b4..d88825fdd 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 @@ -18,16 +18,18 @@ package net.fabricmc.fabric.test.transfer.ingame; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage; -import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.fluid.base.SingleFluidStorage; import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil; -import net.fabricmc.fabric.api.transfer.v1.storage.Storage; public class FluidChuteBlockEntity extends BlockEntity { + final SingleFluidStorage storage = SingleFluidStorage.withFixedCapacity(FluidConstants.BUCKET * 4, this::markDirty); + private int tickCounter = 0; public FluidChuteBlockEntity(BlockPos pos, BlockState state) { @@ -37,12 +39,32 @@ public class FluidChuteBlockEntity extends BlockEntity { @SuppressWarnings("ConstantConditions") public void tick() { if (!world.isClient() && tickCounter++ % 20 == 0) { - Storage<FluidVariant> top = FluidStorage.SIDED.find(world, pos.offset(Direction.UP), Direction.DOWN); - Storage<FluidVariant> bottom = FluidStorage.SIDED.find(world, pos.offset(Direction.DOWN), Direction.UP); - - if (top != null && bottom != null) { - StorageUtil.move(top, bottom, fluid -> true, FluidConstants.BUCKET, null); - } + StorageUtil.move( + FluidStorage.SIDED.find(world, pos.offset(Direction.UP), Direction.DOWN), + storage, + fluid -> true, + FluidConstants.BUCKET, + null + ); + StorageUtil.move( + storage, + FluidStorage.SIDED.find(world, pos.offset(Direction.DOWN), Direction.UP), + fluid -> true, + FluidConstants.BUCKET, + null + ); } } + + @Override + protected void writeNbt(NbtCompound nbt) { + super.writeNbt(nbt); + storage.writeNbt(nbt); + } + + @Override + public void readNbt(NbtCompound nbt) { + super.readNbt(nbt); + storage.readNbt(nbt); + } } diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/TrashingStorage.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/TrashingStorage.java index 69dbe2a49..86f5c79ca 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/TrashingStorage.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/TrashingStorage.java @@ -16,12 +16,8 @@ package net.fabricmc.fabric.test.transfer.ingame; -import java.util.Collections; -import java.util.Iterator; - import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions; -import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; import net.fabricmc.fabric.api.transfer.v1.storage.base.InsertionOnlyStorage; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; @@ -36,9 +32,4 @@ public class TrashingStorage<T extends TransferVariant<?>> implements InsertionO // Insertion always succeeds. return maxAmount; } - - @Override - public Iterator<StorageView<T>> iterator() { - return Collections.emptyIterator(); - } } diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java index 2a3df3bb4..687e9c871 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java @@ -40,6 +40,7 @@ import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributes; /** * Renders the water sprite in the top left of the screen, to make sure that it correctly depends on the position. @@ -47,6 +48,8 @@ import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; public class FluidVariantRenderTest implements ClientModInitializer { @Override public void onInitializeClient() { + FluidVariantAttributes.enableColoredVanillaFluidNames(); + HudRenderCallback.EVENT.register((matrices, tickDelta) -> { PlayerEntity player = MinecraftClient.getInstance().player; if (player == null) return;