Skip to content

Commit

Permalink
Lets hope I tested this thoroughly enough this time (#4)
Browse files Browse the repository at this point in the history
* Revert "Fix major dupe bug caused by trying to be polite and setting the `ItemEntitys` `ItemStack` instead of messing with it's size in place"

This reverts commit 6c74927.

* Actually fix the dupe bug

* 1.1.1
  • Loading branch information
Traister101 authored Oct 12, 2024
1 parent 344391f commit 3264dc7
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 108 deletions.
8 changes: 1 addition & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
### Bug fixes

- Fix duplication bug when picking up items which entirely refill all partial slots with at least one left over

### Changes

- Added new tag 'allowed_in_ore_sack' for items which can go into ore sacks (wow crazy)
- Not ugly Icon courtesy of Aleki
- Start of patchouli entires
- Fix new duplication bug caused by poorly fixing the previous. First reported by: Sigmenzzz
173 changes: 72 additions & 101 deletions src/main/java/mod/traister101/sns/util/handlers/PickupHandler.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package mod.traister101.sns.util.handlers;

import mod.traister101.sns.common.items.ContainerItem;
import mod.traister101.sns.config.SNSConfig;
import mod.traister101.sns.util.ContainerType;
import net.dries007.tfc.common.blocks.GroundcoverBlock;
import net.dries007.tfc.common.blocks.rock.LooseRockBlock;
import net.dries007.tfc.common.blocks.wood.FallenLeavesBlock;
import top.theillusivec4.curios.api.CuriosApi;
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;

import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.game.ClientboundTakeItemEntityPacket;
import net.minecraft.server.level.*;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.*;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.*;
Expand All @@ -25,10 +23,10 @@
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.event.entity.player.EntityItemPickupEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock;
import net.minecraftforge.eventbus.api.Event.Result;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.items.*;

import java.util.Optional;
import net.minecraftforge.items.wrapper.PlayerMainInvWrapper;

public final class PickupHandler {

Expand All @@ -40,22 +38,30 @@ public static void onPickupItem(final EntityItemPickupEvent event) {

final Player player = event.getEntity();
final ItemEntity itemEntity = event.getItem();
final ItemStack itemResult;
final int pickupCount;
{
final ItemStack stack = itemEntity.getItem();
final int startCount = stack.getCount();
itemResult = pickupItemStack(player, stack);
pickupCount = startCount - itemResult.getCount();
if (itemResult.isEmpty()) event.setCanceled(true);
}

final ItemStack entityStack = itemEntity.getItem();
final int startCount = entityStack.getCount();
final ItemStack itemResult = pickupItemStack(player, entityStack);
final int pickupCount = startCount - itemResult.getCount();

// Picked up more than 0
if (0 < pickupCount) {
player.containerMenu.broadcastChanges();
final var packet = new ClientboundTakeItemEntityPacket(itemEntity.getId(), player.getId(), pickupCount);
((ServerPlayer) player).connection.send(packet);
player.take(itemEntity, pickupCount);

// Update the item entity
if (itemResult.isEmpty()) {
itemEntity.discard();
} else {
itemEntity.setItem(itemResult);
}

player.awardStat(Stats.ITEM_PICKED_UP.get(entityStack.getItem()), pickupCount);
player.onItemPickup(itemEntity);
}

event.setCanceled(itemResult.isEmpty());
event.setResult(Result.ALLOW);
}

/**
Expand Down Expand Up @@ -108,133 +114,99 @@ public static void onBlockActivated(final RightClickBlock event) {
}

/**
* Tries to first fill any valid stacks in the player inventory then tries to fill any {@link ContainerItem}s. If both fail to consume the entire
* stack
* the remainer is returned
* Our item pickup handling
*
* @param player Player to handle
* @param itemPickup The item being picked up
*
* @return Empty {@link ItemStack} or the remainer.
* @return The remainer
*/
private static ItemStack pickupItemStack(final Player player, final ItemStack itemPickup) {
ItemStack remainder = itemPickup;

final Inventory playerInventory = player.getInventory();
if (topOffPlayerInventory(playerInventory, remainder)) return ItemStack.EMPTY;

if (ModList.get().isLoaded(CuriosApi.MODID)) {
final Optional<ICuriosItemHandler> optionalCuriosItemHandler = CuriosApi.getCuriosInventory(player).resolve();

if (optionalCuriosItemHandler.isPresent()) {
final ICuriosItemHandler curiosItemHandler = optionalCuriosItemHandler.get();
final IItemHandlerModifiable equippedCurios = curiosItemHandler.getEquippedCurios();

for (int slotIndex = 0; slotIndex < equippedCurios.getSlots(); slotIndex++) {
final ItemStack itemContainer = equippedCurios.getStackInSlot(slotIndex);

if (!ContainerType.canDoItemPickup(itemContainer)) continue;
final Inventory inventory = player.getInventory();
remainder = topOffPlayerInventory(inventory, remainder);

final Optional<IItemHandler> containerInv = itemContainer.getCapability(ForgeCapabilities.ITEM_HANDLER)
.resolve()
.filter(h -> isValidForContainer(h, itemPickup));
if (remainder.isEmpty()) return ItemStack.EMPTY;

if (containerInv.isEmpty()) continue;

remainder = insertStack(itemPickup, containerInv.get());

if (remainder.isEmpty()) continue;
if (SNSConfig.SERVER.doVoiding.get() && !ContainerType.canDoItemVoiding(itemContainer)) continue;
if (ModList.get().isLoaded(CuriosApi.MODID)) {
final var maybeCuriosItemHandler = CuriosApi.getCuriosInventory(player).resolve();

if (!voidedItem(remainder, containerInv.get())) continue;
if (maybeCuriosItemHandler.isPresent()) {
final var curiosItemHandler = maybeCuriosItemHandler.get();
final var equippedCurios = curiosItemHandler.getEquippedCurios();

return ItemStack.EMPTY;
}
remainder = insertItemPickup(equippedCurios, remainder, equippedCurios.getSlots());
if (remainder.isEmpty()) return ItemStack.EMPTY;
}
}

for (int slotIndex = 0; slotIndex < playerInventory.getContainerSize(); slotIndex++) {
final ItemStack itemContainer = playerInventory.getItem(slotIndex);

if (!ContainerType.canDoItemPickup(itemContainer)) continue;

final Optional<IItemHandler> containerInv = itemContainer.getCapability(ForgeCapabilities.ITEM_HANDLER)
.resolve()
.filter(h -> isValidForContainer(h, itemPickup));

if (containerInv.isEmpty()) continue;

remainder = insertStack(itemPickup, containerInv.get());

if (remainder.isEmpty()) continue;
if (SNSConfig.SERVER.doVoiding.get() && !ContainerType.canDoItemVoiding(itemContainer)) continue;

if (!voidedItem(remainder, containerInv.get())) continue;
remainder = insertItemPickup(new PlayerMainInvWrapper(inventory), remainder, Inventory.INVENTORY_SIZE);

return ItemStack.EMPTY;
}
return remainder;
}

/**
* Tries to fill the provided handler until it runs out of capacity or the fillStack runs out.
* Inserts the picked up item into the provided {@link IItemHandler}
*
* @param fillStack The stack to put into the {@link IItemHandler}. May be mutated
* @param itemHandler The {@link IItemHandler} to insert into
* @param itemPickup The {@link ItemStack} to pickup
* @param slotCount The slot count
*
* @return The remaining items that didn't fit
* @return The remaining items
*/
private static ItemStack insertStack(final ItemStack fillStack, final IItemHandler itemHandler) {
ItemStack pickupResult = fillStack;
for (int slotIndex = 0; slotIndex < itemHandler.getSlots(); slotIndex++) {
if (itemHandler.getStackInSlot(slotIndex).getCount() >= itemHandler.getSlotLimit(slotIndex)) continue;
private static ItemStack insertItemPickup(final IItemHandler itemHandler, final ItemStack itemPickup, final int slotCount) {
ItemStack remainder = itemPickup;
for (int slotIndex = 0; slotIndex < slotCount; slotIndex++) {
final ItemStack itemContainer = itemHandler.getStackInSlot(slotIndex);

pickupResult = itemHandler.insertItem(slotIndex, fillStack, false);
if (!ContainerType.canDoItemPickup(itemContainer)) continue;

final var maybeContainerInv = itemContainer.getCapability(ForgeCapabilities.ITEM_HANDLER).resolve();

if (maybeContainerInv.isEmpty()) continue;

remainder = ItemHandlerHelper.insertItem(maybeContainerInv.get(), remainder, false);

if (remainder.isEmpty()) return ItemStack.EMPTY;
if (SNSConfig.SERVER.doVoiding.get() && !ContainerType.canDoItemVoiding(itemContainer)) continue;

if (pickupResult.isEmpty()) return ItemStack.EMPTY;
if (!voidedItem(remainder, maybeContainerInv.get())) return ItemStack.EMPTY;

fillStack.shrink(fillStack.getCount() - pickupResult.getCount());
return ItemStack.EMPTY;
}
return pickupResult;
return remainder;
}

/**
* @param itemStack The Item Stack to try and void. Will be modified if successful
* @param itemStack The Item Stack to try and void
*
* @return If the item was voided.
* @return If the item was voided
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean voidedItem(final ItemStack itemStack, final IItemHandler itemHandler) {
// Make sure there's a slot with the same type of item before voiding the pickup
for (int slotIndex = 0; slotIndex < itemHandler.getSlots(); slotIndex++) {
final ItemStack slotStack = itemHandler.getStackInSlot(slotIndex);
if (!ItemStack.isSameItem(slotStack, itemStack)) continue;

itemStack.setCount(0);
return true;
}
return false;
}

@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean isValidForContainer(final IItemHandler containerInv, final ItemStack itemPickup) {
for (int slotIndex = 0; slotIndex < containerInv.getSlots(); slotIndex++) {
if (containerInv.isItemValid(slotIndex, itemPickup)) return true;
}
return false;
}

/**
* Tops off the player inventory consuming the itemstack until all stacks in the inventory are filled
*
* @param inventoryPlayer Player inventory we should top up
* @param itemStack The itemstack we consume to fill the inventory
* @param inventoryPlayer Player inventory handler
* @param insertStack The {@link ItemStack} we insert into the inventory
*
* @return If the item stack was fully consumed
*/
private static boolean topOffPlayerInventory(final Inventory inventoryPlayer, final ItemStack itemStack) {
private static ItemStack topOffPlayerInventory(final Inventory inventoryPlayer, final ItemStack insertStack) {
ItemStack remainder = insertStack;
// Add to player inventory first, if there is an incomplete stack in there.
for (int i = 0; i < inventoryPlayer.getContainerSize(); i++) {
final ItemStack inventoryStack = inventoryPlayer.getItem(i);
for (int slotIndex = 0; slotIndex < inventoryPlayer.getContainerSize(); slotIndex++) {
final ItemStack inventoryStack = inventoryPlayer.getItem(slotIndex);

// We only add to existing stacks.
if (inventoryStack.isEmpty()) continue;
Expand All @@ -243,22 +215,21 @@ private static boolean topOffPlayerInventory(final Inventory inventoryPlayer, fi
if (inventoryStack.getCount() >= inventoryStack.getMaxStackSize()) continue;

// Can merge stacks
if (ItemStack.isSameItemSameTags(inventoryStack, itemStack)) {
if (ItemStack.isSameItemSameTags(inventoryStack, remainder)) {
final int remainingSpace = inventoryStack.getMaxStackSize() - inventoryStack.getCount();

if (remainingSpace >= itemStack.getCount()) {
if (remainingSpace >= remainder.getCount()) {
// Enough space to add all
inventoryStack.grow(itemStack.getCount());
itemStack.setCount(0);
return true;
inventoryStack.grow(remainder.getCount());
return ItemStack.EMPTY;
} else {
// Only part can be added
inventoryStack.setCount(inventoryStack.getMaxStackSize());
itemStack.shrink(remainingSpace);
remainder = ItemHandlerHelper.copyStackWithSize(remainder, remainder.getCount() - remainingSpace);
}
}
}
return false;
return remainder;
}

/**
Expand Down

0 comments on commit 3264dc7

Please sign in to comment.