From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Wed, 14 Dec 2022 14:47:06 +0800 Subject: [PATCH] Stackable ShulkerBoxes This patch is Powered by fabric-carpet(https://github.com/gnembon/fabric-carpet) and plusls-carpet-addition(https://github.com/plusls/plusls-carpet-addition) diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java index eb0351aa12eebcefab1d1d14641fc3c60cbbcab8..e2f3044ce964f0da9df83b8a302316d2438384e7 100644 --- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java @@ -14,10 +14,12 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.TraceableEntity; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.Vec3; import net.minecraft.core.BlockPos; @@ -33,6 +35,7 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stats; import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.entity.Player; +import org.bukkit.block.ShulkerBox; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.PlayerPickupItemEvent; // CraftBukkit end @@ -71,6 +74,13 @@ public class ItemEntity extends Entity implements TraceableEntity { this.setDeltaMovement(this.random.nextDouble() * 0.2D - 0.1D, 0.2D, this.random.nextDouble() * 0.2D - 0.1D); this.setItem(stack); // Paper end + // Leaves start - stackable shulker boxes + if (top.leavesmc.leaves.LeavesConfig.shulkerBoxStackSize > 1) { + if (stack.getItem() instanceof BlockItem bi && bi.getBlock() instanceof ShulkerBoxBlock) { + top.leavesmc.leaves.util.ShulkerBoxUtils.cleanUpShulkerBoxTag(stack); + } + } + // Leaves end - stackable shulker boxes } public ItemEntity(Level world, double x, double y, double z, ItemStack stack, double velocityX, double velocityY, double velocityZ) { @@ -289,10 +299,49 @@ public class ItemEntity extends Entity implements TraceableEntity { private boolean isMergable() { ItemStack itemstack = this.getItem(); - return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && itemstack.getCount() < itemstack.getMaxStackSize(); // Paper - respect despawn rate in pickup check. + return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && itemstack.getCount() < top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack); // Paper - respect despawn rate in pickup check. // Leaves - stackable shulker boxes } + // Leaves end - stackable shulker boxes + private boolean tryStackShulkerBoxes(ItemEntity other) { + ItemStack selfStack = this.getItem(); + if (top.leavesmc.leaves.LeavesConfig.shulkerBoxStackSize == 1 || !(selfStack.getItem() instanceof BlockItem bi) || !(bi.getBlock() instanceof ShulkerBoxBlock)) { + return false; + } + + ItemStack otherStack = other.getItem(); + if (selfStack.getItem() == otherStack.getItem() + && !top.leavesmc.leaves.util.ShulkerBoxUtils.shulkerBoxHasItems(selfStack) + && !top.leavesmc.leaves.util.ShulkerBoxUtils.shulkerBoxHasItems(otherStack) + && Objects.equals(selfStack.getTag(), otherStack.getTag()) // empty block entity tags are cleaned up when spawning + && selfStack.getCount() != top.leavesmc.leaves.LeavesConfig.shulkerBoxStackSize) { + int amount = Math.min(otherStack.getCount(), top.leavesmc.leaves.LeavesConfig.shulkerBoxStackSize - selfStack.getCount()); + + selfStack.grow(amount); + this.setItem(selfStack); + + this.pickupDelay = Math.max(other.pickupDelay, this.pickupDelay); + this.age = Math.min(other.getAge(), this.age); + + otherStack.shrink(amount); + if (otherStack.isEmpty()) { + other.discard(); + } + else { + other.setItem(otherStack); + } + return true; + } + return false; + } + // Leaves end - stackable shulker boxes + private void tryToMerge(ItemEntity other) { + // Leaves start - stackable shulker boxes + if (tryStackShulkerBoxes(other)) { + return; + } + // Leaves end - stackable shulker boxes ItemStack itemstack = this.getItem(); ItemStack itemstack1 = other.getItem(); diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java index 41cb0d1efd81b7754b68c89af289c1260779c08b..75a26684dc892f96e9209ef77709d1b445bef636 100644 --- a/src/main/java/net/minecraft/world/entity/player/Inventory.java +++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java @@ -114,7 +114,7 @@ public class Inventory implements Container, Nameable { } private boolean hasRemainingSpaceForItem(ItemStack existingStack, ItemStack stack) { - return !existingStack.isEmpty() && ItemStack.isSameItemSameTags(existingStack, stack) && existingStack.isStackable() && existingStack.getCount() < existingStack.getMaxStackSize() && existingStack.getCount() < this.getMaxStackSize(); + return !existingStack.isEmpty() && ItemStack.isSameItemSameTags(existingStack, stack) && top.leavesmc.leaves.util.ShulkerBoxUtils.isStackable(existingStack) && existingStack.getCount() < top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(existingStack) && existingStack.getCount() < this.getMaxStackSize(); // Leaves - stackable shulker boxes } // CraftBukkit start - Watch method above! :D @@ -289,9 +289,11 @@ public class Inventory implements Container, Nameable { int k = j; - if (j > itemstack1.getMaxStackSize() - itemstack1.getCount()) { - k = itemstack1.getMaxStackSize() - itemstack1.getCount(); + // Leaves start - stackable shulker boxes + if (j > top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack1) - itemstack1.getCount()) { + k = top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack1) - itemstack1.getCount(); } + // Leaves start - stackable shulker boxes if (k > this.getMaxStackSize() - itemstack1.getCount()) { k = this.getMaxStackSize() - itemstack1.getCount(); @@ -409,7 +411,7 @@ public class Inventory implements Container, Nameable { } if (i != -1) { - int j = stack.getMaxStackSize() - this.getItem(i).getCount(); + int j = top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(stack) - this.getItem(i).getCount(); // Leaves - stackable shulker boxes if (this.add(i, stack.split(j)) && notifiesClient && this.player instanceof ServerPlayer) { ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getItem(i))); diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java index bc734baa18eb12b499bca2c0fc6b8d8960b3ba26..0c87b9a7ef04fbe500a3c903099c2349dd449438 100644 --- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -30,8 +30,10 @@ import net.minecraft.world.entity.SlotAccess; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.entity.BlockEntity; import org.slf4j.Logger; @@ -456,7 +458,7 @@ public abstract class AbstractContainerMenu { if (slot1 != null && AbstractContainerMenu.canItemQuickReplace(slot1, itemstack2, true) && slot1.mayPlace(itemstack2) && (this.quickcraftType == 2 || itemstack2.getCount() >= this.quickcraftSlots.size()) && this.canDragTo(slot1)) { int j1 = slot1.hasItem() ? slot1.getItem().getCount() : 0; - int k1 = Math.min(itemstack1.getMaxStackSize(), slot1.getMaxStackSize(itemstack1)); + int k1 = Math.min(top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack1), slot1.getMaxStackSize(itemstack1)); // Leaves - stackable shulker boxes int l1 = Math.min(AbstractContainerMenu.getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemstack1) + j1, k1); int l1 = Math.min(AbstractContainerMenu.getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemstack1) + j1, k1); l -= l1 - j1; @@ -575,7 +577,7 @@ public abstract class AbstractContainerMenu { slot.setByPlayer(itemstack3); } } else if (ItemStack.isSameItemSameTags(itemstack, itemstack3)) { - Optional optional1 = slot.tryRemove(itemstack.getCount(), itemstack3.getMaxStackSize() - itemstack3.getCount(), player); + Optional optional1 = slot.tryRemove(itemstack.getCount(), top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack3) - itemstack3.getCount(), player); optional1.ifPresent((itemstack4) -> { itemstack3.grow(itemstack4.getCount()); @@ -642,7 +644,7 @@ public abstract class AbstractContainerMenu { slot2 = (Slot) this.slots.get(slotIndex); if (slot2.hasItem()) { itemstack1 = slot2.getItem(); - this.setCarried(itemstack1.copyWithCount(itemstack1.getMaxStackSize())); + this.setCarried(itemstack1.copyWithCount(top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack1))); // Leaves - stackable shulker boxes } } else if (actionType == ClickType.THROW && this.getCarried().isEmpty() && slotIndex >= 0) { slot2 = (Slot) this.slots.get(slotIndex); @@ -657,14 +659,14 @@ public abstract class AbstractContainerMenu { j2 = button == 0 ? 1 : -1; for (i2 = 0; i2 < 2; ++i2) { - for (int k2 = l; k2 >= 0 && k2 < this.slots.size() && itemstack1.getCount() < itemstack1.getMaxStackSize(); k2 += j2) { + for (int k2 = l; k2 >= 0 && k2 < this.slots.size() && itemstack1.getCount() < top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack1); k2 += j2) { // Leaves - stackable shulker boxes Slot slot3 = (Slot) this.slots.get(k2); if (slot3.hasItem() && AbstractContainerMenu.canItemQuickReplace(slot3, itemstack1, true) && slot3.mayPickup(player) && this.canTakeItemForPickAll(itemstack1, slot3)) { ItemStack itemstack4 = slot3.getItem(); - if (i2 != 0 || itemstack4.getCount() != itemstack4.getMaxStackSize()) { - ItemStack itemstack5 = slot3.safeTake(itemstack4.getCount(), itemstack1.getMaxStackSize() - itemstack1.getCount(), player); + if (i2 != 0 || itemstack4.getCount() != top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack4)) { // Leaves - stackable shulker boxes + ItemStack itemstack5 = slot3.safeTake(itemstack4.getCount(), top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack1) - itemstack1.getCount(), player); // Leaves - stackable shulker boxes itemstack1.grow(itemstack5.getCount()); } @@ -801,7 +803,7 @@ public abstract class AbstractContainerMenu { Slot slot; ItemStack itemstack1; - if (stack.isStackable()) { + if (top.leavesmc.leaves.util.ShulkerBoxUtils.isStackable(stack)) { // Leaves - stackable shulker boxes while (!stack.isEmpty()) { if (fromLast) { if (k < startIndex) { @@ -821,16 +823,18 @@ public abstract class AbstractContainerMenu { if (!itemstack1.isEmpty() && ItemStack.isSameItemSameTags(stack, itemstack1)) { int l = itemstack1.getCount() + stack.getCount(); - if (l <= stack.getMaxStackSize()) { + if (l <= top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(stack)) { // Leaves - stackable shulker boxes stack.setCount(0); itemstack1.setCount(l); if (!isCheck) { // Paper - dont update if only a check slot.setChanged(); } // Paper flag1 = true; - } else if (itemstack1.getCount() < stack.getMaxStackSize()) { - stack.shrink(stack.getMaxStackSize() - itemstack1.getCount()); - itemstack1.setCount(stack.getMaxStackSize()); + // Leaves start - stackable shulker boxes + } else if (itemstack1.getCount() < top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(stack)) { + stack.shrink(top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(stack) - itemstack1.getCount()); + itemstack1.setCount(top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(stack)); + // Leaves end - stackable shulker boxes if (!isCheck) { // Paper - dont update if only a check slot.setChanged(); } // Paper diff --git a/src/main/java/net/minecraft/world/inventory/Slot.java b/src/main/java/net/minecraft/world/inventory/Slot.java index a43b85b82fc83d0e5e3fd589bede0b279927a989..4e68e77180df7929d8993f049fc73c90f0158a2c 100644 --- a/src/main/java/net/minecraft/world/inventory/Slot.java +++ b/src/main/java/net/minecraft/world/inventory/Slot.java @@ -77,7 +77,7 @@ public class Slot { } public int getMaxStackSize(ItemStack stack) { - return Math.min(this.getMaxStackSize(), stack.getMaxStackSize()); + return Math.min(this.getMaxStackSize(), top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(stack)); // Leaves - stackable shulker boxes } @Nullable diff --git a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java index 47468086c1cae252aa99c55b0065f225357dee62..bca757ea05403fe46f5bf0dfa75561b8b124622d 100644 --- a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java +++ b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java @@ -9,6 +9,7 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; @@ -51,9 +52,27 @@ public abstract class AbstractCauldronBlock extends Block { public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { ItemStack itemStack = player.getItemInHand(hand); CauldronInteraction cauldronInteraction = this.interactions.get(itemStack.getItem()); - return cauldronInteraction.interact(state, world, pos, player, hand, itemStack); + return wrapInteractor(cauldronInteraction, state, world, pos, player, hand, itemStack); // Leaves - stackable shulker boxes } + // Leaves start - stackable shulker boxes + private InteractionResult wrapInteractor(CauldronInteraction cauldronBehavior, BlockState blockState, Level world, BlockPos blockPos, Player playerEntity, InteractionHand hand, ItemStack itemStack) { + int count = -1; + if (top.leavesmc.leaves.LeavesConfig.shulkerBoxStackSize > 1 && itemStack.getItem() instanceof BlockItem bi && + bi.getBlock() instanceof ShulkerBoxBlock) { + count = itemStack.getCount(); + } + InteractionResult result = cauldronBehavior.interact(blockState, world, blockPos, playerEntity, hand, itemStack); + if (count > 0 && result.consumesAction()) { + ItemStack current = playerEntity.getItemInHand(hand); + if (current.getItem() instanceof BlockItem bi && bi.getBlock() instanceof ShulkerBoxBlock) { + current.setCount(count); + } + } + return result; + } + // Leaves end - stackable shulker boxes + @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java index 88ae3b334e35ac9d0ca4238b5da3c3af1f421bd4..9310444c04608a0015844eafa872f24c8baedf4d 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -733,9 +733,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen if (itemstack1.isEmpty()) { // Spigot start - SPIGOT-6693, InventorySubcontainer#setItem ItemStack leftover = ItemStack.EMPTY; // Paper - if (!stack.isEmpty() && stack.getCount() > to.getMaxStackSize()) { + if (!stack.isEmpty() && (stack.getCount() > to.getMaxStackSize() || stack.getCount() > stack.getMaxStackSize())) { // Leaves - stackable shulker boxes leftover = stack; // Paper - stack = stack.split(to.getMaxStackSize()); + stack = stack.split(Math.min(to.getMaxStackSize(), stack.getMaxStackSize())); // Leaves - stackable shulker boxes } // Spigot end ignoreTileUpdates = true; // Paper diff --git a/src/main/java/top/leavesmc/leaves/util/ShulkerBoxUtils.java b/src/main/java/top/leavesmc/leaves/util/ShulkerBoxUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..82b4337965663ec8eccbc8c77892e3c49bff87cf --- /dev/null +++ b/src/main/java/top/leavesmc/leaves/util/ShulkerBoxUtils.java @@ -0,0 +1,71 @@ +package top.leavesmc.leaves.util; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import org.jetbrains.annotations.NotNull; +import top.leavesmc.leaves.LeavesConfig; + +// Powered by fabric-carpet/src/main/java/carpet/helpers/InventoryHelper.java +public class ShulkerBoxUtils { + // From nbt/NbtElement.java createTag() + public static final int TAG_END = 0; + public static final int TAG_BYTE = 1; + public static final int TAG_SHORT = 2; + public static final int TAG_INT = 3; + public static final int TAG_LONG = 4; + public static final int TAG_FLOAT = 5; + public static final int TAG_DOUBLE = 6; + public static final int TAG_BYTEARRAY = 7; + public static final int TAG_STRING = 8; + public static final int TAG_LIST = 9; + public static final int TAG_COMPOUND = 10; + public static final int TAG_INTARRAY = 11; + public static final int TAG_LONGARRAY = 12; + + public static boolean cleanUpShulkerBoxTag(@NotNull ItemStack stack) { + boolean changed = false; + CompoundTag tag = stack.getTag(); + + if (tag == null || !tag.contains("BlockEntityTag", TAG_COMPOUND)) return false; + + CompoundTag bet = tag.getCompound("BlockEntityTag"); + if (bet.contains("Items", TAG_LIST) && bet.getList("Items", TAG_COMPOUND).isEmpty()) { + bet.remove("Items"); + changed = true; + } + + if (bet.isEmpty() || (bet.size() == 1 && bet.getString("id").equals("minecraft:shulker_box"))) { + tag.remove("BlockEntityTag"); + changed = true; + } + if (tag.isEmpty()) { + stack.setTag(null); + changed = true; + } + return changed; + } + + public static boolean shulkerBoxHasItems(@NotNull ItemStack stack) { + CompoundTag tag = stack.getTag(); + + if (tag == null || !tag.contains("BlockEntityTag", TAG_COMPOUND)) return false; + + CompoundTag bet = tag.getCompound("BlockEntityTag"); + return bet.contains("Items", TAG_LIST) && !bet.getList("Items", TAG_COMPOUND).isEmpty(); + } + + public static int getItemStackMaxCount(ItemStack stack) { + if (LeavesConfig.shulkerBoxStackSize > 1 && stack.getItem() instanceof BlockItem bi && + bi.getBlock() instanceof ShulkerBoxBlock && !top.leavesmc.leaves.util.ShulkerBoxUtils.shulkerBoxHasItems(stack)) { + return top.leavesmc.leaves.LeavesConfig.shulkerBoxStackSize; + } + return stack.getMaxStackSize(); + } + + public static boolean isStackable(ItemStack itemStack) { + return getItemStackMaxCount(itemStack) > 1 && (!itemStack.isDamageableItem() || !itemStack.isDamaged()); + } +}