diff --git a/dependencies.gradle b/dependencies.gradle index 60debbff..16e4114d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -38,7 +38,6 @@ dependencies { implementation rfg.deobf("curse.maven:baubles-227083:2518667") implementation rfg.deobf("curse.maven:shadowfacts-forgelin-248453:2785465") implementation rfg.deobf("curse.maven:ae-additions-extra-cells-2-fork-493962:3814371") - implementation rfg.deobf("curse.maven:ez-storage2-245425:2530747") // Soft Dependencies implementation "CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.1.20.687" @@ -65,7 +64,13 @@ dependencies { // Debug Chisel compileOnly rfg.deobf("curse.maven:chisel-235279:2915375") if (project.debug_chisel.toBoolean()) { - implementation rfg.deobf("curse.maven:chisel-235279:2915375") + runtimeOnly rfg.deobf("curse.maven:chisel-235279:2915375") + } + + // Debug mixins + compileOnly rfg.deobf("curse.maven:ez-storage2-245425:2530747") + if (project.debug_mixins.toBoolean()) { + runtimeOnly rfg.deobf("curse.maven:ez-storage2-245425:2530747") } // Boot error fix diff --git a/gradle.properties b/gradle.properties index 1b051295..2d13f8ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,6 +21,7 @@ minecraftVersion = 1.12.2 # Debug mod compatibility debug_de = false debug_chisel = false +debug_mixins = false # Select a username for testing your mod with breakpoints. You may leave this empty for a random username each time you # restart Minecraft in development. Choose this dependent on your mod: @@ -53,7 +54,7 @@ accessTransformersFile = # Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! usesMixins = true # Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! -mixinsPackage = mixins +mixinsPackage = mixins.impl # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! # Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin coreModClass = GTEMixinPlugin diff --git a/src/main/java/gtexpert/GTEMixinLoader.java b/src/main/java/gtexpert/GTEMixinLoader.java index feb19cdc..01560a43 100644 --- a/src/main/java/gtexpert/GTEMixinLoader.java +++ b/src/main/java/gtexpert/GTEMixinLoader.java @@ -1,14 +1,19 @@ package gtexpert; +import net.minecraftforge.fml.common.Loader; + import com.google.common.collect.Lists; import zone.rong.mixinbooter.ILateMixinLoader; +import java.util.Collections; import java.util.List; public class GTEMixinLoader implements ILateMixinLoader { @Override public List getMixinConfigs() { - return Lists.newArrayList("mixins.gtexpert.json"); + if (Loader.isModLoaded("ezstorage")) + return Lists.newArrayList("mixins.gtexpert.json"); + return Collections.emptyList(); } } diff --git a/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinContainerStorageCore.java b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinContainerStorageCore.java new file mode 100644 index 00000000..e1f9aca7 --- /dev/null +++ b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinContainerStorageCore.java @@ -0,0 +1,109 @@ +package gtexpert.mixins.impl.ezstorage2; + +import gtexpert.mixins.interfaces.ezstorage2.IMixinEZInventory; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.*; +import net.minecraft.item.ItemStack; + +import com.zerofall.ezstorage.gui.server.ContainerStorageCore; +import com.zerofall.ezstorage.tileentity.TileEntityStorageCore; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import org.jetbrains.annotations.NotNull; + +@Mixin(ContainerStorageCore.class) +public abstract class MixinContainerStorageCore extends Container { + + @Shadow(remap = false) + private TileEntityStorageCore tileEntity; + + @Invoker(value = "rowCount", remap = false) + protected abstract int invokeRowCount(); + + @Inject(method = "slotClick", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/inventory/Container;slotClick(IILnet/minecraft/inventory/ClickType;Lnet/minecraft/entity/player/EntityPlayer;)Lnet/minecraft/item/ItemStack;"), + remap = false, + locals = LocalCapture.CAPTURE_FAILHARD, + cancellable = true) + private void injectSlotClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player, + CallbackInfoReturnable cir, ItemStack val) { + if (slotId != -999) { + Slot slot = this.getSlot(slotId); + if (!(slot instanceof SlotCrafting) && clickTypeIn == ClickType.QUICK_MOVE && slot.canTakeStack(player)) { + ItemStack itemStack = slot.getStack(); + ItemStack result = this.tileEntity.inventory.input(itemStack, true); + slot.onSlotChanged(); + super.detectAndSendChanges(); + cir.setReturnValue(result.copy()); + } + } + } + + @Inject(method = "customSlotClick", at = @At(value = "HEAD"), remap = false, cancellable = true) + private void injectCustomSlotClick(int slotId, int clickedButton, int mode, EntityPlayer playerIn, + CallbackInfoReturnable cir) { + // Always return EMPTY since this return value is never used + cir.setReturnValue(ItemStack.EMPTY); + + int type = 0; + if (clickedButton == 1) { + type = (mode == 0) ? 1 : 2; + } + + // isShiftLeftClick + if (clickedButton == 0 && mode == 1) { + int playerInventoryStartIndex = this.invokeRowCount() * 9; + int playerInventoryEndIndex = playerInventoryStartIndex + playerIn.inventory.mainInventory.size(); + + if (playerIn.inventory.getFirstEmptyStack() < 0) { + ItemStack targetStack = ((IMixinEZInventory) (Object) this.tileEntity.inventory) + .getItemWithoutExtractAt(slotId); + int emptyCapacity = this.inventorySlots.subList(playerInventoryStartIndex, playerInventoryEndIndex) + .stream() + .mapToInt(slot -> { + ItemStack slotStack = slot.getStack(); + if (slotStack.isItemEqual(targetStack) && + ItemStack.areItemStackTagsEqual(slotStack, targetStack)) { + return slotStack.getMaxStackSize() - slotStack.getCount(); + } + return 0; + }).sum(); + + ItemStack retrievedStack = this.tileEntity.inventory.getItemsAt(slotId, type, + Math.min(emptyCapacity, targetStack.getMaxStackSize())); + if (!retrievedStack.isEmpty()) { + this.mergeItemStack(retrievedStack, playerInventoryStartIndex, playerInventoryEndIndex, true); + } + } else { + ItemStack retrievedStack = this.tileEntity.inventory.getItemsAt(slotId, type); + if (!retrievedStack.isEmpty()) { + this.mergeItemStack(retrievedStack, playerInventoryStartIndex, playerInventoryEndIndex, true); + } + } + } else { + ItemStack heldStack = playerIn.inventory.getItemStack(); + if (heldStack.isEmpty()) { + ItemStack retrievedStack = this.tileEntity.inventory.getItemsAt(slotId, type); + playerIn.inventory.setItemStack(retrievedStack); + } else if (clickedButton == 0) { + playerIn.inventory.setItemStack(this.tileEntity.inventory.input(heldStack)); + } else if (clickedButton == 1 && mode != 1) { + playerIn.inventory + .setItemStack(((IMixinEZInventory) (Object) this.tileEntity.inventory).input(heldStack, 1)); + } + } + } + + @Override + public boolean canDragIntoSlot(@NotNull Slot slotIn) { + return !(slotIn.inventory instanceof InventoryBasic); + } +} diff --git a/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinContainerStorageCoreCrafting.java b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinContainerStorageCoreCrafting.java new file mode 100644 index 00000000..fc464b14 --- /dev/null +++ b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinContainerStorageCoreCrafting.java @@ -0,0 +1,68 @@ +package gtexpert.mixins.impl.ezstorage2; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import com.zerofall.ezstorage.gui.server.ContainerStorageCore; +import com.zerofall.ezstorage.gui.server.ContainerStorageCoreCrafting; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.*; + +@Mixin(ContainerStorageCoreCrafting.class) +public class MixinContainerStorageCoreCrafting extends ContainerStorageCore { + + public MixinContainerStorageCoreCrafting(EntityPlayer player, World world, int x, int y, int z) { + super(player, world, x, y, z); + } + + @Redirect(method = "transferStackInSlot", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/item/ItemStack;isItemEqual(Lnet/minecraft/item/ItemStack;)Z", + ordinal = 0), + remap = false) + private boolean redirectTransferStackInSlotIsItemEqual0(ItemStack stack, ItemStack other) { + return true; + } + + @Redirect(method = "transferStackInSlot", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/item/ItemStack;isItemEqual(Lnet/minecraft/item/ItemStack;)Z", + ordinal = 1), + remap = false) + private boolean redirectTransferStackInSlotIsItemEqual1(ItemStack stack, ItemStack other) { + return false; + } + + @Redirect(method = "transferStackInSlot", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/inventory/InventoryCrafting;getStackInSlot(I)Lnet/minecraft/item/ItemStack;", + ordinal = 0), + remap = false) + private ItemStack redirectTransferStackInSlotGetStackInSlot(InventoryCrafting inv, int index) { + return inv.getStackInSlot(index).copy(); + } + + @ModifyConstant(method = "tryToPopulateCraftingGrid", + constant = @Constant(intValue = 1), + slice = @Slice(from = @At("HEAD"), + to = @At(value = "CONSTANT", + args = "intValue=1", + ordinal = 0)), + remap = false) + private int modifyConstantTryToPopulateCraftingGrid(int original) { + return Integer.MAX_VALUE; + } + + @Redirect(method = "tryToPopulateCraftingGrid", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/item/ItemStack;setCount(I)V"), + remap = false) + private void redirectTryToPopulateCraftingGridSetCount(ItemStack itemStack, int count) { + if (itemStack.getCount() > 1) { + itemStack.setCount(itemStack.getCount() - 1); + } + } +} diff --git a/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinEZInventory.java b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinEZInventory.java new file mode 100644 index 00000000..27cbec92 --- /dev/null +++ b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinEZInventory.java @@ -0,0 +1,78 @@ +package gtexpert.mixins.impl.ezstorage2; + +import gtexpert.mixins.interfaces.ezstorage2.IMixinEZInventory; + +import net.minecraft.item.ItemStack; + +import com.zerofall.ezstorage.util.EZInventory; +import com.zerofall.ezstorage.util.ItemGroup; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.List; + +@Mixin(EZInventory.class) +public abstract class MixinEZInventory implements IMixinEZInventory { + + @Shadow(remap = false) + private List inventory; + + @Invoker(value = "extractStack", remap = false) + protected abstract ItemStack invokeExtractStack(ItemGroup group, int size, boolean peek); + + @Inject(method = "getItemsAt(IIIZ)Lnet/minecraft/item/ItemStack;", + at = @At(value = "INVOKE", + target = "Lcom/zerofall/ezstorage/util/EZInventory;extractStack(Lcom/zerofall/ezstorage/util/ItemGroup;IZ)Lnet/minecraft/item/ItemStack;", + ordinal = 0), + remap = false, + locals = LocalCapture.CAPTURE_FAILHARD, + cancellable = true) + private void injectGetItemsAtExtractStack(int index, int type, int size, boolean peek, + CallbackInfoReturnable cir, + ItemGroup group, ItemStack stack) { + if (size == 0) { + cir.setReturnValue(ItemStack.EMPTY); + return; + } + + if (size < 1) { + if (type == 1) { + size = (((int) Math.min(stack.getMaxStackSize(), group.count)) + 2 - 1) / 2; + } else if (type == 2) { + size = 1; + } + } + + ItemStack result = this.invokeExtractStack(group, size, peek); + cir.setReturnValue(result); + } + + @Override + public ItemStack getItemWithoutExtractAt(int index) { + if (index >= this.inventory.size()) { + return ItemStack.EMPTY; + } + return this.inventory.get(index).itemStack; + } + + @Override + public ItemStack input(ItemStack itemStack, int quantity, boolean sort) { + int stackCount = itemStack.getCount(); + quantity = Math.min(itemStack.getCount(), quantity); + + ItemStack inputStack = itemStack.copy(); + inputStack.setCount(Math.min(stackCount, quantity)); + ItemStack inputResult = ((EZInventory) (Object) this).input(inputStack, sort); + if (inputResult.isEmpty()) { + itemStack.shrink(quantity); + } else { + itemStack.setCount(stackCount - quantity + inputResult.getCount()); + } + return itemStack; + } +} diff --git a/src/main/java/gtexpert/mixins/MixinItemDolly.java b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinItemDolly.java similarity index 75% rename from src/main/java/gtexpert/mixins/MixinItemDolly.java rename to src/main/java/gtexpert/mixins/impl/ezstorage2/MixinItemDolly.java index 8de39fc9..66db215b 100644 --- a/src/main/java/gtexpert/mixins/MixinItemDolly.java +++ b/src/main/java/gtexpert/mixins/impl/ezstorage2/MixinItemDolly.java @@ -1,4 +1,4 @@ -package gtexpert.mixins; +package gtexpert.mixins.impl.ezstorage2; import net.minecraft.block.Block; import net.minecraft.block.BlockChest; @@ -37,9 +37,9 @@ public MixinItemDolly(String name) { } @Inject(method = "onItemUse", at = @At("HEAD"), remap = false, cancellable = true) - public void injectOnItemUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumHand hand, - EnumFacing facing, float hitX, float hitY, float hitZ, - CallbackInfoReturnable cir) { + private void injectOnItemUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumHand hand, + EnumFacing facing, float hitX, float hitY, float hitZ, + CallbackInfoReturnable cir) { cir.setReturnValue(EnumActionResult.PASS); cir.cancel(); } @@ -82,20 +82,12 @@ public void injectOnItemUse(ItemStack stack, EntityPlayer player, World world, B } if (player.isCreative()) { - nbt.setBoolean("isFull", false); - nbt.removeTag("blockType"); - nbt.removeTag("isChest"); - nbt.removeTag("isStorageCore"); - nbt.removeTag("stored"); + emptyDolly(nbt); } else { ItemStack emptyDolly = heldItem.splitStack(1); NBTTagCompound emptyDollyNBT = emptyDolly.getTagCompound(); if (emptyDollyNBT != null) { - emptyDollyNBT.setBoolean("isFull", false); - emptyDollyNBT.removeTag("blockType"); - emptyDollyNBT.removeTag("isChest"); - emptyDollyNBT.removeTag("isStorageCore"); - emptyDollyNBT.removeTag("stored"); + emptyDolly(emptyDollyNBT); } emptyDolly.damageItem(1, player); @@ -124,13 +116,7 @@ public void injectOnItemUse(ItemStack stack, EntityPlayer player, World world, B return new ActionResult<>(EnumActionResult.PASS, heldItem); } - NBTTagCompound distTag = new NBTTagCompound(); NBTTagCompound storageData = tileEntity.writeToNBT(new NBTTagCompound()); - distTag.setBoolean("isFull", true); - distTag.setString("blockType", Objects.requireNonNull(state.getBlock().getRegistryName()).toString()); - distTag.setBoolean("isChest", isChest); - distTag.setBoolean("isStorageCore", isStorageCore); - distTag.setTag("stored", storageData); if (isStorageCore) { world.setBlockToAir(pos); @@ -141,23 +127,46 @@ public void injectOnItemUse(ItemStack stack, EntityPlayer player, World world, B } if (player.isCreative()) { - heldItem.setTagCompound(distTag); + fillDolly(heldItem, nbt, state, isChest, isStorageCore, storageData); } else { - ItemStack distItem = heldItem.splitStack(1); - distItem.setTagCompound(distTag); + ItemStack filledDolly = heldItem.splitStack(1); + NBTTagCompound filledDollyNBT = filledDolly.getTagCompound(); + fillDolly(filledDolly, filledDollyNBT, state, isChest, isStorageCore, storageData); if (heldItem.isEmpty()) { - return new ActionResult<>(EnumActionResult.SUCCESS, distItem); + return new ActionResult<>(EnumActionResult.SUCCESS, filledDolly); } - if (!player.addItemStackToInventory(distItem)) { - player.dropItem(distItem, false); + if (!player.addItemStackToInventory(filledDolly)) { + player.dropItem(filledDolly, false); } } } return new ActionResult<>(EnumActionResult.SUCCESS, heldItem); } + private void emptyDolly(NBTTagCompound nbt) { + nbt.setBoolean("isFull", false); + nbt.removeTag("blockType"); + nbt.removeTag("isChest"); + nbt.removeTag("isStorageCore"); + nbt.removeTag("stored"); + } + + private void fillDolly(ItemStack dolly, NBTTagCompound dollyNBT, IBlockState state, boolean isChest, + boolean isStorageCore, NBTTagCompound storageData) { + if (dollyNBT == null) { + dollyNBT = new NBTTagCompound(); + dolly.setTagCompound(dollyNBT); + } + + dollyNBT.setBoolean("isFull", true); + dollyNBT.setString("blockType", Objects.requireNonNull(state.getBlock().getRegistryName()).toString()); + dollyNBT.setBoolean("isChest", isChest); + dollyNBT.setBoolean("isStorageCore", isStorageCore); + dollyNBT.setTag("stored", storageData); + } + @Override public int getItemStackLimit(ItemStack stack) { NBTTagCompound nbt = stack.getTagCompound(); diff --git a/src/main/java/gtexpert/mixins/interfaces/ezstorage2/IMixinEZInventory.java b/src/main/java/gtexpert/mixins/interfaces/ezstorage2/IMixinEZInventory.java new file mode 100644 index 00000000..75ed1a1b --- /dev/null +++ b/src/main/java/gtexpert/mixins/interfaces/ezstorage2/IMixinEZInventory.java @@ -0,0 +1,14 @@ +package gtexpert.mixins.interfaces.ezstorage2; + +import net.minecraft.item.ItemStack; + +public interface IMixinEZInventory { + + ItemStack getItemWithoutExtractAt(int index); + + ItemStack input(ItemStack itemStack, int quantity, boolean sort); + + default ItemStack input(ItemStack itemStack, int quantity) { + return this.input(itemStack, quantity, true); + } +} diff --git a/src/main/resources/mixins.gtexpert.json b/src/main/resources/mixins.gtexpert.json index 31e99e45..335d1d59 100644 --- a/src/main/resources/mixins.gtexpert.json +++ b/src/main/resources/mixins.gtexpert.json @@ -1,15 +1,18 @@ { "required": true, - "package": "gtexpert.mixins", + "package": "gtexpert.mixins.impl", "refmap": "mixins.gtexpert.refmap.json", "target": "@env(DEFAULT)", "compatibilityLevel": "JAVA_8", "mixins": [ - "MixinItemDolly" + "ezstorage2.MixinItemDolly", + "ezstorage2.MixinContainerStorageCore", + "ezstorage2.MixinContainerStorageCoreCrafting" ], "server": [ ], "client": [ + "ezstorage2.MixinEZInventory" ], "injectors": { "defaultRequire": 1