Jump to content

[1.16] wrong Item count when getting crafting recipe


kiou.23

Recommended Posts

I've implemented a workbench that works just like the vanilla one, however when I get the recipe result to output, it always returns a stack with 64 items. I have no idea why this is happening

 

public class ModWorkbenchTileEntity extends TileEntity implements INamedContainerProvider {

    public static final int CRAFT_MATRIX_SLOT_START = 0;
    public static final int CRAFT_MATRIX_SLOT_END = 8;
    public static final int OUTPUT_SLOT = 9;

    public final ItemStackHandler inventory = new ItemStackHandler(10) {
        @Override
        protected void onContentsChanged(int slot) {
            super.onContentsChanged(slot);
            switch (slot) {
                case OUTPUT_SLOT:
                    // this doesn't handle the cases were the ingredient has a container item, eg. Milk Bucket
                    if (getStackInSlot(slot).isEmpty())
                        for (int i = CRAFT_MATRIX_SLOT_START; i <= CRAFT_MATRIX_SLOT_END; i++)
                            setStackInSlot(i, ItemStack.EMPTY);
                default:
                    ItemStack res = getResult().orElse(ItemStack.EMPTY);
                    if (res.isEmpty()) return;

                    // the Crafting Result is outputed into the output slot, and the crafting matrix is cleared
                    // but the Item Stack in the output slot has 64 items instead of the correct count based on the recipe
                    insertItem(OUTPUT_SLOT, res, false);
            }
        }
    };

    public ModWorkbenchTileEntity() {
        super(ModTileEntityTypes.MOD_WORKBENCH.get());
    }

    // ModWorkbenchInventory is a subclass of CraftingInventory
    // which delegates the getStackInSlot method to the ItemStackHandler passed in
    // it's purpose is so I can call RecipeManager#getRecipe()
    private ModWorkbenchInventory getCraftingInventory() {
        IItemHandler craftMatrix = new ItemStackHandler(9);
        for (int slot = CRAFT_MATRIX_SLOT_START; slot <= CRAFT_MATRIX_SLOT_END; slot++)
            craftMatrix.insertItem(slot, inventory.getStackInSlot(slot), false);

        return new ModWorkbenchInventory(craftMatrix);
    }

    private Optional<ICraftingRecipe> getRecipe() {
        return world.getRecipeManager().getRecipe(
            IRecipeType.CRAFTING,
            getCraftingInventory(),
            world
        );
    }

    private Optional<ItemStack> getResult() {
        return getRecipe().map(recipe -> recipe.getCraftingResult(getCraftingInventory()));
    }

    @Override
    public ITextComponent getDisplayName() {
        return new TranslationTextComponent(ModBlocks.MOD_WORKBENCH.get().getTranslationKey());
    }

    @Override
    public Container createMenu(int windowId, PlayerInventory playerInv, PlayerEntity player) {
        return new ModWorkbenchContainer(windowId, playerInv, this);
    }
}

 

Link to comment
Share on other sites

10 hours ago, diesieben07 said:

You have an infinite loop:

onContentsChanged is called and you have a recipe. So getResult returns the resulting item, which you insert into the output slot. This causes onContentsChanged to be called again with OUTPUT_SLOT. The if statement inside there will not be true, because the result was just put in the output slot, so it is no longer empty. The case statement does not have a break inside it, so it falls through to the default case. In there you get the recipe output again (which is still there, because you never shrink the input slots, so the recipe always matches) and insert it into the output slot again. This continues until the output slot is full and no longer changes - breaking the cycle.

yeah, just spent 5 hours in solving the crafting table logic. It's working perfectly now, may not be the best way to implement, and later I'll come back and try to simplify the code, but so far this is it: (The behaviour is the same as the vanilla workbench except it keeps the items in as it is a tile entity)

 

Tile entity

public class ModWorkbenchTileEntity extends TileEntity implements INamedContainerProvider, ICraftingTE {

