From 0709d2dfc4dbfaf584d6e647b03e01430a8235f5 Mon Sep 17 00:00:00 2001 From: Serenibyss <10861407+serenibyss@users.noreply.github.com> Date: Fri, 12 Jan 2024 01:21:24 -0600 Subject: [PATCH] Color applicator improvements (#360) * Now properly updates the Applicator's set color when a dye runs out, instead of staying stuck on a color which has no dye left * Holding Applicator in offhand will now paint cables on placement * Middle-clicking a colored tile while holding an Applicator in main or off hand will now set the Applicator's color to that color, if the dye is available in the Applicator --- .../core/sync/AppEngPacketHandlerBase.java | 4 +- .../PacketColorApplicatorSelectColor.java | 53 +++++++++ .../core/transformer/AE2ELTransformer.java | 33 +++--- .../core/transformer/PickBlockPatch.java | 76 +++++++++++++ .../tools/powered/ToolColorApplicator.java | 105 +++++++++++++----- .../appeng/parts/networking/PartCable.java | 18 +++ 6 files changed, 246 insertions(+), 43 deletions(-) create mode 100644 src/main/java/appeng/core/sync/packets/PacketColorApplicatorSelectColor.java create mode 100644 src/main/java/appeng/core/transformer/PickBlockPatch.java diff --git a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java index 8ab792bfa..72d87f4cf 100644 --- a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java +++ b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java @@ -86,7 +86,9 @@ public class AppEngPacketHandlerBase { PACKET_TERMINAL_KEYBIND(PacketTerminalUse.class), - PACKET_CRAFTING_TOAST(PacketCraftingToast.class); + PACKET_CRAFTING_TOAST(PacketCraftingToast.class), + + PACKET_COLOR_APPLICATOR_SELECT_COLOR(PacketColorApplicatorSelectColor.class); private final Class packetClass; diff --git a/src/main/java/appeng/core/sync/packets/PacketColorApplicatorSelectColor.java b/src/main/java/appeng/core/sync/packets/PacketColorApplicatorSelectColor.java new file mode 100644 index 000000000..051a8252b --- /dev/null +++ b/src/main/java/appeng/core/sync/packets/PacketColorApplicatorSelectColor.java @@ -0,0 +1,53 @@ +package appeng.core.sync.packets; + +import appeng.api.util.AEColor; +import appeng.core.sync.AppEngPacket; +import appeng.core.sync.network.INetworkInfo; +import appeng.items.tools.powered.ToolColorApplicator; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nullable; + +public class PacketColorApplicatorSelectColor extends AppEngPacket { + + @Nullable + private AEColor color = null; + + @SuppressWarnings("unused") + public PacketColorApplicatorSelectColor(final ByteBuf stream) { + if (stream.readBoolean()) { + byte colorIdx = stream.readByte(); + AEColor[] values = AEColor.values(); + if (colorIdx >= 0 && colorIdx < values.length) { + this.color = values[colorIdx]; + } + } + } + + public PacketColorApplicatorSelectColor(@Nullable final AEColor color) { + final ByteBuf data = Unpooled.buffer(); + data.writeInt(this.getPacketID()); + if (color != null) { + data.writeBoolean(true); + data.writeByte(color.ordinal()); + } else { + data.writeBoolean(false); + } + this.configureWrite(data); + } + + @Override + public void serverPacketData(INetworkInfo manager, AppEngPacket packet, EntityPlayer player) { + switchColor(player.getHeldItemMainhand(), color); + switchColor(player.getHeldItemOffhand(), color); + } + + private static void switchColor(ItemStack stack, AEColor color) { + if (!stack.isEmpty() && stack.getItem() instanceof ToolColorApplicator colorApp) { + colorApp.setActiveColor(stack, color); + } + } +} diff --git a/src/main/java/appeng/core/transformer/AE2ELTransformer.java b/src/main/java/appeng/core/transformer/AE2ELTransformer.java index 10ee3b9bd..dec2e6abe 100644 --- a/src/main/java/appeng/core/transformer/AE2ELTransformer.java +++ b/src/main/java/appeng/core/transformer/AE2ELTransformer.java @@ -26,6 +26,7 @@ import com.google.common.io.ByteStreams; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraftforge.fml.common.Loader; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.ClassRemapper; @@ -43,23 +44,29 @@ public class AE2ELTransformer implements IClassTransformer { @Override public byte[] transform(String name, String transformedName, byte[] basicClass) { - if (Loader.instance().getIndexedModList().get("stackup") != null) { - return basicClass; - } transformedName = transformedName.replace('/', '.'); - Consumer consumer = (n) -> { - }; + if ("net.minecraftforge.common.ForgeHooks".equals(transformedName)) { + ClassReader cr = new ClassReader(basicClass); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + ClassVisitor cv = new PickBlockPatch(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } + + Consumer consumer = (n) -> {}; Consumer emptyConsumer = consumer; - if ("net.minecraft.item.ItemStack".equals(transformedName)) { - consumer = consumer.andThen(ItemStackPatch::patchCountGetSet); - } else if ("net.minecraft.network.PacketBuffer".equals(transformedName)) { - consumer = consumer.andThen((node) -> { - spliceClasses(node, "appeng.core.transformer.PacketBufferPatch", - "readItemStack", "func_150791_c", - "writeItemStack", "func_150788_a"); - }); + if (Loader.instance().getIndexedModList().get("stackup") == null) { + if ("net.minecraft.item.ItemStack".equals(transformedName)) { + consumer = consumer.andThen(ItemStackPatch::patchCountGetSet); + } else if ("net.minecraft.network.PacketBuffer".equals(transformedName)) { + consumer = consumer.andThen((node) -> { + spliceClasses(node, "appeng.core.transformer.PacketBufferPatch", + "readItemStack", "func_150791_c", + "writeItemStack", "func_150788_a"); + }); + } } if (consumer != emptyConsumer) { diff --git a/src/main/java/appeng/core/transformer/PickBlockPatch.java b/src/main/java/appeng/core/transformer/PickBlockPatch.java new file mode 100644 index 000000000..ee83e270c --- /dev/null +++ b/src/main/java/appeng/core/transformer/PickBlockPatch.java @@ -0,0 +1,76 @@ +package appeng.core.transformer; + +import appeng.api.AEApi; +import appeng.api.definitions.IItemDefinition; +import appeng.api.implementations.tiles.IColorableTile; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketColorApplicatorSelectColor; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class PickBlockPatch extends ClassVisitor { + + public PickBlockPatch(ClassVisitor cv) { + super(Opcodes.ASM5, cv); + } + + @SuppressWarnings("unused") + public static boolean testColorApplicatorPickBlock(RayTraceResult result, EntityPlayer player, World world) { + if (player == null || player.world == null || result == null || result.typeOfHit != RayTraceResult.Type.BLOCK) { + return false; + } + + IItemDefinition applicator = AEApi.instance().definitions().items().colorApplicator(); + if (!applicator.isSameAs(player.getHeldItemMainhand()) && !applicator.isSameAs(player.getHeldItemOffhand())) { + return false; + } + + TileEntity tile = player.world.getTileEntity(result.getBlockPos()); + if (tile instanceof IColorableTile colorableTile) { + NetworkHandler.instance().sendToServer(new PacketColorApplicatorSelectColor(colorableTile.getColor())); + return true; + } + return false; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if ("onPickBlock".equals(name)) { + return new OnPickBlockVisitor(mv); + } + return mv; + } + + private static class OnPickBlockVisitor extends MethodVisitor implements Opcodes { + + public OnPickBlockVisitor(MethodVisitor mv) { + super(Opcodes.ASM5, mv); + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn( + INVOKESTATIC, + "appeng/core/transformer/PickBlockPatch", + "testColorApplicatorPickBlock", + "(Lnet/minecraft/util/math/RayTraceResult;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/world/World;)Z", + false); + mv.visitInsn(DUP); + Label exitLabel = new Label(); + mv.visitJumpInsn(IFEQ, exitLabel); + + mv.visitInsn(IRETURN); + mv.visitLabel(exitLabel); + } + } +} diff --git a/src/main/java/appeng/items/tools/powered/ToolColorApplicator.java b/src/main/java/appeng/items/tools/powered/ToolColorApplicator.java index a99adab99..1cfe89b89 100644 --- a/src/main/java/appeng/items/tools/powered/ToolColorApplicator.java +++ b/src/main/java/appeng/items/tools/powered/ToolColorApplicator.java @@ -72,11 +72,13 @@ import net.minecraftforge.items.IItemHandler; import net.minecraftforge.oredict.OreDictionary; import org.apache.commons.lang3.text.WordUtils; +import javax.annotation.Nullable; import java.util.*; public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCell, IItemGroup, IBlockTool, IMouseWheelItem { + private static final double POWER_PER_USE = 100; private static final Map ORE_TO_COLOR = new HashMap<>(); static { @@ -118,11 +120,7 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe ItemStack paintBall = this.getColor(is); - final IMEInventory inv = AEApi.instance() - .registries() - .cell() - .getCellInventory(is, null, - AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)); + final IMEInventory inv = getInventory(is); if (inv != null) { final IAEItemStack option = inv.extractItems(AEItemStack.fromItemStack(paintBall), Actionable.SIMULATE, new BaseActionSource()); @@ -137,15 +135,13 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe return EnumActionResult.FAIL; } - final double powerPerUse = 100; if (!paintBall.isEmpty() && paintBall.getItem() instanceof ItemSnowball) { final TileEntity te = w.getTileEntity(pos); // clean cables. if (te instanceof IColorableTile) { - if (this.getAECurrentPower(is) > powerPerUse && ((IColorableTile) te).getColor() != AEColor.TRANSPARENT) { + if (this.getAECurrentPower(is) > POWER_PER_USE && ((IColorableTile) te).getColor() != AEColor.TRANSPARENT) { if (((IColorableTile) te).recolourBlock(side, AEColor.TRANSPARENT, p)) { - inv.extractItems(AEItemStack.fromItemStack(paintBall), Actionable.MODULATE, new BaseActionSource()); - this.extractAEPower(is, powerPerUse, Actionable.MODULATE); + consumeItem(is, paintBall, false); return EnumActionResult.SUCCESS; } } @@ -154,19 +150,17 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe // clean paint balls.. final Block testBlk = w.getBlockState(pos.offset(side)).getBlock(); final TileEntity painted = w.getTileEntity(pos.offset(side)); - if (this.getAECurrentPower(is) > powerPerUse && testBlk instanceof BlockPaint && painted instanceof TilePaint) { - inv.extractItems(AEItemStack.fromItemStack(paintBall), Actionable.MODULATE, new BaseActionSource()); - this.extractAEPower(is, powerPerUse, Actionable.MODULATE); + if (this.getAECurrentPower(is) > POWER_PER_USE && testBlk instanceof BlockPaint && painted instanceof TilePaint) { + consumeItem(is, paintBall, false); ((TilePaint) painted).cleanSide(side.getOpposite()); return EnumActionResult.SUCCESS; } } else if (!paintBall.isEmpty()) { final AEColor color = this.getColorFromItem(paintBall); - if (color != null && this.getAECurrentPower(is) > powerPerUse) { + if (color != null && this.getAECurrentPower(is) > POWER_PER_USE) { if (color != AEColor.TRANSPARENT && this.recolourBlock(blk, side, w, pos, side, color, p)) { - inv.extractItems(AEItemStack.fromItemStack(paintBall), Actionable.MODULATE, new BaseActionSource()); - this.extractAEPower(is, powerPerUse, Actionable.MODULATE); + consumeItem(is, paintBall, false); return EnumActionResult.SUCCESS; } } @@ -176,6 +170,61 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe return EnumActionResult.FAIL; } + public boolean consumeColor(ItemStack applicator, AEColor color, boolean simulate) { + final IMEInventory inv = getInventory(applicator); + if (inv == null) return false; + + ItemStack paintItem = null; + for (final IAEItemStack what : inv.getAvailableItems(getChannel().createList())) { + final ItemStack def = what.createItemStack(); + def.setCount(1); + if (getColorFromItem(def) == color) { + paintItem = def; + } + } + + if (paintItem != null) { + return consumeItem(applicator, paintItem, simulate); + } + return false; + } + + public boolean consumeItem(ItemStack applicator, ItemStack paintItem, boolean simulate) { + final IMEInventory inv = getInventory(applicator); + if (inv == null) return false; + + final Actionable mode = simulate ? Actionable.SIMULATE : Actionable.MODULATE; + boolean success = inv.extractItems(AEItemStack.fromItemStack(paintItem), mode, new BaseActionSource()) != null + && this.extractAEPower(applicator, POWER_PER_USE, mode) >= POWER_PER_USE; + + // Clear the color when we run out + if (success && !simulate && ItemStack.areItemStacksEqual(paintItem, getColor(applicator))) { + if (inv.extractItems(AEItemStack.fromItemStack(paintItem), Actionable.SIMULATE, new BaseActionSource()) == null) { + setColor(applicator, ItemStack.EMPTY); + } + } + return success; + } + + public boolean setActiveColor(ItemStack applicator, @Nullable AEColor color) { + if (color == null) { + setColor(applicator, ItemStack.EMPTY); + return true; + } + + final IMEInventory inv = getInventory(applicator); + if (inv == null) return false; + + for (IAEItemStack stack : inv.getAvailableItems(getChannel().createList())) { + ItemStack def = stack.getDefinition(); + if (getColorFromItem(def) == color) { + setColor(applicator, def); + return true; + } + } + return false; + } + @Override public String getItemStackDisplayName(final ItemStack par1ItemStack) { String extra = GuiText.Empty.getLocal(); @@ -202,8 +251,7 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe return AEColor.TRANSPARENT; } - if (paintBall.getItem() instanceof ItemPaintBall) { - final ItemPaintBall ipb = (ItemPaintBall) paintBall.getItem(); + if (paintBall.getItem() instanceof ItemPaintBall ipb) { return ipb.getColor(paintBall); } else { final int[] id = OreDictionary.getOreIDs(paintBall); @@ -218,6 +266,13 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe return null; } + private IMEInventory getInventory(ItemStack stack) { + return AEApi.instance() + .registries() + .cell() + .getCellInventory(stack, null, getChannel()); + } + public ItemStack getColor(final ItemStack is) { final NBTTagCompound c = is.getTagCompound(); if (c != null && c.hasKey("color")) { @@ -234,14 +289,9 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe private ItemStack findNextColor(final ItemStack is, final ItemStack anchor, final int scrollOffset) { ItemStack newColor = ItemStack.EMPTY; - final IMEInventory inv = AEApi.instance() - .registries() - .cell() - .getCellInventory(is, null, - AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)); + final IMEInventory inv = getInventory(is); if (inv != null) { - final IItemList itemList = inv - .getAvailableItems(AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createList()); + final IItemList itemList = inv.getAvailableItems(getChannel().createList()); if (anchor.isEmpty()) { final IAEItemStack firstItem = itemList.getFirstItem(); if (firstItem != null) { @@ -254,11 +304,8 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe list.add(i); } - Collections.sort(list, (a, b) -> Integer.compare(a.getItemDamage(), b.getItemDamage())); - - if (list.size() <= 0) { - return ItemStack.EMPTY; - } + Collections.sort(list, Comparator.comparingInt(IAEItemStack::getItemDamage)); + if (list.isEmpty()) return ItemStack.EMPTY; IAEItemStack where = list.getFirst(); int cycles = 1 + list.size(); diff --git a/src/main/java/appeng/parts/networking/PartCable.java b/src/main/java/appeng/parts/networking/PartCable.java index a1abd8748..3399dad31 100644 --- a/src/main/java/appeng/parts/networking/PartCable.java +++ b/src/main/java/appeng/parts/networking/PartCable.java @@ -36,6 +36,7 @@ import appeng.api.util.AEColor; import appeng.api.util.AEPartLocation; import appeng.api.util.IReadOnlyCollection; import appeng.items.parts.ItemPart; +import appeng.items.tools.powered.ToolColorApplicator; import appeng.me.GridAccessException; import appeng.parts.AEBasePart; import appeng.util.Platform; @@ -45,6 +46,7 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; import java.io.IOException; import java.util.EnumSet; @@ -93,6 +95,22 @@ public class PartCable extends AEBasePart implements IPartCable { } } + @Override + public void onPlacement(EntityPlayer player, EnumHand hand, ItemStack held, AEPartLocation side) { + super.onPlacement(player, hand, held, side); + + // Apply color applicator color if held in offhand + ItemStack stack = player.getHeldItem(EnumHand.OFF_HAND); + if (!stack.isEmpty() && stack.getItem() instanceof ToolColorApplicator colorApp) { + AEColor color = colorApp.getActiveColor(stack); + if (color != null && color != getCableColor() && colorApp.consumeColor(stack, color, true)) { + if (changeColor(color, player) && !player.isCreative()) { + colorApp.consumeColor(stack, color, false); + } + } + } + } + @Override public boolean changeColor(final AEColor newColor, final EntityPlayer who) { if (this.getCableColor() != newColor) {