Start on item transfer, work on forge reverse-abstraction

This commit is contained in:
shedaniel
2021-12-06 01:36:33 +08:00
parent c28bb6bb39
commit 5ee37d5091
21 changed files with 1293 additions and 168 deletions

View File

@@ -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() {

View File

@@ -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<Object> {
INSTANCE;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
class EmptyTransferHandler<T> implements TransferHandler<T> {
static final TransferHandler<ItemStack> ITEM = new EmptyTransferHandler<>(() -> ItemStack.EMPTY);
static final TransferHandler<FluidStack> FLUID = new EmptyTransferHandler<>(FluidStack::empty);
private final Supplier<T> blank;
@Override
public Iterable<ResourceView<Object>> getResources(TransferContext context) {
return Collections.emptyList();
protected EmptyTransferHandler(Supplier<T> blank) {
this.blank = blank;
}
@Override
public long insert(Object toInsert, TransferAction action, TransferContext context) {
public Stream<ResourceView<T>> getContents() {
return Stream.empty();
}
@Override
public int getContentsSize() {
return 0;
}
@Override
public long extract(Object toExtract, TransferAction action, TransferContext context) {
public ResourceView<T> 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<T> toExtract, long maxAmount, TransferAction action) {
return blank();
}
@Override
public T blank() {
return blank.get();
}
}

View File

@@ -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<T> extends TransferHandler<T> {
TransferHandler<T> forwardingTo();
@Override
default Stream<ResourceView<T>> getContents() {
return forwardingTo().getContents();
}
@Override
@Deprecated
default int getContentsSize() {
return forwardingTo().getContentsSize();
}
@Override
@Deprecated
default ResourceView<T> 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<T> toExtract, long maxAmount, TransferAction action) {
return forwardingTo().extract(toExtract, maxAmount, action);
}
@Override
default T blank() {
return forwardingTo().blank();
}
}

View File

@@ -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<T> {
}

View File

@@ -21,6 +21,6 @@ package dev.architectury.transfer;
public enum TransferAction {
SIMULATE,
COMMIT,
ACT,
;
}

View File

@@ -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<T> {
/**
* Returns an empty transfer handler, which does nothing.
* Returns an empty item transfer handler, which does nothing.
*
* @param <T> the type of resource
* @return an empty transfer handler
* @return an empty item transfer handler
*/
static <T> TransferHandler<T> empty() {
return (TransferHandler<T>) EmptyTransferHandler.INSTANCE;
static TransferHandler<ItemStack> 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<FluidStack> emptyFluid() {
return EmptyTransferHandler.FLUID;
}
/**
* Returns the iterable of immutable resources that are currently in the handler.<br>
* <b>Please properly close this stream.</b> 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<ResourceView<T>> getResources(TransferContext context);
Stream<ResourceView<T>> 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<T> 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<T> toExtract, long maxAmount, TransferAction action);
/**
* Returns a blank resource.
*
* @return a blank resource
*/
T blank();
}

View File

@@ -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<T, C> extends TransferAccess<T> {
@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<T, C> provider);
@FunctionalInterface
interface BlockAccessProvider<T, C> {
@Nullable
BlockAccessApplicator<T, C> get(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity);
}
@FunctionalInterface
interface BlockAccessApplicator<T, C> {
@Nullable
T get(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity, @Nullable C context);
}
}

View File

@@ -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<T, C> extends TransferAccess<T> {
@Nullable
T get(ItemStack stack, C context);
}

View File

@@ -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<TransferHandler<FluidStack>, Direction> BLOCK = instantiateBlockAccess();
public static final ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> ITEM = instantiateItemAccess();
@ExpectPlatform
@Nullable
public static TransferHandler<FluidStack> get(Level level, BlockPos pos, Direction direction) {
private static BlockTransferAccess<TransferHandler<FluidStack>, Direction> instantiateBlockAccess() {
throw new AssertionError();
}
@ExpectPlatform
@Nullable
public static TransferHandler<FluidStack> get(Level level, BlockPos pos, @Nullable BlockEntity blockEntity, Direction direction) {
private static ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemAccess() {
throw new AssertionError();
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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<TransferHandler<ItemStack>, Direction> BLOCK = instantiateBlockAccess();
@ExpectPlatform
static TransferContext create() {
private static BlockTransferAccess<TransferHandler<ItemStack>, 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<ItemVariant>} 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<ItemStack> wrap(@Nullable Object object) {
throw new AssertionError();
}
int nestingDepth();
}

View File

@@ -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<FluidVariant> storageView) {
return fromFabric(storageView.getResource(), storageView.getAmount());
}
public static FluidStack fromFabric(FluidVariant variant, long amount) {
return FluidStack.create(variant.getFluid(), amount, variant.getNbt());
}

View File

@@ -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)

View File

@@ -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<T, F, C> implements BlockTransferAccess<TransferHandler<T>, C> {
private final BlockApiLookup<F, C> lookup;
private final Function<F, TransferHandler<T>> wrapper;
public FabricBlockTransferAccess(BlockApiLookup<F, C> lookup, Function<F, TransferHandler<T>> wrapper) {
this.lookup = lookup;
this.wrapper = wrapper;
}
@Override
@Nullable
public TransferHandler<T> get(Level level, BlockPos pos, C context) {
return wrapper.apply(lookup.find(level, pos, context));
}
@Override
@Nullable
public TransferHandler<T> 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);
}
}
}