    public static final int CRAFT_MATRIX_SLOT_START = 0;
    public static final int CRAFT_MATRIX_SLOT_END = 8;
    public static final int OUTPUT_SLOT = 9;

    public final ItemStackHandler inventory = new ItemStackHandler(10) {
        @Override
        protected void onContentsChanged(int slot) {
            super.onContentsChanged(slot);
            switch (slot) {
                case OUTPUT_SLOT:
                    break;
                default:
                    updateInventory();
            }
        }
    };

    private ModWorkbenchInventory getCraftingInventory() {
        ItemStackHandler craftMatrix = new ItemStackHandler(9);
        for (int _slot = CRAFT_MATRIX_SLOT_START; _slot <= CRAFT_MATRIX_SLOT_END; _slot++)
            craftMatrix.setStackInSlot(_slot, inventory.getStackInSlot(_slot));

        return new ModWorkbenchInventory(craftMatrix);
    }

    public ModWorkbenchTileEntity() {
        super(ModTileEntityTypes.MOD_WORKBENCH.get());
    }

    private ItemStack getRecipeResult() {
        return world.getRecipeManager().getRecipe(
            IRecipeType.CRAFTING,
            getCraftingInventory(),
            world
        ).map(recipe -> recipe.getCraftingResult(getCraftingInventory())).orElse(ItemStack.EMPTY);
    }

    private void updateInventory() {
        ItemStack resultStack = getRecipeResult();
        if (inventory.getStackInSlot(OUTPUT_SLOT) == resultStack) return;

        inventory.setStackInSlot(OUTPUT_SLOT, resultStack);
    }

    private void shrinkStackInSlot(int slot) {
        ItemStack slotStack = inventory.getStackInSlot(slot);

        ItemStack container = slotStack.hasContainerItem() ? slotStack.getContainerItem() : ItemStack.EMPTY;

        slotStack.shrink(1);

        if (inventory.getStackInSlot(slot).isEmpty() && !container.isEmpty())
            inventory.setStackInSlot(slot, container);
    }

    @Override
    public void onCraft() {
        for (int _slot = CRAFT_MATRIX_SLOT_START; _slot <= CRAFT_MATRIX_SLOT_END; _slot++)
            shrinkStackInSlot(_slot);

        ItemStack resultStack = getRecipeResult();
        if (inventory.getStackInSlot(OUTPUT_SLOT) != resultStack)
            inventory.setStackInSlot(OUTPUT_SLOT, resultStack);
    }

    public ItemStack onCraftMany(PlayerInventory playerInv) {
        ItemStack output = inventory.getStackInSlot(OUTPUT_SLOT);

        int amount = 64;
        for (int _slot = CRAFT_MATRIX_SLOT_START; _slot <= CRAFT_MATRIX_SLOT_END; _slot++){
            ItemStack stack = inventory.getStackInSlot(_slot);
            if (stack.isEmpty()) continue;
            amount = amount > stack.getCount() ? stack.getCount() : amount;
        }

        for(int i = 0; i < amount; i++) {
            if (!playerInv.addItemStackToInventory(output))
                return inventory.getStackInSlot(OUTPUT_SLOT);

            for (int _slot = CRAFT_MATRIX_SLOT_START; _slot <= CRAFT_MATRIX_SLOT_END; _slot++)
                shrinkStackInSlot(_slot);

            updateInventory();
        }

        return ItemStack.EMPTY;
    }

    @Override
    public ITextComponent getDisplayName() {
        return new TranslationTextComponent(ModBlocks.MOD_WORKBENCH.get().getTranslationKey());
    }

    @Override
    public Container createMenu(int windowId, PlayerInventory playerInv, PlayerEntity player) {
        return new ModWorkbenchContainer(windowId, playerInv, this);
    }
}

 

Container

public class ModWorkbenchContainer extends Container {

    public final ModWorkbenchTileEntity tileEntity;
    private final IWorldPosCallable canInteractWithCallable;

