crafting terminal JEI logic!

This commit is contained in:
Ellpeck 2020-05-09 14:56:58 +02:00
parent f9c5ea86f7
commit 3104062b82
10 changed files with 236 additions and 25 deletions

View file

@ -0,0 +1,41 @@
package de.ellpeck.prettypipes.compat.jei;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import de.ellpeck.prettypipes.packets.PacketGhostSlot;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
import de.ellpeck.prettypipes.terminal.containers.CraftingTerminalContainer;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.ingredient.IGuiIngredient;
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import javax.annotation.Nullable;
import java.util.Map;
public class CraftingTerminalTransferHandler implements IRecipeTransferHandler<CraftingTerminalContainer> {
@Override
public Class<CraftingTerminalContainer> getContainerClass() {
return CraftingTerminalContainer.class;
}
@Nullable
@Override
public IRecipeTransferError transferRecipe(CraftingTerminalContainer container, IRecipeLayout recipeLayout, PlayerEntity player, boolean maxTransfer, boolean doTransfer) {
if (!doTransfer)
return null;
ListMultimap<Integer, ItemStack> stacks = ArrayListMultimap.create();
Map<Integer, ? extends IGuiIngredient<ItemStack>> ings = recipeLayout.getItemStacks().getGuiIngredients();
for (Map.Entry<Integer, ? extends IGuiIngredient<ItemStack>> entry : ings.entrySet()) {
if (entry.getValue().isInput())
stacks.putAll(entry.getKey() - 1, entry.getValue().getAllIngredients());
}
PacketHandler.sendToServer(new PacketGhostSlot(container.getTile().getPos(), stacks));
return null;
}
}

View file

