Jump to content

[1.16] wrong Item count when getting crafting recipe


Recommended Posts

Posted

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);
    }
}

 

Posted (edited)
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

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • When I first heard about Bitcoin back in 2018, I was skeptical. The idea of a decentralized, digital currency seemed too good to be true. But I was intrigued as I learned more about the technology behind it and its potential. I started small, investing just a few hundred dollars, dipping my toes into the cryptocurrency waters. At first, it was exhilarating to watch the value of my investment grow exponentially. I felt like I was part of the future, an early adopter of this revolutionary new asset. But that euphoria was short-lived. One day, I logged into my digital wallet only to find it empty - my Bitcoin had vanished without a trace. It turned out that the online exchange I had trusted had been hacked, and my funds were stolen. I was devastated, both financially and emotionally. All the potential I had seen in Bitcoin was tainted by the harsh reality that with decentralization came a lack of regulation and oversight. My hard-earned money was gone, lost to the ether of the digital world. This experience taught me a painful lesson about the price of trust in the uncharted territory of cryptocurrency. While the technology holds incredible promise, the risks can be catastrophic if you don't approach it with extreme caution. My Bitcoin investment gamble had failed, and I was left to pick up the pieces, wiser but poorer for having placed my faith in the wrong hands. My sincere appreciation goes to MUYERN TRUST HACKER. You are my hero in recovering my lost funds. Send a direct m a i l ( muyerntrusted ( @ ) mail-me ( . )c o m ) or message on whats app : + 1 ( 4-4-0 ) ( 3 -3 -5 ) ( 0-2-0-5 )
    • You could try posting a log (if there is no log at all, it may be the launcher you are using, the FAQ may have info on how to enable the log) as described in the FAQ, however this will probably need to be reported to/remedied by the mod author.
    • So me and a couple of friends are playing with a shitpost mod pack and one of the mods in the pack is corail tombstone and for some reason there is a problem with it, where on death to fire the player will get kicked out of the server and the tombstone will not spawn basically deleting an entire inventory, it doesn't matter what type of fire it is, whether it's from vanilla fire/lava, or from modded fire like ice&fire/lycanites and it's common enough to where everyone on the server has experienced at least once or twice and it doesn't give any crash log. a solution to this would be much appreciated thank you!
    • It is 1.12.2 - I have no idea if there is a 1.12 pack
  • Topics

×
×
  • Create New...

Important Information

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