View File

@@ -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<F, S> implements TransferHandler<S> {
private final Storage<F> storage;
@Nullable
private final Transaction transaction;
private final TypeAdapter<F, S> typeAdapter;
public FabricStorageTransferHandler(Storage<F> storage, @Nullable Transaction transaction, TypeAdapter<F, S> typeAdapter) {
this.storage = storage;
this.transaction = transaction;
this.typeAdapter = typeAdapter;
}
@Override
public Stream<ResourceView<S>> getContents() {
Transaction transaction = Transaction.openNested(this.transaction);
return StreamSupport.stream(storage.iterable(transaction).spliterator(), false)
.<ResourceView<S>>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<F> view : storage.iterable(transaction)) {
size++;
}
return size;
}
}
@Override
public ResourceView<S> getContent(int index) {
if (storage instanceof InventoryStorage) {
return new FabricStorageResourceView((StorageView<F>) ((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<S> toExtract, long maxAmount, TransferAction action) {
try (Transaction nested = Transaction.openNested(this.transaction)) {
for (StorageView<F> 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<F> 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<F, S> {
S apply(F variant, long amount);
}
private class FabricStorageResourceView implements ResourceView<S> {
private final StorageView<F> view;
private FabricStorageResourceView(StorageView<F> 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<F, S> {
private final Function<S, F> toFabric;
private final FunctionWithAmount<F, S> fromFabric;
private final FunctionWithAmount<S, S> copyWithAmount;
private final Supplier<S> blank;
private final Predicate<S> isEmpty;
private final ToLongFunction<S> toAmount;
public TypeAdapter(Function<S, F> toFabric, FunctionWithAmount<F, S> fromFabric, FunctionWithAmount<S, S> copyWithAmount, Supplier<S> blank, Predicate<S> isEmpty, ToLongFunction<S> toAmount) {
this.toFabric = toFabric;
this.fromFabric = fromFabric;
this.copyWithAmount = copyWithAmount;
this.blank = blank;
this.isEmpty = isEmpty;
this.toAmount = toAmount;
}
}
}

View File

@@ -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<FluidStack> get(Level level, BlockPos pos, Direction direction) {
return wrap(FluidStorage.SIDED.find(level, pos, direction));
}
@Nullable
public static TransferHandler<FluidStack> 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<FluidStack, FluidVariant> TO_FABRIC = FluidStackHooksFabric::toFabric;
private static final FabricStorageTransferHandler.FunctionWithAmount<FluidVariant, FluidStack> FROM_FABRIC = FluidStackHooksFabric::fromFabric;
private static final FabricStorageTransferHandler.FunctionWithAmount<FluidStack, FluidStack> COPY_WITH_AMOUNT = FluidStack::copyWithAmount;
private static final Supplier<FluidStack> BLANK = FluidStack::empty;
private static final Predicate<FluidStack> IS_EMPTY = FluidStack::isEmpty;
private static final ToLongFunction<FluidStack> TO_AMOUNT = FluidStack::getAmount;
private static final FabricStorageTransferHandler.TypeAdapter<FluidVariant, FluidStack> TYPE_ADAPTER = new FabricStorageTransferHandler.TypeAdapter<>(TO_FABRIC, FROM_FABRIC, COPY_WITH_AMOUNT, BLANK, IS_EMPTY, TO_AMOUNT);
@Nullable
public static TransferHandler<FluidStack> 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<FluidStack> {
private final Storage<FluidVariant> storage;
private FabricTransferHandler(Storage<FluidVariant> storage) {
this.storage = storage;
}
@Override
public Iterable<ResourceView<FluidStack>> 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<TransferHandler<FluidStack>, Direction> instantiateBlockAccess() {
return new FabricBlockTransferAccess<>(FluidStorage.SIDED, FluidTransferImpl::wrap);
}
public static ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemAccess() {
return new ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>>() {
@Override
@Nullable
public TransferHandler<FluidStack> get(ItemStack stack, TransferHandler<ItemStack> 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<FluidStack> {
private final StorageView<FluidVariant> view;
private FabricResourceView(StorageView<FluidVariant> view) {
this.view = view;
};
}
public static ContainerItemContext fromTransfer(ItemStack stack, TransferHandler<ItemStack> transferHandler) {
SingleSlotStorage<ItemVariant> mainSlot = new SingleVariantStorage<ItemVariant>() {
@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<ItemVariant> getMainSlot() {
return mainSlot;
}
@Override
public long getCapacity() {
return view.getCapacity();
public List<SingleSlotStorage<ItemVariant>> getAdditionalSlots() {
return null;
}
}
@Override
public long insertOverflow(ItemVariant itemVariant, long maxAmount, TransactionContext transactionContext) {
return 0;
}
};
}
}

View File

@@ -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<ItemStack> {
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<ResourceView<ItemStack>> 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<ItemStack> 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<ItemStack> toExtract, long maxAmount, TransferAction action) {
try (Transaction nested = Transaction.openNested(this.transaction)) {
for (StorageView<ItemVariant> 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<ItemStack> {
private final SingleSlotStorage<ItemVariant> storage;
private FabricResourceView(SingleSlotStorage<ItemVariant> storage) {
this.storage = storage;
}
@Override
public ItemStack getResource() {
return storage.getResource().toStack((int) storage.getAmount());
}
@Override
public long getCapacity() {
return storage.getCapacity();
}
}
}

View File

@@ -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<ItemStack, ItemVariant> TO_FABRIC = ItemVariant::of;
private static final FabricStorageTransferHandler.FunctionWithAmount<ItemVariant, ItemStack> FROM_FABRIC = (variant, amount) -> variant.toStack((int) amount);
private static final FabricStorageTransferHandler.FunctionWithAmount<ItemStack, ItemStack> COPY_WITH_AMOUNT = (stack, amount) -> ItemStackHooks.copyWithCount(stack, (int) amount);
private static final Supplier<ItemStack> BLANK = () -> ItemStack.EMPTY;
private static final Predicate<ItemStack> IS_EMPTY = ItemStack::isEmpty;
private static final ToLongFunction<ItemStack> TO_AMOUNT = ItemStack::getCount;
private static final FabricStorageTransferHandler.TypeAdapter<ItemVariant, ItemStack> TYPE_ADAPTER = new FabricStorageTransferHandler.TypeAdapter<>(TO_FABRIC, FROM_FABRIC, COPY_WITH_AMOUNT, BLANK, IS_EMPTY, TO_AMOUNT);
@Nullable
public static TransferHandler<ItemStack> 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<TransferHandler<ItemStack>, Direction> instantiateBlockAccess() {
return new FabricBlockTransferAccess<>(ItemStorage.SIDED, ItemTransferImpl::wrap);
}
}

View File

@@ -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<FluidStack> get(Level level, BlockPos pos, Direction direction) {
return get(level, pos, null, direction);
}
@Nullable
public static TransferHandler<FluidStack> 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<FluidStack> get(ItemStack stack) {
return wrap(stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).resolve().orElse(null));
}
@Nullable
public static TransferHandler<FluidStack> wrap(@Nullable Object object) {
if (object == null) return null;
@@ -87,6 +65,104 @@ public class FluidTransferImpl {
}
}
public static BlockTransferAccess<TransferHandler<FluidStack>, Direction> instantiateBlockAccess() {
return new ForgeBlockTransferAccess<TransferHandler<FluidStack>, IFluidHandler>() {
@Override
@Nullable
public TransferHandler<FluidStack> get(Level level, BlockPos pos, Direction direction) {
return get(level, pos, level.getBlockState(pos), null, direction);
}
@Override
@Nullable
public TransferHandler<FluidStack> 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<IFluidHandler> getCapability() {
return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY;
}
@Override
public IFluidHandler from(TransferHandler<FluidStack> handler) {
return new ArchFluidHandler(handler);
}
};
}
public static ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemAccess() {
return new ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>>() {
@Override
@Nullable
public TransferHandler<FluidStack> get(ItemStack stack, TransferHandler<ItemStack> context) {
return wrap(stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).resolve().orElse(null));
}
};
}
public static class ArchFluidHandler implements IFluidHandler {
private static final Predicate<FluidStack> TRUE = stack -> true;
private TransferHandler<FluidStack> handler;
public ArchFluidHandler(TransferHandler<FluidStack> 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<FluidStack> {
private IFluidHandler handler;
@@ -95,18 +171,50 @@ public class FluidTransferImpl {
}
@Override
public Iterable<ResourceView<FluidStack>> getResources(TransferContext context) {
return Itr::new;
public Stream<ResourceView<FluidStack>> 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<FluidStack> 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<FluidStack> 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<ResourceView<FluidStack>> {
@@ -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;
}
}

View File

@@ -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<Consumer<AttachCapabilitiesEvent>> LISTENERS = new ArrayList<>();
static {
MinecraftForge.EVENT_BUS.register(CapabilitiesAttachListeners.class);
}
@SubscribeEvent
public static void attach(AttachCapabilitiesEvent event) {
for (Consumer<AttachCapabilitiesEvent> consumer : LISTENERS) {
consumer.accept(event);
}
}
public static void add(Consumer<AttachCapabilitiesEvent> consumer) {
LISTENERS.add(consumer);
}
}

View File

@@ -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<T, C> extends BlockTransferAccess<T, Direction> {
Capability<C> getCapability();
C from(T handler);
@Override
default void register(ResourceLocation id, BlockAccessProvider<T, Direction> provider) {
CapabilitiesAttachListeners.add(event -> {
if (event.getObject() instanceof BlockEntity) {
BlockEntity blockEntity = (BlockEntity) event.getObject();
BlockAccessApplicator<T, Direction> applicator = provider.get(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity);
if (applicator != null) {
event.addCapability(id, new ICapabilityProvider() {
@NotNull
@Override
public <S> LazyOptional<S> getCapability(@NotNull Capability<S> 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();
}
});
}
}
});
}
}

View File

@@ -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<ItemStack> 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<TransferHandler<ItemStack>, Direction> instantiateBlockAccess() {
return new ForgeBlockTransferAccess<TransferHandler<ItemStack>, IItemHandler>() {
@Override
@Nullable
public TransferHandler<ItemStack> get(Level level, BlockPos pos, Direction direction) {
return get(level, pos, level.getBlockState(pos), null, direction);
}
@Override
@Nullable
public TransferHandler<ItemStack> 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<IItemHandler> getCapability() {
return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
}
@Override
public IItemHandler from(TransferHandler<ItemStack> handler) {
return new ArchItemHandler(handler);
}
};
}
public static class ArchItemHandler implements IItemHandler {
private static final Predicate<FluidStack> TRUE = stack -> true;
private TransferHandler<ItemStack> handler;
public ArchItemHandler(TransferHandler<ItemStack> 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<ItemStack> {
private IItemHandler handler;
public ForgeTransferHandler(IItemHandler handler) {
this.handler = handler;
}
@Override
public Stream<ResourceView<ItemStack>> getContents() {
return IntStream.range(0, handler.getSlots()).mapToObj(ForgeResourceView::new);
}
@Override
public int getContentsSize() {
return handler.getSlots();
}
@Override
public ResourceView<ItemStack> 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<ItemStack> toExtract, long maxAmount, TransferAction action) {
return null;
}
@Override
public ItemStack blank() {
return ItemStack.EMPTY;
}
private class ForgeResourceView implements ResourceView<ItemStack> {
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);
}
}
}
}