diff --git a/src/main/java/github/kasuminova/mmce/client/gui/GuiMEItemInputBus.java b/src/main/java/github/kasuminova/mmce/client/gui/GuiMEItemInputBus.java index 52d141a3..f94c0b24 100644 --- a/src/main/java/github/kasuminova/mmce/client/gui/GuiMEItemInputBus.java +++ b/src/main/java/github/kasuminova/mmce/client/gui/GuiMEItemInputBus.java @@ -1,14 +1,23 @@ package github.kasuminova.mmce.client.gui; +import appeng.container.interfaces.IJEIGhostIngredients; +import appeng.container.slot.IJEITargetSlot; import appeng.container.slot.SlotDisabled; import appeng.container.slot.SlotFake; import appeng.core.localization.GuiText; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketInventoryAction; +import appeng.fluids.client.gui.widgets.GuiFluidSlot; +import appeng.fluids.util.AEFluidStack; +import appeng.helpers.InventoryAction; +import appeng.util.item.AEItemStack; import github.kasuminova.mmce.common.container.ContainerMEItemInputBus; import github.kasuminova.mmce.common.network.PktMEInputBusInvAction; import github.kasuminova.mmce.common.tile.MEItemInputBus; import hellfirepvp.modularmachinery.ModularMachinery; import hellfirepvp.modularmachinery.client.ClientProxy; import hellfirepvp.modularmachinery.common.util.MiscUtils; +import mezz.jei.api.gui.IGhostIngredientHandler; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.resources.I18n; @@ -17,20 +26,23 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fml.client.config.GuiUtils; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import javax.annotation.Nonnull; +import java.awt.*; import java.io.IOException; import java.text.NumberFormat; -import java.util.ArrayList; +import java.util.*; import java.util.List; -import java.util.Locale; -public class GuiMEItemInputBus extends GuiMEItemBus { +public class GuiMEItemInputBus extends GuiMEItemBus implements IJEIGhostIngredients { private static final ResourceLocation TEXTURES_INPUT_BUS = new ResourceLocation(ModularMachinery.MODID, "textures/gui/meiteminputbus.png"); + protected final Map, Object> mapTargetSlot = new HashMap<>(); private int invActionAmount = 0; public GuiMEItemInputBus(final MEItemInputBus te, final EntityPlayer player) { @@ -209,4 +221,81 @@ protected void renderToolTip(@Nonnull final ItemStack stack, final int x, final this.drawHoveringText(tooltip, x, y, (font == null ? fontRenderer : font)); GuiUtils.postItemToolTip(); } + + // Code adapted from appeng.client.gui.implementations.GuiUpgradeable, full credits to the original author + @Override + public List> getPhantomTargets(Object ingredient) { + mapTargetSlot.clear(); + + FluidStack fluidStack = null; + ItemStack itemStack = ItemStack.EMPTY; + + if (ingredient instanceof ItemStack) { + itemStack = (ItemStack) ingredient; + fluidStack = FluidUtil.getFluidContained(itemStack); + } else if (ingredient instanceof FluidStack) { + fluidStack = (FluidStack) ingredient; + } + + if (!(ingredient instanceof ItemStack) && !(ingredient instanceof FluidStack)) { + return Collections.emptyList(); + } + + List> targets = new ArrayList(); + List slots = new ArrayList(); + if (!this.inventorySlots.inventorySlots.isEmpty()) { + for(Slot slot : this.inventorySlots.inventorySlots) { + if (slot instanceof SlotFake && (!itemStack.isEmpty())) { + slots.add((IJEITargetSlot)slot); + } + } + } + for (IJEITargetSlot slot : slots) { + ItemStack finalItemStack = itemStack; + FluidStack finalFluidStack = fluidStack; + IGhostIngredientHandler.Target targetItem = new IGhostIngredientHandler.Target<>() { + @Nonnull + @Override + public Rectangle getArea() { + if (slot instanceof SlotFake && ((SlotFake) slot).isSlotEnabled()) { + return new Rectangle(getGuiLeft() + ((SlotFake) slot).xPos, getGuiTop() + ((SlotFake) slot).yPos, 16, 16); + } else if (slot instanceof GuiFluidSlot && ((GuiFluidSlot) slot).isSlotEnabled()) { + return new Rectangle(getGuiLeft() + ((GuiFluidSlot) slot).xPos(), getGuiTop() + ((GuiFluidSlot) slot).yPos(), 16, 16); + } + return new Rectangle(); + } + + @Override + public void accept(@Nonnull Object ingredient) { + PacketInventoryAction p = null; + try { + if (slot instanceof SlotFake && ((SlotFake) slot).isSlotEnabled()) { + if (finalItemStack.isEmpty() && finalFluidStack != null) { + p = new PacketInventoryAction(InventoryAction.PLACE_JEI_GHOST_ITEM, slot, AEItemStack.fromItemStack(FluidUtil.getFilledBucket(finalFluidStack))); + } else if (!finalItemStack.isEmpty()) { + p = new PacketInventoryAction(InventoryAction.PLACE_JEI_GHOST_ITEM, slot, AEItemStack.fromItemStack(finalItemStack)); + } + } else { + if (finalFluidStack == null) { + return; + } + p = new PacketInventoryAction(InventoryAction.PLACE_JEI_GHOST_ITEM, slot, AEItemStack.fromItemStack(AEFluidStack.fromFluidStack(finalFluidStack).asItemStackRepresentation())); + } + NetworkHandler.instance().sendToServer(p); + + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + targets.add(targetItem); + mapTargetSlot.putIfAbsent(targetItem, slot); + } + return targets; + } + + @Override + public Map, Object> getFakeSlotTargetMap() { + return mapTargetSlot; + } } diff --git a/src/main/java/github/kasuminova/mmce/client/gui/integration/handler/MEInputGhostSlotHandler.java b/src/main/java/github/kasuminova/mmce/client/gui/integration/handler/MEInputGhostSlotHandler.java new file mode 100644 index 00000000..58879b57 --- /dev/null +++ b/src/main/java/github/kasuminova/mmce/client/gui/integration/handler/MEInputGhostSlotHandler.java @@ -0,0 +1,62 @@ +package github.kasuminova.mmce.client.gui.integration.handler; + +import appeng.client.gui.AEGuiHandler; +import appeng.container.interfaces.IJEIGhostIngredients; +import appeng.container.slot.IJEITargetSlot; +import github.kasuminova.mmce.client.gui.GuiMEItemInputBus; +import mezz.jei.api.gui.IAdvancedGuiHandler; +import mezz.jei.api.gui.IGhostIngredientHandler; +import net.minecraft.client.gui.GuiScreen; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.input.Mouse; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +public class MEInputGhostSlotHandler implements IGhostIngredientHandler, IAdvancedGuiHandler { + + private static final AEGuiHandler aeGuiHandler = new AEGuiHandler(); + + + @Override + public List> getTargets(GuiMEItemInputBus guiMEItemInputBus, I ingredient, boolean doStart) { + ArrayList> targets = new ArrayList<>(); + IJEIGhostIngredients ghostGui = guiMEItemInputBus; + List> phantomTargets = ghostGui.getPhantomTargets(ingredient); + targets.addAll((List>) (Object) phantomTargets); + if (doStart && GuiScreen.isShiftKeyDown() && Mouse.isButtonDown(0)) { + for (Target target : targets) { + if (ghostGui.getFakeSlotTargetMap().get(target) instanceof IJEITargetSlot jeiSlot) { + if (jeiSlot.needAccept()) { + target.accept(ingredient); + break; + } + } + } + } + return targets; + } + + @Override + public void onComplete() { + + } + + @Override + public Class getGuiContainerClass() { + return GuiMEItemInputBus.class; + } + + @Nullable + @Override + public List getGuiExtraAreas(GuiMEItemInputBus guiContainer) { + return guiContainer.getJEIExclusionArea(); + } + + @Nullable + @Override + public Object getIngredientUnderMouse(GuiMEItemInputBus guiContainer, int mouseX, int mouseY) { + return aeGuiHandler.getIngredientUnderMouse(guiContainer, mouseX, mouseY); + } +} diff --git a/src/main/java/github/kasuminova/mmce/common/container/handler/MEInputRecipeTransferHandler.java b/src/main/java/github/kasuminova/mmce/common/container/handler/MEInputRecipeTransferHandler.java new file mode 100644 index 00000000..01afc9f9 --- /dev/null +++ b/src/main/java/github/kasuminova/mmce/common/container/handler/MEInputRecipeTransferHandler.java @@ -0,0 +1,66 @@ +package github.kasuminova.mmce.common.container.handler; + +import github.kasuminova.mmce.common.container.ContainerMEItemInputBus; +import github.kasuminova.mmce.common.network.PktMEInputBusRecipeTransfer; +import hellfirepvp.modularmachinery.ModularMachinery; +import mezz.jei.api.gui.IGuiIngredient; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.recipe.transfer.IRecipeTransferError; +import mezz.jei.api.recipe.transfer.IRecipeTransferHandler; +import mezz.jei.transfer.RecipeTransferErrorInternal; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Map; + +public class MEInputRecipeTransferHandler implements IRecipeTransferHandler { + + @Override + public Class getContainerClass() { + return ContainerMEItemInputBus.class; + } + + @Nullable + @Override + public IRecipeTransferError transferRecipe(ContainerMEItemInputBus containerMEItemInputBus, IRecipeLayout recipeLayout, EntityPlayer entityPlayer, boolean maxTransfer, boolean doTransfer) { + final String recipeType = recipeLayout.getRecipeCategory().getUid(); + + // MM recipes are identified by this prefix + if (!recipeType.contains("modularmachinery.recipe")) { + return RecipeTransferErrorInternal.INSTANCE; + } + + if (!doTransfer) { + return null; + } + + Map> ingredients = recipeLayout.getItemStacks().getGuiIngredients(); + + ArrayList inputs = new ArrayList<>(); + for (Map.Entry> entry : ingredients.entrySet()) { + IGuiIngredient ingredient = entry.getValue(); + // We don't care about outputs + if (ingredient.isInput()) { + inputs.add(ingredient.getDisplayedIngredient()); + } + } + + if (inputs.isEmpty()) { + return RecipeTransferErrorInternal.INSTANCE; + } + + ArrayList nonNullInputs = new ArrayList<>(); + for (ItemStack input : inputs) { + if (input != null) { + nonNullInputs.add(input); + } + } + + PktMEInputBusRecipeTransfer pktMEInputBusRecipeTransfer = new PktMEInputBusRecipeTransfer(nonNullInputs); + ModularMachinery.NET_CHANNEL.sendToServer(pktMEInputBusRecipeTransfer); + + return null; + } +} diff --git a/src/main/java/github/kasuminova/mmce/common/network/PktMEInputBusRecipeTransfer.java b/src/main/java/github/kasuminova/mmce/common/network/PktMEInputBusRecipeTransfer.java new file mode 100644 index 00000000..ec866827 --- /dev/null +++ b/src/main/java/github/kasuminova/mmce/common/network/PktMEInputBusRecipeTransfer.java @@ -0,0 +1,79 @@ +package github.kasuminova.mmce.common.network; + +import appeng.container.slot.SlotFake; +import appeng.helpers.ItemStackHelper; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fml.common.network.ByteBufUtils; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +import java.util.ArrayList; +import java.util.List; + +public class PktMEInputBusRecipeTransfer implements IMessage, IMessageHandler { + + private ArrayList inputs; + + public PktMEInputBusRecipeTransfer() { + } + + public PktMEInputBusRecipeTransfer(ArrayList inputs) { + this.inputs = inputs; + } + + @Override + public void fromBytes(ByteBuf buf) { + inputs = new ArrayList<>(); + NBTTagCompound tagCompound = ByteBufUtils.readTag(buf); + NBTTagList tagList = tagCompound.getTagList("itemInputs", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < tagList.tagCount(); i++) { + NBTTagCompound tag = tagList.getCompoundTagAt(i); + if (!tagList.isEmpty()) { + ItemStack stack = new ItemStack(tag); + if (!stack.isEmpty()) { + inputs.add(stack); + } + } + } + } + + @Override + public void toBytes(ByteBuf buf) { + final NBTTagList recipeInputs = new NBTTagList(); + for (ItemStack input : inputs) { + recipeInputs.appendTag(ItemStackHelper.stackToNBT(input)); + } + NBTTagCompound recipeTag = new NBTTagCompound(); + recipeTag.setTag("itemInputs", recipeInputs); + ByteBufUtils.writeTag(buf, recipeTag); + } + + @Override + public IMessage onMessage(PktMEInputBusRecipeTransfer message, MessageContext ctx) { + final EntityPlayerMP player = ctx.getServerHandler().player; + final Container container = player.openContainer; + ArrayList inputs = message.inputs; + List inventorySlots = container.inventorySlots; + ext: + for (ItemStack input : inputs) { + for (Slot slot : inventorySlots) { + if (slot instanceof SlotFake slotFake) { + if (!slotFake.getHasStack()) { + slotFake.putStack(input.copy()); + continue ext; + } + } + } + } + + return null; + } +} diff --git a/src/main/java/hellfirepvp/modularmachinery/ModularMachinery.java b/src/main/java/hellfirepvp/modularmachinery/ModularMachinery.java index 36d7324a..d335ff9b 100644 --- a/src/main/java/hellfirepvp/modularmachinery/ModularMachinery.java +++ b/src/main/java/hellfirepvp/modularmachinery/ModularMachinery.java @@ -9,11 +9,7 @@ package hellfirepvp.modularmachinery; import github.kasuminova.mmce.common.concurrent.TaskExecutor; -import github.kasuminova.mmce.common.network.PktAutoAssemblyRequest; -import github.kasuminova.mmce.common.network.PktMEInputBusInvAction; -import github.kasuminova.mmce.common.network.PktMEPatternProviderAction; -import github.kasuminova.mmce.common.network.PktMEPatternProviderHandlerItems; -import github.kasuminova.mmce.common.network.PktPerformanceReport; +import github.kasuminova.mmce.common.network.*; import hellfirepvp.modularmachinery.common.CommonProxy; import hellfirepvp.modularmachinery.common.base.Mods; import hellfirepvp.modularmachinery.common.command.CommandGetBluePrint; @@ -117,6 +113,7 @@ public void preInit(FMLPreInitializationEvent event) { NET_CHANNEL.registerMessage(PktParallelControllerUpdate.class, PktParallelControllerUpdate.class, 102, Side.SERVER); if (Mods.AE2.isPresent()) { NET_CHANNEL.registerMessage(PktMEInputBusInvAction.class, PktMEInputBusInvAction.class, 103, Side.SERVER); + NET_CHANNEL.registerMessage(PktMEInputBusRecipeTransfer.class, PktMEInputBusRecipeTransfer.class, 107, Side.SERVER); } NET_CHANNEL.registerMessage(PktAutoAssemblyRequest.class, PktAutoAssemblyRequest.class, 104, Side.SERVER); if (Mods.AE2.isPresent()) { diff --git a/src/main/java/hellfirepvp/modularmachinery/common/integration/ModIntegrationJEI.java b/src/main/java/hellfirepvp/modularmachinery/common/integration/ModIntegrationJEI.java index 5985f1bf..8fcf21b9 100644 --- a/src/main/java/hellfirepvp/modularmachinery/common/integration/ModIntegrationJEI.java +++ b/src/main/java/hellfirepvp/modularmachinery/common/integration/ModIntegrationJEI.java @@ -9,6 +9,9 @@ package hellfirepvp.modularmachinery.common.integration; import com.google.common.collect.Lists; +import github.kasuminova.mmce.client.gui.GuiMEItemInputBus; +import github.kasuminova.mmce.client.gui.integration.handler.MEInputGhostSlotHandler; +import github.kasuminova.mmce.common.container.handler.MEInputRecipeTransferHandler; import hellfirepvp.modularmachinery.ModularMachinery; import hellfirepvp.modularmachinery.common.base.Mods; import hellfirepvp.modularmachinery.common.block.BlockController; @@ -204,6 +207,14 @@ public void register(IModRegistry registry) { registry.addRecipeCatalyst(stack, machineCategory); } + registry.addGhostIngredientHandler(GuiMEItemInputBus.class, new MEInputGhostSlotHandler()); + + // Only handle MM recipes + for (DynamicMachine machine : MachineRegistry.getRegistry()) { + String machineCategory = getCategoryStringFor(machine); + registry.getRecipeTransferRegistry().addRecipeTransferHandler(new MEInputRecipeTransferHandler(), machineCategory); + } + for (DynamicMachine machine : MachineRegistry.getRegistry()) { PREVIEW_WRAPPERS.add(new StructurePreviewWrapper(machine)); }