From 9e72423a412a384d75b5228a2e19b8a92d227078 Mon Sep 17 00:00:00 2001 From: Marc Hannappel Date: Thu, 16 May 2019 18:05:15 +0200 Subject: [PATCH] Ported "stackable empty shulkerboxes on ground" based on @maruohon's tweakeroo code --- src/main/java/carpet/CarpetSettings.java | 3 +- .../java/carpet/helpers/InventoryHelper.java | 57 +++++++++ .../java/carpet/mixins/ItemEntityMixin.java | 108 ++++++++++++++++++ src/main/java/carpet/utils/IItemEntity.java | 6 + src/main/resources/carpet.mixins.json | 3 +- 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 src/main/java/carpet/helpers/InventoryHelper.java create mode 100644 src/main/java/carpet/mixins/ItemEntityMixin.java create mode 100644 src/main/java/carpet/utils/IItemEntity.java diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index 7ba57e65..0d171a27 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -60,6 +60,7 @@ public class CarpetSettings public static boolean b_disableSpawnChunks = false; public static boolean b_movableTileEntities = false; public static boolean b_huskSpawningInTemples = false; + public static boolean b_stackableShulkerBoxes = false; private static CarpetSettingEntry rule(String s1, String s2, String s3) { return CarpetSettingEntry.create(s1,s2,s3);} @@ -82,7 +83,7 @@ public class CarpetSettings rule("xpNoCooldown", "creative", "Players absorb XP instantly, without delay"), rule("combineXPOrbs", "creative", "XP orbs combine with other into bigger orbs"), rule("stackableShulkerBoxes", "survival", "Empty shulker boxes can stack to 64 when dropped on the ground") - .extraInfo("To move them around between inventories, use shift click to move entire stacks"), + .extraInfo("To move them around between inventories, use shift click to move entire stacks").boolAccelerate().defaultFalse(), rule("explosionNoBlockDamage", "tnt", "Explosions won't destroy blocks"), rule("tntPrimerMomentumRemoved", "tnt", "Removes random TNT momentum when primed"), rule("fastRedstoneDust", "experimental optimizations", "Lag optimizations for redstone dust") diff --git a/src/main/java/carpet/helpers/InventoryHelper.java b/src/main/java/carpet/helpers/InventoryHelper.java new file mode 100644 index 00000000..132f1196 --- /dev/null +++ b/src/main/java/carpet/helpers/InventoryHelper.java @@ -0,0 +1,57 @@ +package carpet.helpers; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundTag; + +public class InventoryHelper +{ + // From nbt/Tag.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(ItemStack stack) + { + boolean changed = false; + CompoundTag tag = stack.getTag(); + + if (tag == null || !tag.containsKey("BlockEntityTag", TAG_COMPOUND)) + return false; + + CompoundTag bet = tag.getCompound("BlockEntityTag"); + if (bet.containsKey("Items", TAG_LIST) && bet.getList("Items", TAG_COMPOUND).isEmpty()) + { + bet.remove("Items"); + changed = true; + } + + if (bet.isEmpty()) + { + stack.setTag(null); + changed = true; + } + + return changed; + } + + public static boolean shulkerBoxHasItems(ItemStack stack) + { + CompoundTag tag = stack.getTag(); + + if (tag == null || !tag.containsKey("BlockEntityTag", TAG_COMPOUND)) + return false; + + CompoundTag bet = tag.getCompound("BlockEntityTag"); + return bet.containsKey("Items", TAG_LIST) && !bet.getList("Items", TAG_COMPOUND).isEmpty(); + } +} diff --git a/src/main/java/carpet/mixins/ItemEntityMixin.java b/src/main/java/carpet/mixins/ItemEntityMixin.java new file mode 100644 index 00000000..a1437c21 --- /dev/null +++ b/src/main/java/carpet/mixins/ItemEntityMixin.java @@ -0,0 +1,108 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.helpers.InventoryHelper; +import carpet.utils.IItemEntity; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ItemEntity.class) +public abstract class ItemEntityMixin extends Entity implements IItemEntity { + private static final int SHULKERBOX_MAX_STACK_AMOUNT = 64; + + @Shadow private int age; + @Shadow private int pickupDelay; + + public ItemEntityMixin(EntityType entityType_1, World world_1) { + super(entityType_1, world_1); + } + + @Override + public int getAge() { + return this.age; + } + + @Override + public int getPickupDelay() { + return this.pickupDelay; + } + + @Inject(method="(Lnet/minecraft/world/World;DDDLnet/minecraft/item/ItemStack;)V", at = @At("RETURN")) + private void removeEmptyShulkerBoxTags(World worldIn, double x, double y, double z, ItemStack stack, CallbackInfo ci) + { + if (CarpetSettings.b_stackableShulkerBoxes + && stack.getItem() instanceof BlockItem + && ((BlockItem)stack.getItem()).getBlock() instanceof ShulkerBoxBlock) + { + if (InventoryHelper.cleanUpShulkerBoxTag(stack)) { + ((ItemEntity) (Object) this).setStack(stack); + } + } + } + + @Redirect( + method = "method_20397", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/item/ItemStack;getMaxAmount()I" + ) + ) + private int getItemStackMaxAmount(ItemStack stack) { + if (CarpetSettings.b_stackableShulkerBoxes && stack.getItem() instanceof BlockItem && ((BlockItem)stack.getItem()).getBlock() instanceof ShulkerBoxBlock) + return SHULKERBOX_MAX_STACK_AMOUNT; + + return stack.getMaxAmount(); + } + + @Inject( + method = "tryMerge(Lnet/minecraft/entity/ItemEntity;)V", + at = @At("HEAD"), + cancellable = true + ) + private void tryStackShulkerBoxes(ItemEntity other, CallbackInfo ci) + { + ItemEntity self = (ItemEntity)(Object)this; + ItemStack selfStack = self.getStack(); + if (!CarpetSettings.b_stackableShulkerBoxes || !(selfStack.getItem() instanceof BlockItem) || !(((BlockItem)selfStack.getItem()).getBlock() instanceof ShulkerBoxBlock)) { + return; + } + + ItemStack otherStack = other.getStack(); + if (selfStack.getItem() == otherStack.getItem() + && !InventoryHelper.shulkerBoxHasItems(selfStack) + && selfStack.hasTag() == otherStack.hasTag() + && selfStack.getAmount() + otherStack.getAmount() <= SHULKERBOX_MAX_STACK_AMOUNT) + { + int amount = Math.min(otherStack.getAmount(), SHULKERBOX_MAX_STACK_AMOUNT - selfStack.getAmount()); + + selfStack.addAmount(amount); + self.setStack(selfStack); + + this.pickupDelay = Math.max(((IItemEntity)other).getPickupDelay(), this.pickupDelay); + this.age = Math.min(other.getAge(), this.age); + + otherStack.subtractAmount(amount); + if (otherStack.isEmpty()) + { + other.remove(); + } + else + { + other.setStack(otherStack); + } + ci.cancel(); + } + } + +} diff --git a/src/main/java/carpet/utils/IItemEntity.java b/src/main/java/carpet/utils/IItemEntity.java new file mode 100644 index 00000000..2b7b2b10 --- /dev/null +++ b/src/main/java/carpet/utils/IItemEntity.java @@ -0,0 +1,6 @@ +package carpet.utils; + +public interface IItemEntity { + int getAge(); + int getPickupDelay(); +} diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index f57c26cd..ef1388d2 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -13,7 +13,8 @@ "ServerPlayerEntityMixin", "ServerChunkManagerMixin", "HopperBlockEntityMixin", - "ClientConnectionMixin" + "ClientConnectionMixin", + "ItemEntityMixin" ], "client": [ "MinecraftClientMixin",