    public ModWorkbenchContainer(final int windowId, PlayerInventory playerInv, final PacketBuffer data) {
        this(windowId, playerInv, (ModWorkbenchTileEntity)playerInv.player.world.getTileEntity(data.readBlockPos()));
    }

    public ModWorkbenchContainer(final int windowId, PlayerInventory playerInventory, ModWorkbenchTileEntity tileEntity) {
        super(ModContainerTypes.MOD_WORKBENCH.get(), windowId);

        this.tileEntity = tileEntity;
        this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos());

        final int slotSizePlus2 = 18;

        //Craft Matrix
        final int craftMatStartX = 30;
        final int craftMatStartY = 17;

        for (int row = 0; row < 3; row++)
            for (int column = 0; column < 3; column++)
                this.addSlot(new SlotItemHandler(tileEntity.inventory,
                    row * 3 + column,
                    craftMatStartX + column * slotSizePlus2,
                    craftMatStartY + row * slotSizePlus2
                ));

        //Output Slot
        this.addSlot(new ClosedSlotItemHandler(tileEntity.inventory, 9, 124, 35, tileEntity));

        //Player Inventory
        final int playerInvStartX = 8;
        final int playerInvStartY = 84;

        for (int row = 0; row < 3; row++)
            for (int column = 0; column < 9; column++)
                this.addSlot(new Slot(playerInventory,
                    9 + (row * 9) + column,
                    playerInvStartX + (column * slotSizePlus2),
                    playerInvStartY + (row * slotSizePlus2)
                ));

        final int playerHotbarY = playerInvStartY + slotSizePlus2 * 3 + 4;
        for (int column = 0; column < 9; column++)
            this.addSlot(new Slot(playerInventory,
                column,
                playerInvStartX + (column * slotSizePlus2),
                playerHotbarY
            ));
    }

    @Override
    public boolean canInteractWith(PlayerEntity player) {
        return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.MOD_WORKBENCH.get());
    }

    @Override
    public ItemStack transferStackInSlot(PlayerEntity player, int index) {
        ItemStack stack = ItemStack.EMPTY;
        Slot slot = this.inventorySlots.get(index);

        if (slot == null || !slot.getHasStack()) return stack;

        ItemStack slotStack = slot.getStack();
        stack = slotStack.copy();

        if (index >= tileEntity.inventory.getSlots()) {

            if (!mergeItemStack(slotStack, 0, tileEntity.inventory.getSlots() - 1, false))
                return ItemStack.EMPTY;

        } else if (index == tileEntity.OUTPUT_SLOT) {

            return tileEntity.onCraftMany(player.inventory);

        } else {

            if (!mergeItemStack(slotStack, tileEntity.inventory.getSlots(), inventorySlots.size(), false))
                return ItemStack.EMPTY;

        }

        if (slotStack.isEmpty())
            slot.putStack(ItemStack.EMPTY);
        else
            slot.onSlotChanged();

        return stack;
    }
}

 

 

Edit: ICraftingTE is an interface so that I can pass the TileEntity to the Slot in the container and have the Slot handle the code for whenever an Item is taken out of it

(As I couldn't assume the player had taken the recipe output if the slot changed to Empty since it could also be the crafting matrix being changed to an invalid recipe)

 

10 hours ago, diesieben07 said:

Your getCraftingInventory is needlessly complicated, why don't you have ModWorkbenchInventory directly use the "inventory" IItemHandler? That way you would not have to keep copying the items and making new ItemStackHandler and ModWorkbenchInventory instances.

I tried had that, but whenever getting the recipe it would act weird, I suppose because of the Output slot is also in the "inventory"

for instance: if I placed a block of iron in the crafting matrix, the result slot would update correctly to be 9 iron ingots. however, if I removed the iron block from the crafting matrix and left it empty, the result slot would update to be 9 iron nuggets. There's definetely a better way of getting the crafting inventory to pass to the recipe manager, but this is it worked and I wanted to have it working before I polished the code

Edited by kiou.23
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.