Polling items from Carrier not working

Hello, I’m using the following to transfer items from a carrier into a custom inventory.
The carrier is a Furnace with 48 Iron ore in the input and 16 Iron_Ingots in the output.
While i can peek the slot polling returns Optional.empty() and I don’t think that’s supposed to happen?

private int transfer(ItemType i, Inventory from, Inventory to) {
  Inventory f = from.query(QueryOperationTypes.INVENTORY_TYPE.of(OutputSlot.class));
  Optional<ItemStack> istack = (i == null ? f.peek() : f.query(QueryOperationTypes.ITEM_TYPE.of(i)).peek());
  if (istack.isPresent())
    istack = from.query(QueryOperationTypes.ITEM_STACK_IGNORE_QUANTITY.of(istack.get())).poll();
  else {
    thisWorker.setError("Could not look up items in output slots");
    return 0;
  }
  int res = 0;
  if (istack.isPresent()) {
    ItemStack stack = istack.get();
    res = stack.getQuantity();
    to.offer(stack);
    res -= stack.getQuantity();
    f.offer(stack);
  } else {
    thisWorker.setError("Could not poll items from the output slot");
    return 0;
  }
  return res;
}
...
transfer(ItemTypes.IRON_INGOT, carrier.get().getInventory(), thisWorker.getInventory());

I’m using spongeapi-7.1.0-20180110.070302-4 on SpongeVanilla 1.12.2-7.1.0-BETA-9

So, looking at this code in particular I see a couple of flags.

Optional<ItemStack> istack = (i == null ? f.peek() : f.query(QueryOperationTypes.ITEM_TYPE.(i)).peek());
if (istack.isPresent())
  istack = from.query(QueryOperationTypes.ITEM_STACK_IGNORE_QUANTITY.of(istack.get())).poll();

First, remember that a query doesn’t necessarily return the value of the query - an OutputSlot - if there are multiple results. I’ve talked about this before in this reply, so I’ll simply link it rather than rewriting it.

Next, I’m seeing multiple layers of query logic that follow different rules. If you haven’t already, I would start by verifying that you are receiving an OutputSlot back from the first query. Additionally, because this is a furnace (remember, context is key) there should only be a single OutputSlot anyways, so it should be the direct result of the query.

Right now, the largest thing I see is that there doesn’t seem to be any reason for you to peek the inventory for an ItemStack and then query for the same ItemStack yet with a different quantity - you would get the same functionally from polling the query to begin with. What is this section of code intended to do?

That being said, it’s entirely possible that one of the queries is doing something incorrectly and that’s causing this issue. However, your code is highly coupled and convoluted with a lot of things going on - can you replicate this in a simple test plugin that includes log outputs for each query and the resulting ItemStacks?

I will write a small test plugin over the next week or so…

Just for clarification on what i intend to do:
The actual goal is to pick a stack of itemType ‘i’ from one inventory (a carrier) and put it into another inventory where the stack taken from the carrier MUST fit into the target inventory.
If for example a furnace burnt through a stack of iron ore and has 64 iron ingots in it’s output slot, but the target inventory may only accept 8 more iron ingots i want to take exactly 8 iron ingots from the furnace.
After moving the items the method should return the amount of items moved.
Additionally, if I don’t specify the type (i==null) i want to ignore it and instead use the first available itemstack in the carriers first output slot.

The source in my tests was, as mentioned a Carrier (type checked), and the target is a virtual hopper inventory. while I always managed to add the correct items to the target inventory i can not remove them from the source inventory.

I’ll write back once I wrote the test plugin

Here’s the code (specifically the poll part) wrapped in a plugin, Primary interact with a Carrier Block to trigger:

@Plugin(id="demo", name="demo", version="0.1", authors={"DosMike"})
public class demo {
  
  public static void main(String[] args) { System.err.println("This plugin can not be run as executable!"); }
  
  static demo instance;
  static demo getInstance() { return instance; }
  
  static PluginContainer getContainer() { return Sponge.getPluginManager().fromInstance(getInstance()).get(); }
  
  @Inject
  private Logger logger;
  public static void l(String format, Object... args) { instance.logger.info(String.format(format, args)); }
  public static void w(String format, Object... args) { instance.logger.warn(String.format(format, args)); }
  
  public static Random rng = new Random(System.currentTimeMillis());
  
  /// --- === Main Plugin stuff === --- \\\ 
  
  @Listener
  public void onServerInit(GameInitializationEvent event) {
    instance = this;
  }
  
  @Listener
  public void interactEntity(InteractBlockEvent.Primary event) {
    Optional<Player> p = event.getCause().first(Player.class);
    if (!p.isPresent()) return;
    
    Optional<Location<World>> tmp = event.getTargetBlock().getLocation();
    if (!tmp.isPresent()) return;
    
    Optional<TileEntity> te = tmp.get().getTileEntity();
    if (!te.isPresent()) return;
    
    if (!(te.get() instanceof Carrier)) return; //i want this to work on any carrier, so i won't be more specific here
    Carrier target = (Carrier)te.get();
    
    l("The clicked block has a Carrier TileEntity");
    
    int iron_ingots = transfer(ItemTypes.IRON_INGOT, target.getInventory(), p.get().getInventory());
    int anything = transfer(null, target.getInventory(), p.get().getInventory());
    
    l("You have take %d Iron Ingots and %d of something", iron_ingots, anything);
  }
  
  private int transfer(ItemType i, Inventory from, Inventory to) {
    //get all output slots to look through, i only want to take from output slots
    //note that some items may fit into input and output slots, or fuel and output slots 
    Inventory f = from.query(QueryOperationTypes.INVENTORY_TYPE.of(OutputSlot.class));
    //detect, what i'll take out
    Optional<ItemStack> istack;
    if (i == null) {
      istack = f.poll(); //take the first available stack, if no specific itemtype was specified
    } else {
      //from the output slots filtered earlier i want to take the specified type of items
      istack = f.query(QueryOperationTypes.ITEM_TYPE.of(i)).poll();
    }
    if (!istack.isPresent()) { 
      //i was unable to remove any items from the output slots
      w("Could not poll items in output slots");
      
      //just a validity check, to see if sponge is actually using the correct slots
      for (Inventory slot : f.slots()) {
        Optional<ItemStack> check = slot.peek();
        if (check.isPresent()) l("Output contains %d %s", check.get().getQuantity(), check.get().getType().getId());
      }
      
      return 0;
    }
    //TODO put stuff into the other inventory, not the problem here
    return istack.get().getQuantity();
  }
}

This for me outputs, when hitting a furnace as described with 48 iron ore in the input and 16 iron ingots in the output:

[demo]: The clicked block has a Carrier TileEntity
[demo]: Could not poll items in output slots
[demo]: Output contains 16 minecraft:iron_ingot
[demo]: Output contains 16 minecraft:iron_ingot
[demo]: Could not poll items in output slots
[demo]: Output contains 16 minecraft:iron_ingot
[demo]: Output contains 16 minecraft:iron_ingot
[demo]: You have take 0 Iron Ingots and 0 of something
``