diff --git a/common/src/main/java/dev/architectury/fluid/FluidStack.java b/common/src/main/java/dev/architectury/fluid/FluidStack.java index b522c32c..b64560c3 100644 --- a/common/src/main/java/dev/architectury/fluid/FluidStack.java +++ b/common/src/main/java/dev/architectury/fluid/FluidStack.java @@ -183,6 +183,10 @@ public final class FluidStack { return getFluid() == other.getFluid() && getAmount() == other.getAmount() && isTagEqual(other); } + public boolean isFluidEqual(FluidStack other) { + return getFluid() == other.getFluid(); + } + private boolean isTagEqual(FluidStack other) { return tag == null ? other.tag == null : other.tag != null && tag.equals(other.tag); } @@ -203,6 +207,11 @@ public final class FluidStack { return FluidStackHooks.write(this, tag); } + public FluidStack copyWithAmount(long amount) { + if (isEmpty()) return this; + return new FluidStack(fluid, amount, tag); + } + @Environment(EnvType.CLIENT) @Nullable public TextureAtlasSprite getStillTexture() { diff --git a/common/src/main/java/dev/architectury/transfer/EmptyTransferHandler.java b/common/src/main/java/dev/architectury/transfer/EmptyTransferHandler.java index 65927146..4216b3e1 100644 --- a/common/src/main/java/dev/architectury/transfer/EmptyTransferHandler.java +++ b/common/src/main/java/dev/architectury/transfer/EmptyTransferHandler.java @@ -19,23 +19,54 @@ package dev.architectury.transfer; -import java.util.Collections; +import dev.architectury.fluid.FluidStack; +import net.minecraft.world.item.ItemStack; -enum EmptyTransferHandler implements TransferHandler { - INSTANCE; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +class EmptyTransferHandler implements TransferHandler { + static final TransferHandler ITEM = new EmptyTransferHandler<>(() -> ItemStack.EMPTY); + static final TransferHandler FLUID = new EmptyTransferHandler<>(FluidStack::empty); + private final Supplier blank; - @Override - public Iterable> getResources(TransferContext context) { - return Collections.emptyList(); + protected EmptyTransferHandler(Supplier blank) { + this.blank = blank; } @Override - public long insert(Object toInsert, TransferAction action, TransferContext context) { + public Stream> getContents() { + return Stream.empty(); + } + + @Override + public int getContentsSize() { return 0; } @Override - public long extract(Object toExtract, TransferAction action, TransferContext context) { + public ResourceView getContent(int index) { + throw new IndexOutOfBoundsException(index); + } + + @Override + public long insert(T toInsert, TransferAction action) { return 0; } + + @Override + public T extract(T toExtract, TransferAction action) { + return blank(); + } + + @Override + public T extract(Predicate toExtract, long maxAmount, TransferAction action) { + return blank(); + } + + @Override + public T blank() { + return blank.get(); + } } diff --git a/common/src/main/java/dev/architectury/transfer/ForwardingTransferHandler.java b/common/src/main/java/dev/architectury/transfer/ForwardingTransferHandler.java new file mode 100644 index 00000000..21abad3b --- /dev/null +++ b/common/src/main/java/dev/architectury/transfer/ForwardingTransferHandler.java @@ -0,0 +1,64 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +public interface ForwardingTransferHandler extends TransferHandler { + TransferHandler forwardingTo(); + + @Override + default Stream> getContents() { + return forwardingTo().getContents(); + } + + @Override + @Deprecated + default int getContentsSize() { + return forwardingTo().getContentsSize(); + } + + @Override + @Deprecated + default ResourceView getContent(int index) { + return forwardingTo().getContent(index); + } + + @Override + default long insert(T toInsert, TransferAction action) { + return forwardingTo().insert(toInsert, action); + } + + @Override + default T extract(T toExtract, TransferAction action) { + return forwardingTo().extract(toExtract, action); + } + + @Override + default T extract(Predicate toExtract, long maxAmount, TransferAction action) { + return forwardingTo().extract(toExtract, maxAmount, action); + } + + @Override + default T blank() { + return forwardingTo().blank(); + } +} diff --git a/common/src/main/java/dev/architectury/transfer/TransferAccess.java b/common/src/main/java/dev/architectury/transfer/TransferAccess.java new file mode 100644 index 00000000..af9f9ce7 --- /dev/null +++ b/common/src/main/java/dev/architectury/transfer/TransferAccess.java @@ -0,0 +1,23 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer; + +public interface TransferAccess { +} diff --git a/common/src/main/java/dev/architectury/transfer/TransferAction.java b/common/src/main/java/dev/architectury/transfer/TransferAction.java index 50762013..de20a70b 100644 --- a/common/src/main/java/dev/architectury/transfer/TransferAction.java +++ b/common/src/main/java/dev/architectury/transfer/TransferAction.java @@ -21,6 +21,6 @@ package dev.architectury.transfer; public enum TransferAction { SIMULATE, - COMMIT, + ACT, ; } diff --git a/common/src/main/java/dev/architectury/transfer/TransferHandler.java b/common/src/main/java/dev/architectury/transfer/TransferHandler.java index 9cff017d..bec01c03 100644 --- a/common/src/main/java/dev/architectury/transfer/TransferHandler.java +++ b/common/src/main/java/dev/architectury/transfer/TransferHandler.java @@ -19,8 +19,13 @@ package dev.architectury.transfer; +import dev.architectury.fluid.FluidStack; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.ApiStatus; +import java.util.function.Predicate; +import java.util.stream.Stream; + /** * A handler for transferring resources. * This is wrapped around apis given by the platform, @@ -33,40 +38,82 @@ import org.jetbrains.annotations.ApiStatus; @ApiStatus.NonExtendable public interface TransferHandler { /** - * Returns an empty transfer handler, which does nothing. + * Returns an empty item transfer handler, which does nothing. * - * @param the type of resource - * @return an empty transfer handler + * @return an empty item transfer handler */ - static TransferHandler empty() { - return (TransferHandler) EmptyTransferHandler.INSTANCE; + static TransferHandler emptyItem() { + return EmptyTransferHandler.ITEM; } /** - * Returns the iterable of immutable resources that are currently in the handler. + * Returns an empty fluid transfer handler, which does nothing. + * + * @return an empty fluid transfer handler + */ + static TransferHandler emptyFluid() { + return EmptyTransferHandler.FLUID; + } + + /** + * Returns the iterable of immutable resources that are currently in the handler.
+ * Please properly close this stream. Failure to do so will result in a potential + * crash in conflicting transactions. * - * @param context the context of the transfer * @return the iterable of resources that are currently in the handler */ - Iterable> getResources(TransferContext context); + Stream> getContents(); + + /** + * Returns the size of the handler. + * This may be extremely expensive to compute, avoid if you can. + * + * @return the size of the handler + */ + @Deprecated + int getContentsSize(); + + /** + * Returns the resource in a particular index. + * This may be extremely expensive to compute, avoid if you can. + * + * @param index the index of the resource + * @return the resource in the given index + */ + @Deprecated + ResourceView getContent(int index); /** * Inserts the given resource into the handler, returning the amount that was inserted. * * @param toInsert the resource to insert * @param action whether to simulate or actually insert the resource - * @param context the context of the transfer * @return the amount that was inserted */ - long insert(T toInsert, TransferAction action, TransferContext context); + long insert(T toInsert, TransferAction action); /** - * Extracts the given resource from the handler, returning the amount that was extracted. + * Extracts the given resource from the handler, returning the stack that was extracted. * * @param toExtract the resource to extract * @param action whether to simulate or actually extract the resource - * @param context the context of the transfer - * @return the amount that was extracted + * @return the stack that was extracted */ - long extract(T toExtract, TransferAction action, TransferContext context); + T extract(T toExtract, TransferAction action); + + /** + * Extracts the given resource from the handler, returning the stack that was extracted. + * + * @param toExtract the predicates to use to filter the resources to extract + * @param action whether to simulate or actually extract the resource + * @return the stack that was extracted + */ + T extract(Predicate toExtract, long maxAmount, TransferAction action); + + /** + * Returns a blank resource. + * + * @return a blank resource + */ + T blank(); } diff --git a/common/src/main/java/dev/architectury/transfer/access/BlockTransferAccess.java b/common/src/main/java/dev/architectury/transfer/access/BlockTransferAccess.java new file mode 100644 index 00000000..dd786256 --- /dev/null +++ b/common/src/main/java/dev/architectury/transfer/access/BlockTransferAccess.java @@ -0,0 +1,50 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.access; + +import dev.architectury.transfer.TransferAccess; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public interface BlockTransferAccess extends TransferAccess { + @Nullable + T get(Level level, BlockPos pos, C context); + + @Nullable + T get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, C context); + + void register(ResourceLocation id, BlockAccessProvider provider); + + @FunctionalInterface + interface BlockAccessProvider { + @Nullable + BlockAccessApplicator get(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity); + } + + @FunctionalInterface + interface BlockAccessApplicator { + @Nullable + T get(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity, @Nullable C context); + } +} diff --git a/common/src/main/java/dev/architectury/transfer/access/ItemTransferAccess.java b/common/src/main/java/dev/architectury/transfer/access/ItemTransferAccess.java new file mode 100644 index 00000000..c5b1051f --- /dev/null +++ b/common/src/main/java/dev/architectury/transfer/access/ItemTransferAccess.java @@ -0,0 +1,30 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.access; + +import dev.architectury.transfer.TransferAccess; +import dev.architectury.transfer.TransferHandler; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +public interface ItemTransferAccess extends TransferAccess { + @Nullable + T get(ItemStack stack, C context); +} diff --git a/common/src/main/java/dev/architectury/transfer/fluid/FluidTransfer.java b/common/src/main/java/dev/architectury/transfer/fluid/FluidTransfer.java index c3675628..2828c4cf 100644 --- a/common/src/main/java/dev/architectury/transfer/fluid/FluidTransfer.java +++ b/common/src/main/java/dev/architectury/transfer/fluid/FluidTransfer.java @@ -22,25 +22,26 @@ package dev.architectury.transfer.fluid; import dev.architectury.fluid.FluidStack; import dev.architectury.injectables.annotations.ExpectPlatform; import dev.architectury.transfer.TransferHandler; -import net.minecraft.core.BlockPos; +import dev.architectury.transfer.access.BlockTransferAccess; +import dev.architectury.transfer.access.ItemTransferAccess; import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; public class FluidTransfer { private FluidTransfer() { } + public static final BlockTransferAccess, Direction> BLOCK = instantiateBlockAccess(); + public static final ItemTransferAccess, TransferHandler> ITEM = instantiateItemAccess(); + @ExpectPlatform - @Nullable - public static TransferHandler get(Level level, BlockPos pos, Direction direction) { + private static BlockTransferAccess, Direction> instantiateBlockAccess() { throw new AssertionError(); } @ExpectPlatform - @Nullable - public static TransferHandler get(Level level, BlockPos pos, @Nullable BlockEntity blockEntity, Direction direction) { + private static ItemTransferAccess, TransferHandler> instantiateItemAccess() { throw new AssertionError(); } diff --git a/common/src/main/java/dev/architectury/transfer/TransferContext.java b/common/src/main/java/dev/architectury/transfer/item/ItemTransfer.java similarity index 50% rename from common/src/main/java/dev/architectury/transfer/TransferContext.java rename to common/src/main/java/dev/architectury/transfer/item/ItemTransfer.java index f4e7827b..8fc125a1 100644 --- a/common/src/main/java/dev/architectury/transfer/TransferContext.java +++ b/common/src/main/java/dev/architectury/transfer/item/ItemTransfer.java @@ -17,37 +17,35 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package dev.architectury.transfer; +package dev.architectury.transfer.item; import dev.architectury.injectables.annotations.ExpectPlatform; -import dev.architectury.injectables.annotations.PlatformOnly; +import dev.architectury.transfer.TransferHandler; +import dev.architectury.transfer.access.BlockTransferAccess; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; -/** - * Transfer context is used to defer the state of the transaction. - *

- * On Fabric, each thread can only have one transaction at a time, - * you can create a new context with {@link #create()}, this will instantiate - * a Transaction on Fabric. - *

- * If you wish to create a context with a transaction, you can use - * {@link #create(Object)} with the transaction object, this method - * is only available on Fabric. - *

- * This class must be closed with {@link #close()}, you can use try-and-resources - * block to ensure that the context is closed. - */ -public interface TransferContext extends AutoCloseable { +public class ItemTransfer { + public static final BlockTransferAccess, Direction> BLOCK = instantiateBlockAccess(); + @ExpectPlatform - static TransferContext create() { + private static BlockTransferAccess, Direction> instantiateBlockAccess() { throw new AssertionError(); } - @PlatformOnly(PlatformOnly.FABRIC) + /** + * Wraps a platform-specific item transfer handler into the architectury transfer handler. + * This accepts {@code IItemHandler} on Forge. + * This accepts {@code Storage} or {@code ContainerItemContext} on Fabric. + * + * @param object the handler to wrap + * @return the wrapped handler, or {@code null} if {@code object} is null + * @throws IllegalArgumentException if {@code object} is not a supported handler + */ @ExpectPlatform - static TransferContext create(@Nullable Object transaction) { + @Nullable + public static TransferHandler wrap(@Nullable Object object) { throw new AssertionError(); } - - int nestingDepth(); } diff --git a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java index 5dc48fa6..24b6f882 100644 --- a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java +++ b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java @@ -21,11 +21,16 @@ package dev.architectury.hooks.fluid.fabric; import dev.architectury.fluid.FluidStack; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; public final class FluidStackHooksFabric { private FluidStackHooksFabric() { } + public static FluidStack fromFabric(StorageView storageView) { + return fromFabric(storageView.getResource(), storageView.getAmount()); + } + public static FluidStack fromFabric(FluidVariant variant, long amount) { return FluidStack.create(variant.getFluid(), amount, variant.getNbt()); } diff --git a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java index 6089431b..571cd3c8 100644 --- a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java +++ b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java @@ -26,6 +26,7 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry; import net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; @@ -106,7 +107,7 @@ public class FluidStackHooksImpl { } public static long bucketAmount() { - return 81000; + return FluidConstants.BLOCK; } @Environment(EnvType.CLIENT) diff --git a/fabric/src/main/java/dev/architectury/transfer/fabric/FabricBlockTransferAccess.java b/fabric/src/main/java/dev/architectury/transfer/fabric/FabricBlockTransferAccess.java new file mode 100644 index 00000000..4e2511e8 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/transfer/fabric/FabricBlockTransferAccess.java @@ -0,0 +1,57 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.fabric; + +import dev.architectury.transfer.TransferHandler; +import dev.architectury.transfer.access.BlockTransferAccess; +import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +public class FabricBlockTransferAccess implements BlockTransferAccess, C> { + private final BlockApiLookup lookup; + private final Function> wrapper; + + public FabricBlockTransferAccess(BlockApiLookup lookup, Function> wrapper) { + this.lookup = lookup; + this.wrapper = wrapper; + } + + @Override + @Nullable + public TransferHandler get(Level level, BlockPos pos, C context) { + return wrapper.apply(lookup.find(level, pos, context)); + } + + @Override + @Nullable + public TransferHandler get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, C context) { + if (blockEntity != null) { + return wrapper.apply(lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, context)); + } else { + return get(level, pos, context); + } + } +} diff --git a/fabric/src/main/java/dev/architectury/transfer/fabric/FabricStorageTransferHandler.java b/fabric/src/main/java/dev/architectury/transfer/fabric/FabricStorageTransferHandler.java new file mode 100644 index 00000000..ef614884 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/transfer/fabric/FabricStorageTransferHandler.java @@ -0,0 +1,201 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.fabric; + +import com.google.common.collect.Iterators; +import dev.architectury.transfer.ResourceView; +import dev.architectury.transfer.TransferAction; +import dev.architectury.transfer.TransferHandler; +import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage; +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; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToLongFunction; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class FabricStorageTransferHandler implements TransferHandler { + private final Storage storage; + @Nullable + private final Transaction transaction; + private final TypeAdapter typeAdapter; + + public FabricStorageTransferHandler(Storage storage, @Nullable Transaction transaction, TypeAdapter typeAdapter) { + this.storage = storage; + this.transaction = transaction; + this.typeAdapter = typeAdapter; + } + + @Override + public Stream> getContents() { + Transaction transaction = Transaction.openNested(this.transaction); + return StreamSupport.stream(storage.iterable(transaction).spliterator(), false) + .>map(FabricStorageResourceView::new) + .onClose(transaction::close); + } + + @Override + public int getContentsSize() { + if (storage instanceof InventoryStorage) { + return ((InventoryStorage) storage).getSlots().size(); + } + try (Transaction transaction = Transaction.openNested(this.transaction)) { + int size = 0; + for (StorageView view : storage.iterable(transaction)) { + size++; + } + return size; + } + } + + @Override + public ResourceView getContent(int index) { + if (storage instanceof InventoryStorage) { + return new FabricStorageResourceView((StorageView) ((InventoryStorage) storage).getSlots().get(index)); + } + try (Transaction transaction = Transaction.openNested(this.transaction)) { + return new FabricStorageResourceView(Iterators.get(storage.iterator(transaction), index)); + } + } + + @Override + public long insert(S toInsert, TransferAction action) { + long inserted; + + try (Transaction nested = Transaction.openNested(this.transaction)) { + inserted = this.storage.insert(toFabric(toInsert), getAmount(toInsert), nested); + + if (action == TransferAction.ACT) { + nested.commit(); + } + } + + return inserted; + } + + @Override + public S extract(S toExtract, TransferAction action) { + if (isEmpty(toExtract)) return blank(); + long extracted; + + try (Transaction nested = Transaction.openNested(this.transaction)) { + extracted = this.storage.extract(toFabric(toExtract), getAmount(toExtract), nested); + + if (action == TransferAction.ACT) { + nested.commit(); + } + } + + return copyWithAmount(toExtract, extracted); + } + + @Override + public S extract(Predicate toExtract, long maxAmount, TransferAction action) { + try (Transaction nested = Transaction.openNested(this.transaction)) { + for (StorageView view : this.storage.iterable(nested)) { + if (toExtract.test(fromFabric(view))) { + long extracted = view.extract(view.getResource(), maxAmount, nested); + + if (action == TransferAction.ACT) { + nested.commit(); + } + + return fromFabric(view.getResource(), extracted); + } + } + } + + return blank(); + } + + @Override + public S blank() { + return typeAdapter.blank.get(); + } + + private boolean isEmpty(S stack) { + return typeAdapter.isEmpty.test(stack); + } + + private long getAmount(S stack) { + return typeAdapter.toAmount.applyAsLong(stack); + } + + private F toFabric(S stack) { + return typeAdapter.toFabric.apply(stack); + } + + private S fromFabric(StorageView view) { + return fromFabric(view.getResource(), view.getAmount()); + } + + private S fromFabric(F variant, long amount) { + return typeAdapter.fromFabric.apply(variant, amount); + } + + private S copyWithAmount(S stack, long amount) { + return typeAdapter.copyWithAmount.apply(stack, amount); + } + + public interface FunctionWithAmount { + S apply(F variant, long amount); + } + + private class FabricStorageResourceView implements ResourceView { + private final StorageView view; + + private FabricStorageResourceView(StorageView view) { + this.view = view; + } + + @Override + public S getResource() { + return fromFabric(view.getResource(), view.getAmount()); + } + + @Override + public long getCapacity() { + return view.getCapacity(); + } + } + + public static class TypeAdapter { + private final Function toFabric; + private final FunctionWithAmount fromFabric; + private final FunctionWithAmount copyWithAmount; + private final Supplier blank; + private final Predicate isEmpty; + private final ToLongFunction toAmount; + + public TypeAdapter(Function toFabric, FunctionWithAmount fromFabric, FunctionWithAmount copyWithAmount, Supplier blank, Predicate isEmpty, ToLongFunction toAmount) { + this.toFabric = toFabric; + this.fromFabric = fromFabric; + this.copyWithAmount = copyWithAmount; + this.blank = blank; + this.isEmpty = isEmpty; + this.toAmount = toAmount; + } + } +} diff --git a/fabric/src/main/java/dev/architectury/transfer/fluid/fabric/FluidTransferImpl.java b/fabric/src/main/java/dev/architectury/transfer/fluid/fabric/FluidTransferImpl.java index 0ef7e30a..5a089715 100644 --- a/fabric/src/main/java/dev/architectury/transfer/fluid/fabric/FluidTransferImpl.java +++ b/fabric/src/main/java/dev/architectury/transfer/fluid/fabric/FluidTransferImpl.java @@ -19,112 +19,93 @@ package dev.architectury.transfer.fluid.fabric; -import com.google.common.collect.Iterables; import dev.architectury.fluid.FluidStack; import dev.architectury.hooks.fluid.fabric.FluidStackHooksFabric; -import dev.architectury.transfer.ResourceView; -import dev.architectury.transfer.TransferAction; -import dev.architectury.transfer.TransferContext; import dev.architectury.transfer.TransferHandler; -import dev.architectury.transfer.fabric.TransferContextImpl; +import dev.architectury.transfer.access.BlockTransferAccess; +import dev.architectury.transfer.access.ItemTransferAccess; +import dev.architectury.transfer.fabric.FabricBlockTransferAccess; +import dev.architectury.transfer.fabric.FabricStorageTransferHandler; +import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; 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.item.ItemVariant; 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; -import net.minecraft.core.BlockPos; +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.TransactionContext; import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToLongFunction; + public class FluidTransferImpl { - @Nullable - public static TransferHandler get(Level level, BlockPos pos, Direction direction) { - return wrap(FluidStorage.SIDED.find(level, pos, direction)); - } - - @Nullable - public static TransferHandler get(Level level, BlockPos pos, @Nullable BlockEntity blockEntity, Direction direction) { - if (blockEntity != null) { - return wrap(FluidStorage.SIDED.find(level, pos, blockEntity.getBlockState(), blockEntity, direction)); - } else { - return get(level, pos, direction); - } - } + private static final Function TO_FABRIC = FluidStackHooksFabric::toFabric; + private static final FabricStorageTransferHandler.FunctionWithAmount FROM_FABRIC = FluidStackHooksFabric::fromFabric; + private static final FabricStorageTransferHandler.FunctionWithAmount COPY_WITH_AMOUNT = FluidStack::copyWithAmount; + private static final Supplier BLANK = FluidStack::empty; + private static final Predicate IS_EMPTY = FluidStack::isEmpty; + private static final ToLongFunction TO_AMOUNT = FluidStack::getAmount; + private static final FabricStorageTransferHandler.TypeAdapter TYPE_ADAPTER = new FabricStorageTransferHandler.TypeAdapter<>(TO_FABRIC, FROM_FABRIC, COPY_WITH_AMOUNT, BLANK, IS_EMPTY, TO_AMOUNT); @Nullable public static TransferHandler wrap(@Nullable Object object) { if (object == null) return null; if (object instanceof Storage) { - return new FabricTransferHandler((Storage) object); + return new FabricStorageTransferHandler<>((Storage) object, null, TYPE_ADAPTER); } else { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } - private static class FabricTransferHandler implements TransferHandler { - private final Storage storage; - - private FabricTransferHandler(Storage storage) { - this.storage = storage; - } - - @Override - public Iterable> getResources(TransferContext context) { - return Iterables.transform(storage.iterable(((TransferContextImpl) context).transaction), - FabricResourceView::new); - } - - @Override - public long insert(FluidStack toInsert, TransferAction action, TransferContext context) { - Transaction transaction = ((TransferContextImpl) context).transaction; - long inserted; - - try (Transaction nested = Transaction.openNested(transaction)) { - inserted = this.storage.insert(FluidStackHooksFabric.toFabric(toInsert), toInsert.getAmount(), nested); - - if (action == TransferAction.COMMIT) { - nested.commit(); - } + public static BlockTransferAccess, Direction> instantiateBlockAccess() { + return new FabricBlockTransferAccess<>(FluidStorage.SIDED, FluidTransferImpl::wrap); + } + + public static ItemTransferAccess, TransferHandler> instantiateItemAccess() { + return new ItemTransferAccess, TransferHandler>() { + @Override + @Nullable + public TransferHandler get(ItemStack stack, TransferHandler context) { + return wrap(FluidStorage.ITEM.find(stack, fromTransfer(stack, context))); } - - return inserted; - } - - @Override - public long extract(FluidStack toExtract, TransferAction action, TransferContext context) { - Transaction transaction = ((TransferContextImpl) context).transaction; - long extracted; - - try (Transaction nested = Transaction.openNested(transaction)) { - extracted = this.storage.extract(FluidStackHooksFabric.toFabric(toExtract), toExtract.getAmount(), nested); - - if (action == TransferAction.COMMIT) { - nested.commit(); - } - } - - return extracted; - } - - private static class FabricResourceView implements ResourceView { - private final StorageView view; - - private FabricResourceView(StorageView view) { - this.view = view; + }; + } + + public static ContainerItemContext fromTransfer(ItemStack stack, TransferHandler transferHandler) { + SingleSlotStorage mainSlot = new SingleVariantStorage() { + @Override + protected ItemVariant getBlankVariant() { + return ItemVariant.blank(); } @Override - public FluidStack getResource() { - return FluidStackHooksFabric.fromFabric(view.getResource(), view.getAmount()); + protected long getCapacity(ItemVariant variant) { + // TODO Revisit this + return variant.getItem().getMaxStackSize(); + } + }; + return new ContainerItemContext() { + @Override + public SingleSlotStorage getMainSlot() { + return mainSlot; } @Override - public long getCapacity() { - return view.getCapacity(); + public List> getAdditionalSlots() { + return null; } - } + + @Override + public long insertOverflow(ItemVariant itemVariant, long maxAmount, TransactionContext transactionContext) { + return 0; + } + }; } } diff --git a/fabric/src/main/java/dev/architectury/transfer/item/fabric/FabricContainerItemTransferHandler.java b/fabric/src/main/java/dev/architectury/transfer/item/fabric/FabricContainerItemTransferHandler.java new file mode 100644 index 00000000..e2ec5164 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/transfer/item/fabric/FabricContainerItemTransferHandler.java @@ -0,0 +1,138 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.item.fabric; + +import com.google.common.collect.Iterables; +import dev.architectury.hooks.item.ItemStackHooks; +import dev.architectury.transfer.ResourceView; +import dev.architectury.transfer.TransferAction; +import dev.architectury.transfer.TransferHandler; +import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; +import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class FabricContainerItemTransferHandler implements TransferHandler { + private final ContainerItemContext context; + @Nullable + private final Transaction transaction; + + public FabricContainerItemTransferHandler(ContainerItemContext context, @Nullable Transaction transaction) { + this.context = context; + this.transaction = transaction; + } + + @Override + public Stream> getContents() { + return Stream.concat(Stream.of(context.getMainSlot()), context.getAdditionalSlots().stream()) + .map(FabricResourceView::new); + } + + @Override + public int getContentsSize() { + return 1 + context.getAdditionalSlots().size(); + } + + @Override + public ResourceView getContent(int index) { + if (index == 0) return new FabricResourceView(context.getMainSlot()); + return new FabricResourceView(context.getAdditionalSlots().get(index - 1)); + } + + @Override + public long insert(ItemStack toInsert, TransferAction action) { + long inserted; + + try (Transaction nested = Transaction.openNested(this.transaction)) { + inserted = this.context.insert(ItemVariant.of(toInsert), toInsert.getCount(), nested); + + if (action == TransferAction.ACT) { + nested.commit(); + } + } + + return inserted; + } + + @Override + public ItemStack extract(ItemStack toExtract, TransferAction action) { + if (toExtract.isEmpty()) return blank(); + long extracted; + + try (Transaction nested = Transaction.openNested(this.transaction)) { + extracted = this.context.extract(ItemVariant.of(toExtract), toExtract.getCount(), nested); + + if (action == TransferAction.ACT) { + nested.commit(); + } + } + + return ItemStackHooks.copyWithCount(toExtract, (int) extracted); + } + + @Override + public ItemStack extract(Predicate toExtract, long maxAmount, TransferAction action) { + try (Transaction nested = Transaction.openNested(this.transaction)) { + for (StorageView view : Iterables.concat(Collections.singletonList(context.getMainSlot()), context.getAdditionalSlots())) { + if (toExtract.test(view.getResource().toStack((int) view.getAmount()))) { + long extracted = view.extract(view.getResource(), maxAmount, nested); + + if (action == TransferAction.ACT) { + nested.commit(); + } + + return view.getResource().toStack((int) extracted); + } + } + } + + return blank(); + } + + @Override + public ItemStack blank() { + return ItemStack.EMPTY; + } + + private static class FabricResourceView implements ResourceView { + private final SingleSlotStorage storage; + + private FabricResourceView(SingleSlotStorage storage) { + this.storage = storage; + } + + @Override + public ItemStack getResource() { + return storage.getResource().toStack((int) storage.getAmount()); + } + + @Override + public long getCapacity() { + return storage.getCapacity(); + } + } +} diff --git a/fabric/src/main/java/dev/architectury/transfer/item/fabric/ItemTransferImpl.java b/fabric/src/main/java/dev/architectury/transfer/item/fabric/ItemTransferImpl.java new file mode 100644 index 00000000..63acd632 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/transfer/item/fabric/ItemTransferImpl.java @@ -0,0 +1,65 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.item.fabric; + +import dev.architectury.hooks.item.ItemStackHooks; +import dev.architectury.transfer.TransferHandler; +import dev.architectury.transfer.access.BlockTransferAccess; +import dev.architectury.transfer.fabric.FabricBlockTransferAccess; +import dev.architectury.transfer.fabric.FabricStorageTransferHandler; +import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; +import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage; +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.Storage; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToLongFunction; + +public class ItemTransferImpl { + private static final Function TO_FABRIC = ItemVariant::of; + private static final FabricStorageTransferHandler.FunctionWithAmount FROM_FABRIC = (variant, amount) -> variant.toStack((int) amount); + private static final FabricStorageTransferHandler.FunctionWithAmount COPY_WITH_AMOUNT = (stack, amount) -> ItemStackHooks.copyWithCount(stack, (int) amount); + private static final Supplier BLANK = () -> ItemStack.EMPTY; + private static final Predicate IS_EMPTY = ItemStack::isEmpty; + private static final ToLongFunction TO_AMOUNT = ItemStack::getCount; + private static final FabricStorageTransferHandler.TypeAdapter TYPE_ADAPTER = new FabricStorageTransferHandler.TypeAdapter<>(TO_FABRIC, FROM_FABRIC, COPY_WITH_AMOUNT, BLANK, IS_EMPTY, TO_AMOUNT); + + @Nullable + public static TransferHandler wrap(@Nullable Object object) { + if (object == null) return null; + + if (object instanceof Storage) { + return new FabricStorageTransferHandler<>((Storage) object, null, TYPE_ADAPTER); + } else if (object instanceof ContainerItemContext) { + return new FabricContainerItemTransferHandler((ContainerItemContext) object, null); + } else { + throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); + } + } + + public static BlockTransferAccess, Direction> instantiateBlockAccess() { + return new FabricBlockTransferAccess<>(ItemStorage.SIDED, ItemTransferImpl::wrap); + } +} diff --git a/forge/src/main/java/dev/architectury/transfer/fluid/forge/FluidTransferImpl.java b/forge/src/main/java/dev/architectury/transfer/fluid/forge/FluidTransferImpl.java index 8bce40f5..e6157864 100644 --- a/forge/src/main/java/dev/architectury/transfer/fluid/forge/FluidTransferImpl.java +++ b/forge/src/main/java/dev/architectury/transfer/fluid/forge/FluidTransferImpl.java @@ -23,8 +23,10 @@ import dev.architectury.fluid.FluidStack; import dev.architectury.hooks.fluid.forge.FluidStackHooksForge; import dev.architectury.transfer.ResourceView; import dev.architectury.transfer.TransferAction; -import dev.architectury.transfer.TransferContext; import dev.architectury.transfer.TransferHandler; +import dev.architectury.transfer.access.BlockTransferAccess; +import dev.architectury.transfer.access.ItemTransferAccess; +import dev.architectury.transfer.forge.ForgeBlockTransferAccess; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; @@ -33,49 +35,25 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.BucketPickup; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fluids.IFluidBlock; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.wrappers.BucketPickupHandlerWrapper; import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Spliterators; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; public class FluidTransferImpl { - @Nullable - public static TransferHandler get(Level level, BlockPos pos, Direction direction) { - return get(level, pos, null, direction); - } - - @Nullable - public static TransferHandler get(Level level, BlockPos pos, @Nullable BlockEntity blockEntity, Direction direction) { - BlockState state = level.getBlockState(pos); - Block block = state.getBlock(); - IFluidHandler handler = null; - if (block instanceof IFluidBlock) { - handler = new FluidBlockWrapper((IFluidBlock) block, level, pos); - } else if (block instanceof BucketPickup) { - handler = new BucketPickupHandlerWrapper((BucketPickup) block, level, pos); - } else if (state.hasBlockEntity()) { - if (blockEntity == null) { - blockEntity = level.getBlockEntity(pos); - } - if (blockEntity != null) { - handler = blockEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, direction).resolve().orElse(null); - } - } - return wrap(handler); - } - - @Nullable - public static TransferHandler get(ItemStack stack) { - return wrap(stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).resolve().orElse(null)); - } - @Nullable public static TransferHandler wrap(@Nullable Object object) { if (object == null) return null; @@ -87,6 +65,104 @@ public class FluidTransferImpl { } } + public static BlockTransferAccess, Direction> instantiateBlockAccess() { + return new ForgeBlockTransferAccess, IFluidHandler>() { + @Override + @Nullable + public TransferHandler get(Level level, BlockPos pos, Direction direction) { + return get(level, pos, level.getBlockState(pos), null, direction); + } + + @Override + @Nullable + public TransferHandler get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, Direction direction) { + Block block = state.getBlock(); + IFluidHandler handler = null; + if (block instanceof IFluidBlock) { + handler = new FluidBlockWrapper((IFluidBlock) block, level, pos); + } else if (block instanceof BucketPickup) { + handler = new BucketPickupHandlerWrapper((BucketPickup) block, level, pos); + } else if (state.hasBlockEntity()) { + if (blockEntity == null) { + blockEntity = level.getBlockEntity(pos); + } + if (blockEntity != null) { + handler = blockEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, direction).resolve().orElse(null); + } + } + return wrap(handler); + } + + @Override + public Capability getCapability() { + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY; + } + + @Override + public IFluidHandler from(TransferHandler handler) { + return new ArchFluidHandler(handler); + } + }; + } + + public static ItemTransferAccess, TransferHandler> instantiateItemAccess() { + return new ItemTransferAccess, TransferHandler>() { + @Override + @Nullable + public TransferHandler get(ItemStack stack, TransferHandler context) { + return wrap(stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).resolve().orElse(null)); + } + }; + } + + public static class ArchFluidHandler implements IFluidHandler { + private static final Predicate TRUE = stack -> true; + private TransferHandler handler; + + public ArchFluidHandler(TransferHandler handler) { + this.handler = handler; + } + + @Override + public int getTanks() { + return handler.getContentsSize(); + } + + @NotNull + @Override + public net.minecraftforge.fluids.FluidStack getFluidInTank(int index) { + return FluidStackHooksForge.toForge(handler.getContent(index).getResource()); + } + + @Override + public int getTankCapacity(int index) { + return (int) handler.getContent(index).getCapacity(); + } + + @Override + public boolean isFluidValid(int index, @NotNull net.minecraftforge.fluids.FluidStack stack) { + FluidStack content = handler.getContent(index).getResource(); + return content.getFluid() == stack.getFluid() && Objects.equals(content.getTag(), stack.getTag()); + } + + @Override + public int fill(net.minecraftforge.fluids.FluidStack stack, FluidAction action) { + return (int) handler.insert(FluidStackHooksForge.fromForge(stack), getFluidAction(action)); + } + + @NotNull + @Override + public net.minecraftforge.fluids.FluidStack drain(net.minecraftforge.fluids.FluidStack stack, FluidAction action) { + return FluidStackHooksForge.toForge(handler.extract(FluidStackHooksForge.fromForge(stack), getFluidAction(action))); + } + + @NotNull + @Override + public net.minecraftforge.fluids.FluidStack drain(int maxAmount, FluidAction action) { + return FluidStackHooksForge.toForge(handler.extract(TRUE, maxAmount, getFluidAction(action))); + } + } + private static class ForgeTransferHandler implements TransferHandler { private IFluidHandler handler; @@ -95,18 +171,50 @@ public class FluidTransferImpl { } @Override - public Iterable> getResources(TransferContext context) { - return Itr::new; + public Stream> getContents() { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Itr(), 0), false); } @Override - public long insert(FluidStack toInsert, TransferAction action, TransferContext context) { - return handler.fill(FluidStackHooksForge.toForge(toInsert), action == TransferAction.SIMULATE ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE); + public int getContentsSize() { + return handler.getTanks(); } @Override - public long extract(FluidStack toExtract, TransferAction action, TransferContext context) { - return handler.drain(FluidStackHooksForge.toForge(toExtract), action == TransferAction.SIMULATE ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE).getAmount(); + public ResourceView getContent(int index) { + return new ForgeResourceView(index); + } + + @Override + public long insert(FluidStack toInsert, TransferAction action) { + return handler.fill(FluidStackHooksForge.toForge(toInsert), getFluidAction(action)); + } + + @Override + public FluidStack extract(FluidStack toExtract, TransferAction action) { + return FluidStackHooksForge.fromForge(handler.drain(FluidStackHooksForge.toForge(toExtract), getFluidAction(action))); + } + + @Override + public FluidStack extract(Predicate toExtract, long maxAmount, TransferAction action) { + for (int i = 0; i < handler.getTanks(); i++) { + net.minecraftforge.fluids.FluidStack forgeStack = handler.getFluidInTank(i); + FluidStack stack = FluidStackHooksForge.fromForge(forgeStack); + if (toExtract.test(stack)) { + net.minecraftforge.fluids.FluidStack copy = forgeStack.copy(); + copy.setAmount((int) maxAmount); + net.minecraftforge.fluids.FluidStack extracted = handler.drain(copy, getFluidAction(action)); + stack.setAmount(extracted.getAmount()); + return stack; + } + } + + return blank(); + } + + @Override + public FluidStack blank() { + return FluidStack.empty(); } private class Itr implements Iterator> { @@ -161,4 +269,12 @@ public class FluidTransferImpl { } } } + + private static IFluidHandler.FluidAction getFluidAction(TransferAction action) { + return action == TransferAction.SIMULATE ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE; + } + + private static TransferAction getFluidAction(IFluidHandler.FluidAction action) { + return action == IFluidHandler.FluidAction.SIMULATE ? TransferAction.SIMULATE : TransferAction.ACT; + } } diff --git a/forge/src/main/java/dev/architectury/transfer/forge/CapabilitiesAttachListeners.java b/forge/src/main/java/dev/architectury/transfer/forge/CapabilitiesAttachListeners.java new file mode 100644 index 00000000..caa78606 --- /dev/null +++ b/forge/src/main/java/dev/architectury/transfer/forge/CapabilitiesAttachListeners.java @@ -0,0 +1,47 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.forge; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class CapabilitiesAttachListeners { + private static final List> LISTENERS = new ArrayList<>(); + + static { + MinecraftForge.EVENT_BUS.register(CapabilitiesAttachListeners.class); + } + + @SubscribeEvent + public static void attach(AttachCapabilitiesEvent event) { + for (Consumer consumer : LISTENERS) { + consumer.accept(event); + } + } + + public static void add(Consumer consumer) { + LISTENERS.add(consumer); + } +} diff --git a/forge/src/main/java/dev/architectury/transfer/forge/ForgeBlockTransferAccess.java b/forge/src/main/java/dev/architectury/transfer/forge/ForgeBlockTransferAccess.java new file mode 100644 index 00000000..8ce35544 --- /dev/null +++ b/forge/src/main/java/dev/architectury/transfer/forge/ForgeBlockTransferAccess.java @@ -0,0 +1,61 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.forge; + +import dev.architectury.transfer.access.BlockTransferAccess; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ForgeBlockTransferAccess extends BlockTransferAccess { + Capability getCapability(); + + C from(T handler); + + @Override + default void register(ResourceLocation id, BlockAccessProvider provider) { + CapabilitiesAttachListeners.add(event -> { + if (event.getObject() instanceof BlockEntity) { + BlockEntity blockEntity = (BlockEntity) event.getObject(); + BlockAccessApplicator applicator = provider.get(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity); + if (applicator != null) { + event.addCapability(id, new ICapabilityProvider() { + @NotNull + @Override + public LazyOptional getCapability(@NotNull Capability capability, @Nullable Direction arg) { + if (capability == ForgeBlockTransferAccess.this.getCapability()) { + T handler = applicator.get(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, arg); + + return LazyOptional.of(() -> from(handler)).cast(); + } + + return LazyOptional.empty(); + } + }); + } + } + }); + } +} diff --git a/forge/src/main/java/dev/architectury/transfer/item/forge/ItemTransferImpl.java b/forge/src/main/java/dev/architectury/transfer/item/forge/ItemTransferImpl.java new file mode 100644 index 00000000..ae5ae860 --- /dev/null +++ b/forge/src/main/java/dev/architectury/transfer/item/forge/ItemTransferImpl.java @@ -0,0 +1,200 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.transfer.item.forge; + +import dev.architectury.fluid.FluidStack; +import dev.architectury.transfer.ResourceView; +import dev.architectury.transfer.TransferAction; +import dev.architectury.transfer.TransferHandler; +import dev.architectury.transfer.access.BlockTransferAccess; +import dev.architectury.transfer.forge.ForgeBlockTransferAccess; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemHandlerHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ItemTransferImpl { + @Nullable + public static TransferHandler wrap(@Nullable Object object) { + if (object == null) return null; + + if (object instanceof IItemHandler) { + return new ForgeTransferHandler((IItemHandler) object); + } else { + throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); + } + } + + public static BlockTransferAccess, Direction> instantiateBlockAccess() { + return new ForgeBlockTransferAccess, IItemHandler>() { + @Override + @Nullable + public TransferHandler get(Level level, BlockPos pos, Direction direction) { + return get(level, pos, level.getBlockState(pos), null, direction); + } + + @Override + @Nullable + public TransferHandler get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, Direction direction) { + Block block = state.getBlock(); + IItemHandler handler = null; + if (state.hasBlockEntity()) { + if (blockEntity == null) { + blockEntity = level.getBlockEntity(pos); + } + if (blockEntity != null) { + handler = blockEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, direction).resolve().orElse(null); + } + } + return wrap(handler); + } + + @Override + public Capability getCapability() { + return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY; + } + + @Override + public IItemHandler from(TransferHandler handler) { + return new ArchItemHandler(handler); + } + }; + } + + public static class ArchItemHandler implements IItemHandler { + private static final Predicate TRUE = stack -> true; + private TransferHandler handler; + + public ArchItemHandler(TransferHandler handler) { + this.handler = handler; + } + + @Override + public int getSlots() { + return handler.getContentsSize(); + } + + @NotNull + @Override + public ItemStack getStackInSlot(int index) { + return handler.getContent(index).getResource(); + } + + @Override + public int getSlotLimit(int index) { + return (int) handler.getContent(index).getCapacity(); + } + + @NotNull + @Override + public ItemStack insertItem(int index, @NotNull ItemStack arg, boolean simulate) { + return null; + } + + @NotNull + @Override + public ItemStack extractItem(int index, int maxAmount, boolean simulate) { + return null; + } + + @Override + public boolean isItemValid(int index, @NotNull ItemStack stack) { + ItemStack content = handler.getContent(index).getResource(); + return content.getItem() == stack.getItem() && Objects.equals(content.getTag(), stack.getTag()); + } + } + + private static class ForgeTransferHandler implements TransferHandler { + private IItemHandler handler; + + public ForgeTransferHandler(IItemHandler handler) { + this.handler = handler; + } + + @Override + public Stream> getContents() { + return IntStream.range(0, handler.getSlots()).mapToObj(ForgeResourceView::new); + } + + @Override + public int getContentsSize() { + return handler.getSlots(); + } + + @Override + public ResourceView getContent(int index) { + return new ForgeResourceView(index); + } + + @Override + public long insert(ItemStack toInsert, TransferAction action) { + int toInsertCount = toInsert.getCount(); + ItemStack left = ItemHandlerHelper.insertItemStacked(handler, toInsert, action == TransferAction.SIMULATE); + return toInsertCount - left.getCount(); + } + + @Override + public ItemStack extract(ItemStack toExtract, TransferAction action) { + return null; + } + + @Override + public ItemStack extract(Predicate toExtract, long maxAmount, TransferAction action) { + return null; + } + + @Override + public ItemStack blank() { + return ItemStack.EMPTY; + } + + private class ForgeResourceView implements ResourceView { + int index; + + public ForgeResourceView(int index) { + this.index = index; + } + + @Override + public ItemStack getResource() { + return handler.getStackInSlot(index); + } + + @Override + public long getCapacity() { + return handler.getSlotLimit(index); + } + } + } +}