filename:
fabric/src/main/java/rearth/oritech/fabric/FabricItemApi.java
branch:
1.21
back to repo
package rearth.oritech.fabric;
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.SlottedStorage;
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.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.Oritech;
import rearth.oritech.api.item.BlockItemApi;
import rearth.oritech.api.item.ItemApi;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.IntStream;
public class FabricItemApi implements BlockItemApi {
@Override
public void registerBlockEntity(Supplier<BlockEntityType<?>> typeSupplier) {
ItemStorage.SIDED.registerForBlockEntity((entity, direction) ->
ContainerStorageWrapper.of(((ItemApi.BlockProvider) entity).getInventoryStorage(direction)), typeSupplier.get());
}
@Override
public ItemApi.InventoryStorage find(World world, BlockPos pos, @Nullable BlockState state, @Nullable BlockEntity entity, @Nullable Direction direction) {
var candidate = ItemStorage.SIDED.find(world, pos, state, entity, direction);
if (candidate == null) return null;
if (candidate instanceof ContainerStorageWrapper wrapper) return wrapper.container;
return new FabricStorageWrapper(candidate);
}
@Override
public ItemApi.InventoryStorage find(World world, BlockPos pos, @Nullable Direction direction) {
return find(world, pos, null, null, direction);
}
// used to interact with storages from other mods
public static class FabricStorageWrapper implements ItemApi.InventoryStorage {
public final Storage<ItemVariant> storage;
public FabricStorageWrapper(Storage<ItemVariant> storage) {
this.storage = storage;
}
@Override
public boolean supportsInsertion() {
return storage.supportsInsertion();
}
@Override
public int insert(ItemStack inserted, boolean simulate) {
if (inserted.isEmpty()) return 0;
try (var transaction = Transaction.openOuter()) {
var insertCount = storage.insert(ItemVariant.of(inserted), inserted.getCount(), transaction);
if (!simulate)
transaction.commit();
return (int) insertCount;
}
}
@Override
public int insertToSlot(ItemStack inserted, int slot, boolean simulate) {
if (inserted.isEmpty()) return 0;
// this usually won't be used
if (storage instanceof SlottedStorage<ItemVariant> slottedStorage) {
try (var transaction = Transaction.openOuter()) {
var insertCount = slottedStorage.getSlot(slot).insert(ItemVariant.of(inserted), inserted.getCount(), transaction);
if (!simulate)
transaction.commit();
return (int) insertCount;
}
}
return 0;
}
@Override
public boolean supportsExtraction() {
return storage.supportsExtraction();
}
@Override
public int extract(ItemStack extracted, boolean simulate) {
if (extracted.isEmpty()) return 0;
try (var transaction = Transaction.openOuter()) {
var extractedCount = storage.extract(ItemVariant.of(extracted), extracted.getCount(), transaction);
if (!simulate)
transaction.commit();
return (int) extractedCount;
}
}
@Override
public int extractFromSlot(ItemStack extracted, int slot, boolean simulate) {
if (extracted.isEmpty()) return 0;
if (storage instanceof SlottedStorage<ItemVariant> slottedStorage) {
try (var transaction = Transaction.openOuter()) {
var extractedCount = slottedStorage.getSlot(slot).extract(ItemVariant.of(extracted), extracted.getCount(), transaction);
if (!simulate)
transaction.commit();
return (int) extractedCount;
}
}
return 0;
}
@Override
public void setStackInSlot(int slot, ItemStack stack) {
Oritech.LOGGER.error("Unable to set stack in slot: {}, stack is: {}", slot, stack);
Oritech.LOGGER.error("This should never happen");
}
@Override
public ItemStack getStackInSlot(int slot) {
// this usually won't be used
if (storage instanceof SlottedStorage<ItemVariant> slottedStorage) {
return slottedStorage.getSlot(slot).getResource().toStack((int) slottedStorage.getSlot(slot).getAmount());
}
return ItemStack.EMPTY;
}
@Override
public int getSlotCount() {
if (storage instanceof SlottedStorage<ItemVariant> slottedStorage) {
return slottedStorage.getSlotCount();
}
return 1;
}
@Override
public int getSlotLimit(int slot) {
if (storage instanceof SlottedStorage<ItemVariant> slottedStorage) {
return (int) slottedStorage.getSlot(slot).getCapacity();
}
return 0;
}
@Override
public void update() {
}
}
public static class ContainerStorageWrapper extends SnapshotParticipant<List<ItemStack>> implements Storage<ItemVariant> {
private final ItemApi.InventoryStorage container;
public static ContainerStorageWrapper of(ItemApi.InventoryStorage container) {
if (container == null) return null;
return new ContainerStorageWrapper(container);
}
public ContainerStorageWrapper(ItemApi.InventoryStorage container) {
this.container = container;
}
@Override
public boolean supportsInsertion() {
return container.supportsInsertion();
}
@Override
public long insert(ItemVariant resource, long maxAmount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.insert(resource.toStack((int) maxAmount), false);
}
@Override
public boolean supportsExtraction() {
return container.supportsExtraction();
}
@Override
public long extract(ItemVariant resource, long maxAmount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.extract(resource.toStack((int) maxAmount), false);
}
@Override
public @NotNull Iterator<StorageView<ItemVariant>> iterator() {
return IntStream.range(0, container.getSlotCount()).mapToObj(slot -> new StorageView<ItemVariant>() {
@Override
public long extract(ItemVariant resource, long maxAmount, TransactionContext transaction) {
updateSnapshots(transaction);
transaction.addCloseCallback((transactionContext, result) -> {
if (result.wasCommitted()) {
container.update();
}
});
return container.extractFromSlot(resource.toStack((int) maxAmount), slot, false);
}
@Override
public boolean isResourceBlank() {
return getStack().isEmpty();
}
@Override
public ItemVariant getResource() {
return ItemVariant.of(getStack());
}
@Override
public long getAmount() {
return getStack().getCount();
}
@Override
public long getCapacity() {
return container.getSlotLimit(slot);
}
private ItemStack getStack() {
return container.getStackInSlot(slot);
}
}.getUnderlyingView()).iterator();
}
@Override
protected List<ItemStack> createSnapshot() {
return IntStream.range(0, container.getSlotCount()).mapToObj(container::getStackInSlot).toList();
}
@Override
protected void readSnapshot(List<ItemStack> snapshot) {
IntStream.range(0, snapshot.size()).forEach(slot -> container.setStackInSlot(slot, snapshot.get(slot)));
}
}
}