@ -5,6 +5,8 @@ import de.ellpeck.prettypipes.misc.PlayerPrefs;
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalGui;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
import mezz.jei.api.registration.IRecipeTransferRegistration;
import mezz.jei.api.runtime.IIngredientFilter;
import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.client.Minecraft;
@ -46,6 +48,11 @@ public class JEIPrettyPipesPlugin implements IModPlugin {
this.runtime = jeiRuntime;
}
@Override
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
registration.addRecipeTransferHandler(new CraftingTerminalTransferHandler(), VanillaRecipeCategoryUid.CRAFTING);
}
@SubscribeEvent
public void onInitGui(InitGuiEvent.Post event) {
Screen screen = event.getGui();

View file

@ -0,0 +1,65 @@
package de.ellpeck.prettypipes.packets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.network.NetworkEvent;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class PacketGhostSlot {
private BlockPos pos;
private ListMultimap<Integer, ItemStack> stacks;
public PacketGhostSlot(BlockPos pos, ListMultimap<Integer, ItemStack> stacks) {
this.pos = pos;
this.stacks = stacks;
}
private PacketGhostSlot() {
}
public static PacketGhostSlot fromBytes(PacketBuffer buf) {
PacketGhostSlot packet = new PacketGhostSlot();
packet.pos = buf.readBlockPos();
packet.stacks = ArrayListMultimap.create();
for (int i = buf.readInt(); i > 0; i--)
packet.stacks.put(buf.readInt(), buf.readItemStack());
return packet;
}
public static void toBytes(PacketGhostSlot packet, PacketBuffer buf) {
buf.writeBlockPos(packet.pos);
buf.writeInt(packet.stacks.size());
for (Map.Entry<Integer, ItemStack> entry : packet.stacks.entries()) {
buf.writeInt(entry.getKey());
buf.writeItemStack(entry.getValue());
}
}
@SuppressWarnings("Convert2Lambda")
public static void onMessage(PacketGhostSlot message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(new Runnable() {
@Override
public void run() {
PlayerEntity player = ctx.get().getSender();
if (player == null)
player = Minecraft.getInstance().player;
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, player.world, message.pos);
if (tile != null)
tile.setGhostItems(message.stacks);
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -22,6 +22,7 @@ public final class PacketHandler {
network.registerMessage(1, PacketButton.class, PacketButton::toBytes, PacketButton::fromBytes, PacketButton::onMessage);
network.registerMessage(2, PacketNetworkItems.class, PacketNetworkItems::toBytes, PacketNetworkItems::fromBytes, PacketNetworkItems::onMessage);
network.registerMessage(3, PacketRequest.class, PacketRequest::toBytes, PacketRequest::fromBytes, PacketRequest::onMessage);
network.registerMessage(4, PacketGhostSlot.class, PacketGhostSlot::toBytes, PacketGhostSlot::fromBytes, PacketGhostSlot::onMessage);
}
public static void sendToAllLoaded(World world, BlockPos pos, Object message) {

View file

@ -28,22 +28,25 @@ public class CraftingTerminalBlock extends ItemTerminalBlock {
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, world, pos);
if (tile != null) {
ItemStack remain = item.stack;
int lowestFitting = -1;
int lowestSlot = -1;
do {
for (int i = 0; i < tile.craftItems.getSlots(); i++) {
ItemStack stack = tile.getRequestedCraftItem(i);
int count = tile.isGhostItem(i) ? 0 : stack.getCount();
if (!ItemHandlerHelper.canItemStacksStackRelaxed(stack, remain))
continue;
if (lowestFitting < 0 || stack.getCount() < tile.getRequestedCraftItem(lowestFitting).getCount())
lowestFitting = i;
if (lowestSlot < 0 || !tile.isGhostItem(lowestSlot) && count < tile.getRequestedCraftItem(lowestSlot).getCount())
lowestSlot = i;
}
if (lowestFitting >= 0) {
remain = tile.craftItems.insertItem(lowestFitting, remain, false);
if (lowestSlot >= 0) {
ItemStack copy = remain.copy();
copy.setCount(1);
remain.shrink(1 - tile.craftItems.insertItem(lowestSlot, copy, false).getCount());
if (remain.isEmpty())
return ItemStack.EMPTY;
}
}
while (lowestFitting >= 0);
while (lowestSlot >= 0);
return ItemHandlerHelper.insertItemStacked(tile.items, remain, false);
}
return item.stack;

View file

@ -1,5 +1,7 @@
package de.ellpeck.prettypipes.terminal;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.misc.EquatableItemStack;
@ -8,6 +10,8 @@ import de.ellpeck.prettypipes.network.NetworkItem;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.packets.PacketGhostSlot;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.terminal.containers.CraftingTerminalContainer;
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer;
import net.minecraft.entity.player.PlayerEntity;
@ -23,23 +27,69 @@ import net.minecraftforge.items.ItemStackHandler;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
public final ItemStackHandler craftItems = new ItemStackHandler(9);
public final ItemStackHandler craftItems = new ItemStackHandler(9) {
@Override
protected void onContentsChanged(int slot) {
for (PlayerEntity playerEntity : CraftingTerminalTileEntity.this.getLookingPlayers())
playerEntity.openContainer.onCraftMatrixChanged(null);
}
};
public final ItemStackHandler ghostItems = new ItemStackHandler(9);
public CraftingTerminalTileEntity() {
super(Registry.craftingTerminalTileEntity);
}
public ItemStack getRequestedCraftItem(int slot) {
// TODO put ghost slot contents here
return this.craftItems.getStackInSlot(slot);
ItemStack stack = this.craftItems.getStackInSlot(slot);
if (!stack.isEmpty())
return stack;
return this.ghostItems.getStackInSlot(slot);
}
public boolean isGhostItem(int slot) {
return this.craftItems.getStackInSlot(slot).isEmpty() && !this.ghostItems.getStackInSlot(slot).isEmpty();
}
public void setGhostItems(ListMultimap<Integer, ItemStack> stacks) {
this.updateItems();
items:
for (int i = 0; i < this.ghostItems.getSlots(); i++) {
List<ItemStack> items = stacks.get(i);
if (items.isEmpty()) {
this.ghostItems.setStackInSlot(i, ItemStack.EMPTY);
continue;
}
if (items.size() > 1) {
// set the item into the ghost slot that already has a variant of itself available in the system
for (ItemStack stack : items) {
EquatableItemStack equatable = new EquatableItemStack(stack);
NetworkItem network = this.networkItems.get(equatable);
if (network == null)
continue;
if (network.getLocations().stream().anyMatch(l -> l.getItemAmount(this.world, stack, ItemEqualityType.NBT) > 0)) {
this.ghostItems.setStackInSlot(i, stack);
continue items;
}
}
}
// if the ghost slot wasn't set, then we don't have the item in the system
// so just pick a random one to put into the slot
this.ghostItems.setStackInSlot(i, items.get(0));
}
if (!this.world.isRemote) {
ListMultimap<Integer, ItemStack> clients = ArrayListMultimap.create();
for (int i = 0; i < this.ghostItems.getSlots(); i++)
clients.put(i, this.ghostItems.getStackInSlot(i));
PacketHandler.sendToAllLoaded(this.world, this.pos, new PacketGhostSlot(this.pos, clients));
}
}
public void requestCraftingItems(PlayerEntity player, boolean all) {
@ -47,6 +97,8 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
network.startProfile("terminal_request_crafting");
this.updateItems();
// the highest amount we can craft with the items we have
int lowestAvailable = Integer.MAX_VALUE;
// this is the amount of items required for each ingredient when crafting ONE
Map<EquatableItemStack, MutableInt> requiredItems = new HashMap<>();
for (int i = 0; i < this.craftItems.getSlots(); i++) {
@ -55,15 +107,16 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
continue;
MutableInt amount = requiredItems.computeIfAbsent(new EquatableItemStack(requested), s -> new MutableInt());
amount.add(1);
int fit = requested.getMaxStackSize() - (this.isGhostItem(i) ? 0 : requested.getCount());
if (lowestAvailable > fit)
lowestAvailable = fit;
}
// the highest amount we can craft with the items we have
int lowestAvailable = Integer.MAX_VALUE;
for (Map.Entry<EquatableItemStack, MutableInt> entry : requiredItems.entrySet()) {
EquatableItemStack stack = entry.getKey();
NetworkItem item = this.networkItems.get(stack);
// total amount of available items of this type
int available = 0;
if (item != null) {
// total amount of available items of this type
int available = 0;
for (NetworkLocation location : item.getLocations()) {
for (int slot : location.getStackSlots(this.world, stack.stack, ItemEqualityType.NBT)) {
ItemStack inSlot = location.getItemHandler(this.world).extractItem(slot, Integer.MAX_VALUE, true);
@ -76,15 +129,12 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
// divide the total by the amount required to get the amount that
// we have available for each crafting slot that contains this item
available /= entry.getValue().intValue();
int fit = stack.stack.getMaxStackSize() - stack.stack.getCount();
if (available > fit)
available = fit;
if (available < lowestAvailable)
lowestAvailable = available;
} else {
lowestAvailable = 0;
}
if (lowestAvailable <= 0)
if (available <= 0)
player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".not_found", stack.stack.getDisplayName()).setStyle(new Style().setColor(TextFormatting.RED)));
}
if (lowestAvailable > 0) {

View file

@ -177,7 +177,7 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
return 0;
}
private PlayerEntity[] getLookingPlayers() {
protected PlayerEntity[] getLookingPlayers() {
return this.world.getPlayers().stream()
.filter(p -> p.openContainer instanceof ItemTerminalContainer)
.filter(p -> ((ItemTerminalContainer) p.openContainer).tile == this)

View file

@ -8,6 +8,7 @@ import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.CraftResultInventory;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.container.ClickType;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.CraftingResultSlot;
import net.minecraft.inventory.container.Slot;
@ -68,7 +69,17 @@ public class CraftingTerminalContainer extends ItemTerminalContainer {
return 65;
}
protected CraftingTerminalTileEntity getTile() {
@Override
public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, PlayerEntity player) {
if (slotId > 0) {
Slot slot = this.inventorySlots.get(slotId);
if (slot.inventory == this.craftInventory)
this.getTile().ghostItems.setStackInSlot(slot.getSlotIndex(), ItemStack.EMPTY);
}
return super.slotClick(slotId, dragType, clickTypeIn, player);
}
public CraftingTerminalTileEntity getTile() {
return (CraftingTerminalTileEntity) this.tile;
}
}

View file

@ -1,5 +1,7 @@
package de.ellpeck.prettypipes.terminal.containers;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.packets.PacketButton;
import de.ellpeck.prettypipes.packets.PacketHandler;
@ -9,6 +11,7 @@ import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
@ -29,13 +32,41 @@ public class CraftingTerminalGui extends ItemTerminalGui {
int all = hasShiftDown() ? 1 : 0;
PacketHandler.sendToServer(new PacketButton(this.container.tile.getPos(), PacketButton.ButtonResult.CRAFT_TERMINAL_REQUEST, all));
}));
this.requestButton.active = !this.getCraftingContainer().craftInventory.isEmpty();
this.requestButton.active = false;
}
@Override
public void tick() {
super.tick();
this.requestButton.active = !this.getCraftingContainer().craftInventory.isEmpty();
CraftingTerminalTileEntity tile = this.getCraftingContainer().getTile();
this.requestButton.active = false;
for (int i = 0; i < tile.craftItems.getSlots(); i++) {
if (!tile.getRequestedCraftItem(i).isEmpty()) {
this.requestButton.active = true;
break;
}
}
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
super.drawGuiContainerForegroundLayer(mouseX, mouseY);
CraftingTerminalContainer container = this.getCraftingContainer();
CraftingTerminalTileEntity tile = container.getTile();
for (int i = 0; i < tile.ghostItems.getSlots(); i++) {
if (!tile.craftItems.getStackInSlot(i).isEmpty())
continue;
ItemStack ghost = tile.ghostItems.getStackInSlot(i);
if (ghost.isEmpty())
continue;
int finalI = i;
Slot slot = container.inventorySlots.stream().filter(s -> s.inventory == container.craftInventory && s.getSlotIndex() == finalI).findFirst().orElse(null);
if (slot == null)
continue;
this.minecraft.getItemRenderer().renderItemIntoGUI(ghost, slot.xPos, slot.yPos);
this.minecraft.getItemRenderer().renderItemOverlayIntoGUI(this.font, ghost, slot.xPos, slot.yPos, "0");
}
}
@Override

View file

@ -96,6 +96,8 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
this.search = this.addButton(new TextFieldWidget(this.font, this.guiLeft + this.getXOffset() + 97, this.guiTop + 6, 86, 8, ""));
this.search.setEnableBackgroundDrawing(false);
this.lastSearchText = "";
if (this.items != null)
this.updateWidgets();
}
protected int getXOffset() {