9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-22 08:29:22 +00:00
Files
LeavesMC/patches/server/0045-Stackable-ShulkerBoxes.patch
2024-02-11 14:30:44 +08:00

352 lines
21 KiB
Diff

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 eb0d6238588efa35fa868f26290547574a08eca2..adc179f85a774c47d386144da6b2291e915a0e1d 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
@@ -73,6 +76,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 - Don't use level random in entity constructors
+ // 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) {
@@ -307,10 +317,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 - Alternative item-despawn-rate
+ return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && itemstack.getCount() < top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack); // Paper - Alternative item-despawn-rate // 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 d2f6027056af9d2816542f4e3d9e278d9ec2c9b4..10db6853dafd8f757499992af84c6b618caf2eeb 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 7cef5c518207752f7e1bfdd5bbec55fe9fafca6b..925cc4d99d1388bc32cebb5238a322102b6f68e5 100644
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -456,7 +456,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 +575,7 @@ public abstract class AbstractContainerMenu {
slot.setByPlayer(itemstack3);
}
} else if (ItemStack.isSameItemSameTags(itemstack, itemstack3)) {
- Optional<ItemStack> optional1 = slot.tryRemove(itemstack.getCount(), itemstack3.getMaxStackSize() - itemstack3.getCount(), player);
+ Optional<ItemStack> optional1 = slot.tryRemove(itemstack.getCount(), top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack3) - itemstack3.getCount(), player);
optional1.ifPresent((itemstack4) -> {
itemstack3.grow(itemstack4.getCount());
@@ -645,7 +645,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);
@@ -660,13 +660,13 @@ 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 itemstack5 = slot3.getItem();
- if (i2 != 0 || itemstack5.getCount() != itemstack5.getMaxStackSize()) {
+ if (i2 != 0 || itemstack5.getCount() != top.leavesmc.leaves.util.ShulkerBoxUtils.getItemStackMaxCount(itemstack5)) {
ItemStack itemstack6 = slot3.safeTake(itemstack5.getCount(), itemstack1.getMaxStackSize() - itemstack1.getCount(), player);
itemstack1.grow(itemstack6.getCount());
@@ -805,7 +805,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) {
@@ -825,16 +825,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 - Add PlayerTradeEvent and PlayerPurchaseEvent
slot.setChanged();
} // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
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 - Add PlayerTradeEvent and PlayerPurchaseEvent
slot.setChanged();
} // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
diff --git a/src/main/java/net/minecraft/world/inventory/Slot.java b/src/main/java/net/minecraft/world/inventory/Slot.java
index e2fd415b69e0213688561e9a19aec2cdcd267211..bb99bdd45efdb52323429565a4e13c07f5ab6d44 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 5780d349f968ea3d05cf569b63531f8e76884827..d30af75de7d146b6bf49814b0eabae94e57682f5 100644
--- a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
@@ -53,9 +53,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.map().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 net.minecraft.world.item.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 net.minecraft.world.item.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 dfc623059f9c5920883a06f9662f1bf3b7a2c4c6..46eeae81a7cda8fab80a255a57dcf096b37461bd 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 - Make hoppers respect inventory max stack size
- 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 - Make hoppers respect inventory max stack size
- stack = stack.split(to.getMaxStackSize());
+ stack = stack.split(Math.min(to.getMaxStackSize(), stack.getMaxStackSize())); // Leaves - stackable shulker boxes
}
// Spigot end
ignoreTileUpdates = true; // Paper - Perf: Optimize Hoppers
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());
+ }
+}