Skip to content
This repository has been archived by the owner on Sep 16, 2023. It is now read-only.

Commit

Permalink
added fabricated-forge-item-api module
Browse files Browse the repository at this point in the history
  • Loading branch information
Trinsdar committed Jul 18, 2023
1 parent 3281bb8 commit 7b1aae7
Show file tree
Hide file tree
Showing 24 changed files with 2,289 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package net.fabricatedforgeapi.transfer.item;

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.TransactionContext;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

@SuppressWarnings("UnstableApiUsage")
public interface IItemHandlerStorage extends Storage<ItemVariant>, Iterable<StorageView<ItemVariant>> {
IItemHandler getHandler();

@Override
default long insert(ItemVariant resource, long maxAmount, TransactionContext transaction) {
AtomicLong atomicLong = new AtomicLong(0);
transaction.addCloseCallback((t, result) -> {
if (result.wasCommitted()) {
ItemStack toInsert = resource.toStack((int) maxAmount);
ItemStack remainder = ItemHandlerHelper.insertItemStacked(getHandler(), toInsert, false);
atomicLong.set(maxAmount - remainder.getCount());
}
});
return atomicLong.get();
}

@Override
default long extract(ItemVariant resource, long maxAmount, TransactionContext transaction) {
AtomicLong atomicLong = new AtomicLong(0);
transaction.addCloseCallback((t, result) -> {
if (result.wasCommitted()) {
ItemStack toExtract = resource.toStack((int) maxAmount);
ItemStack extracted = ItemHandlerHelper.extract(getHandler(), toExtract, false);
atomicLong.set(extracted.getCount());
}
});
return atomicLong.get();
}

@Override
default Iterable<StorageView<ItemVariant>> iterable(TransactionContext transaction) {
return this;
}

@Override
default Iterator<StorageView<ItemVariant>> iterator(TransactionContext transaction) {
return iterator();
}
@Override
default Iterator<StorageView<ItemVariant>> iterator() {
int slots = getHandler().getSlots();
List<StorageView<ItemVariant>> views = new ArrayList<>();
for (int i = 0; i < slots; i++) {
views.add(new SlotStorageView(i, getHandler()));
}
return views.iterator();
}

@Override
@Nullable
default StorageView<ItemVariant> exactView(TransactionContext transaction, ItemVariant resource) {
for (StorageView<ItemVariant> view : this) {
if (view.getResource().equals(resource)) {
return view;
}
}
return null;
}

//@Override
@Nullable
default StorageView<ItemVariant> exactView(ItemVariant resource) {
for (StorageView<ItemVariant> view : this) {
if (view.getResource().equals(resource)) {
return view;
}
}
return null;
}

class SlotStorageView implements StorageView<ItemVariant> {
protected final int slotIndex;
protected final IItemHandler owner;

public SlotStorageView(int index, IItemHandler owner) {
this.owner = owner;
this.slotIndex = index;
}

@Override
public long extract(ItemVariant resource, long maxAmount, TransactionContext transaction) {
long actual = 0;
ItemStack extracted = owner.extractItem(slotIndex, (int) maxAmount, true);
if (extracted.is(resource.getItem())) {
actual = extracted.getCount();
transaction.addCloseCallback((t, result) -> {
if (result.wasCommitted()) {
owner.extractItem(slotIndex, (int) maxAmount, false);
}
});
}
return actual;
}

@Override
public boolean isResourceBlank() {
return owner.getStackInSlot(slotIndex).isEmpty();
}

@Override
public ItemVariant getResource() {
return ItemVariant.of(owner.getStackInSlot(slotIndex));
}

@Override
public long getAmount() {
return owner.getStackInSlot(slotIndex).getCount();
}

@Override
public long getCapacity() {
return owner.getSlotLimit(slotIndex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.fabricatedforgeapi.transfer.item;

import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.wrapper.EmptyHandler;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

@SuppressWarnings("UnstableApiUsage")
public class ItemHandlerStorage implements IItemHandlerStorage {
@Nonnull
protected IItemHandler handler;

public ItemHandlerStorage(@Nullable IItemHandler handler) {
if (handler == null) {
this.handler = EmptyHandler.INSTANCE;
} else {
this.handler = handler;
}
}

@Override
@Nonnull
public IItemHandler getHandler() {
return handler;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package net.fabricatedforgeapi.transfer.item;

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.world.item.ItemStack;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@SuppressWarnings("UnstableApiUsage")
public class ItemStorageHandler implements IItemHandlerModifiable {
protected final Storage<ItemVariant> storage;
protected long version;
protected int slots;
protected ItemStack[] stacks;
protected Long[] capacities;

public ItemStorageHandler(Storage<ItemVariant> storage) {
this.storage = storage;
this.version = storage.getVersion();
updateContents();
}

public boolean shouldUpdate() {
return storage.getVersion() != version;
}

private void updateContents() {
List<ItemStack> stacks = new ArrayList<>();
List<Long> capacities = new ArrayList<>();
try (Transaction t = Transaction.openOuter()) {
for (StorageView<ItemVariant> view : storage.iterable(t)) {
stacks.add(view.getResource().toStack((int) view.getAmount()));
capacities.add(view.getCapacity());
}
t.abort();
}
this.stacks = stacks.toArray(ItemStack[]::new);
this.capacities = capacities.toArray(Long[]::new);
this.slots = stacks.size();
this.version = storage.getVersion();
}

private boolean validIndex(int slot) {
return slot >= 0 && slot < slots;
}

@Override
public int getSlots() {
if (shouldUpdate())
updateContents();
return slots;
}

@NotNull
@Override
public ItemStack getStackInSlot(int slot) {
if (validIndex(slot)) {
if (shouldUpdate())
updateContents();
return stacks[slot].copy();
}
return ItemStack.EMPTY;
}

@NotNull
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean sim) {
if (!validIndex(slot)) // first check valid slot index
return stack;
if (stack.isEmpty()) // check stack is not empty
return stack;
if (!isItemValid(slot, stack)) // make sure this stack can be stored
return stack;
if (!storage.supportsInsertion()) // make sure insertion is supported
return stack;
ItemStack current = getStackInSlot(slot);
int limit = Math.min(getSlotLimit(slot), current.getMaxStackSize());
if (limit <= 0 || !ItemHandlerHelper.canItemStacksStack(current, stack)) // make sure there's room
return stack;
// finally insert
ItemStack finalVal = ItemStack.EMPTY;
try (Transaction t = Transaction.openOuter()) {
// this technically breaks spec and ignores 'slot' but thanks FAPI, we literally have no choice!
long remainder = stack.getCount() - storage.insert(ItemVariant.of(stack), stack.getCount(), t);
if (remainder != 0) {
finalVal = new ItemStack(stack.getItem(), (int) remainder);
}

if (sim) t.abort();
else {
t.commit();
if (shouldUpdate())
updateContents();
}
}
return finalVal;
}

@NotNull
@Override
public ItemStack extractItem(int slot, int amount, boolean sim) {
if (amount <= 0)
return ItemStack.EMPTY;
if (!validIndex(slot)) // check valid slot index
return ItemStack.EMPTY;
if (!storage.supportsExtraction()) // make sure insertion is supported
return ItemStack.EMPTY;

ItemStack finalVal = ItemStack.EMPTY;
try (Transaction t = Transaction.openOuter()) {
int index = 0;
for (StorageView<ItemVariant> view : storage.iterable(t)) {
if (index == slot) {
ItemVariant variant = view.getResource();
long extracted = view.isResourceBlank() ? 0 : view.extract(variant, amount, t);
if (extracted != 0) {
finalVal = variant.toStack((int) extracted);
}
break;
}
index++;
}
if (sim) t.abort();
else {
t.commit();
if (shouldUpdate())
updateContents();
}
}
return finalVal;
}

@Override
public int getSlotLimit(int slot) {
if (validIndex(slot)) {
if (shouldUpdate())
updateContents();
return (int) (long) capacities[slot];
}
return 0;
}

@Override
public boolean isItemValid(int slot, ItemStack stack) {
return true;
}

@Override
public void setStackInSlot(int slot, ItemStack stack) {
// jank
extractItem(slot, getSlotLimit(slot), false);
insertItem(slot, stack, false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.minecraftforge.common.util;

import net.minecraft.nbt.Tag;

/**
* An interface designed to unify various things in the Minecraft
* code base that can be serialized to and from a NBT tag.
*/
public interface INBTSerializable<T extends Tag> {
T serializeNBT();

void deserializeNBT(T nbt);
}
Loading

0 comments on commit 7b1aae7

Please sign in to comment.