mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-08 12:34:46 +00:00
Autocrafting Optimization (#1084)
This commit is contained in:
1 parent
e2a2d2a9df
commit
560618024f
7 files changed
+190
-9
No files matched your search
@@ -61,6 +61,9 @@ public class NEICPH implements IClientPacketHandler {
|
||||
case S2C.CACHE_BOOKMARK_DATA:
|
||||
handleCacheBookmarkData(packet);
|
||||
break;
|
||||
case S2C.SYNC:
|
||||
ServerSync.onReply(packet.readInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,9 +92,17 @@ public class NEISPH implements IServerPacketHandler {
|
||||
case C2S.SEND_CHAT_ITEM_LINK:
|
||||
handleSendChatItemLink(sender, packet);
|
||||
break;
|
||||
case C2S.SYNC:
|
||||
handleSync(sender, packet.readInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSync(EntityPlayerMP sender, int id) {
|
||||
sender.sendContainerAndContentsToPlayer(sender.openContainer, sender.openContainer.getInventory());
|
||||
new PacketCustom(channel, S2C.SYNC).writeInt(id).sendToPlayer(sender);
|
||||
}
|
||||
|
||||
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
|
||||
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
|
||||
NEIServerUtils.sendChatItemLink(sender, payloadTag);
|
||||
|
||||
@@ -24,6 +24,7 @@ public final class PacketIDs {
|
||||
public static final int REQUEST_POTION_GUI = 24;
|
||||
public static final int SET_DUMMY_SLOT = 25;
|
||||
public static final int SEND_CHAT_ITEM_LINK = 26;
|
||||
public static final int SYNC = 27;
|
||||
}
|
||||
|
||||
public static final class S2C {
|
||||
@@ -38,5 +39,6 @@ public final class PacketIDs {
|
||||
public static final int SET_CREATIVE_PLUS_MODE = 23;
|
||||
public static final int OPEN_POTION_GUI = 24;
|
||||
public static final int CACHE_BOOKMARK_DATA = 25;
|
||||
public static final int SYNC = 26;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.nei.PacketIDs.C2S;
|
||||
|
||||
/**
|
||||
* Waits until the server has processed everything the client sent before. The server handles slot clicks and NEI
|
||||
* packets in the order they came and answers after it has sent the slot updates, so once the answer is here the client
|
||||
* container holds what the server computed.
|
||||
*/
|
||||
public final class ServerSync {
|
||||
|
||||
private static int lastId;
|
||||
private static volatile int awaiting;
|
||||
|
||||
private ServerSync() {}
|
||||
|
||||
public static void request() {
|
||||
|
||||
if (!NEIClientConfig.hasSMPCounterPart()) {
|
||||
awaiting = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
awaiting = ++lastId;
|
||||
new PacketCustom(NEICPH.channel, C2S.SYNC).writeInt(awaiting).sendToServer();
|
||||
}
|
||||
|
||||
public static boolean pending() {
|
||||
return awaiting != 0;
|
||||
}
|
||||
|
||||
static void onReply(int id) {
|
||||
if (id == awaiting) awaiting = 0;
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.inventory.SlotCrafting;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.FastTransferManager;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemStackAmount;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.NEIServerUtils;
|
||||
import codechicken.nei.RestartableTask;
|
||||
import codechicken.nei.ServerSync;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.chain.RecipeChainIterator;
|
||||
@@ -26,6 +31,7 @@ public class AutoCraftingManager {
|
||||
@Override
|
||||
public void execute() {
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final ItemStack[] layout = snapshotLayout(guiContainer);
|
||||
final List<BookmarkItem> initialItems = prepareInitialItems(math, getInventoryItems(guiContainer));
|
||||
boolean processed = false;
|
||||
boolean changed = false;
|
||||
@@ -69,6 +75,11 @@ public class AutoCraftingManager {
|
||||
|
||||
} while (changed && !interrupted(guiContainer));
|
||||
|
||||
if (!interrupted(guiContainer)) {
|
||||
restoreLayout(guiContainer, layout);
|
||||
ServerSync.request();
|
||||
}
|
||||
|
||||
} finally {
|
||||
StackInfo.pauseItemDamageSound(false);
|
||||
}
|
||||
@@ -134,4 +145,70 @@ public class AutoCraftingManager {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
private static ItemStack[] snapshotLayout(GuiContainer guiContainer) {
|
||||
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
|
||||
final ItemStack[] layout = new ItemStack[slots.size()];
|
||||
|
||||
for (int index = 0; index < slots.size(); index++) {
|
||||
final Slot slot = slots.get(index);
|
||||
|
||||
if (slot.getHasStack() && !(slot instanceof SlotCrafting)) {
|
||||
layout[index] = slot.getStack().copy();
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private static void restoreLayout(GuiContainer guiContainer, ItemStack[] layout) {
|
||||
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
|
||||
final EntityPlayer player = guiContainer.mc.thePlayer;
|
||||
|
||||
for (int index = 0; index < slots.size() && player.inventory.getItemStack() == null
|
||||
&& guiContainer.mc.currentScreen == guiContainer; index++) {
|
||||
final ItemStack expected = layout[index];
|
||||
final Slot target = slots.get(index);
|
||||
|
||||
if (expected == null || inPlace(slots, layout, index)
|
||||
|| !target.isItemValid(expected)
|
||||
|| target.getHasStack() && !target.canTakeStack(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Slot source = findMisplaced(slots, layout, expected, player);
|
||||
if (source == null) continue;
|
||||
|
||||
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
|
||||
FastTransferManager.clickSlot(guiContainer, target.slotNumber);
|
||||
|
||||
if (player.inventory.getItemStack() != null) {
|
||||
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
|
||||
}
|
||||
}
|
||||
|
||||
FastTransferManager.dropHeldItem(guiContainer);
|
||||
}
|
||||
|
||||
private static boolean inPlace(List<Slot> slots, ItemStack[] layout, int index) {
|
||||
final ItemStack stack = slots.get(index).getStack();
|
||||
return stack != null && layout[index] != null
|
||||
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(layout[index], stack);
|
||||
}
|
||||
|
||||
private static Slot findMisplaced(List<Slot> slots, ItemStack[] layout, ItemStack expected, EntityPlayer player) {
|
||||
|
||||
for (int index = 0; index < slots.size(); index++) {
|
||||
final Slot slot = slots.get(index);
|
||||
|
||||
if (slot.getHasStack() && !(slot instanceof SlotCrafting)
|
||||
&& !inPlace(slots, layout, index)
|
||||
&& slot.canTakeStack(player)
|
||||
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(expected, slot.getStack())) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -20,10 +21,13 @@ import codechicken.lib.inventory.InventoryUtils;
|
||||
import codechicken.nei.FastTransferManager;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.ServerSync;
|
||||
import codechicken.nei.api.IOverlayHandler;
|
||||
|
||||
public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
|
||||
private static final long SYNC_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
public static class DistributedIngred {
|
||||
|
||||
public DistributedIngred(ItemStack item) {
|
||||
@@ -113,7 +117,8 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
|
||||
boolean craft = false;
|
||||
while (multiplier > 0) {
|
||||
while (multiplier > 0 && firstGui.mc.currentScreen == firstGui
|
||||
&& presenceOverlay(firstGui, handler, recipeIndex).stream().allMatch(s -> s.isPresent())) {
|
||||
final int transfer = transferRecipe(firstGui, handler, recipeIndex, multiplier);
|
||||
|
||||
if (transfer <= 0) {
|
||||
@@ -122,6 +127,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
|
||||
multiplier -= transfer;
|
||||
|
||||
if (!craftingSlot.getHasStack() && !awaitServer(firstGui)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (craftingSlot.getHasStack() && craftingSlot.canTakeStack(firstGui.mc.thePlayer)) {
|
||||
FastTransferManager.clickSlot(firstGui, craftingSlot.slotNumber, 0, 1);
|
||||
|
||||
@@ -139,9 +148,30 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
return craft;
|
||||
}
|
||||
|
||||
private boolean awaitServer(GuiContainer gui) {
|
||||
final long deadline = System.currentTimeMillis() + SYNC_TIMEOUT_MILLIS;
|
||||
ServerSync.request();
|
||||
|
||||
while (ServerSync.pending()) {
|
||||
if (gui.mc.currentScreen != gui || System.currentTimeMillis() > deadline) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean clearIngredients(GuiContainer gui, IRecipeHandler handler) {
|
||||
|
||||
for (Slot slot : getCraftMatrixSlots(gui, handler)) {
|
||||
if (gui.mc.currentScreen != gui) return false;
|
||||
|
||||
if (slot.canTakeStack(gui.mc.thePlayer)) {
|
||||
FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1);
|
||||
if (slot.getHasStack()) return false;
|
||||
@@ -206,7 +236,9 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
protected void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
|
||||
final Slot craftingSlot = getCraftingResultSlot(gui);
|
||||
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) {
|
||||
for (Slot slot : getSourceSlots(gui)) {
|
||||
if (gui.mc.currentScreen != gui) return;
|
||||
|
||||
if (slot == craftingSlot || !slot.getHasStack()
|
||||
|| !canMoveFrom(slot, gui)
|
||||
|| !slot.canTakeStack(gui.mc.thePlayer))
|
||||
@@ -240,6 +272,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
FastTransferManager.clickSlot(gui, slot.slotNumber);
|
||||
}
|
||||
|
||||
// A right click with an empty or foreign cursor would take half of the dest stack
|
||||
final ItemStack held = gui.mc.thePlayer.inventory.getItemStack();
|
||||
if (held == null || !canStack(distrib.permutation, held)) break;
|
||||
|
||||
FastTransferManager.clickSlot(gui, dest.slotNumber, 1);
|
||||
stackSize--;
|
||||
}
|
||||
@@ -370,7 +406,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
|
||||
protected void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) /* work out how much we have to go round */ {
|
||||
for (Slot slot : getSourceSlots(gui)) /* work out how much we have to go round */ {
|
||||
if (slot.getHasStack() && canMoveFrom(slot, gui)) {
|
||||
final ItemStack pstack = slot.getStack();
|
||||
final DistributedIngred istack = findIngred(ingredStacks, pstack);
|
||||
@@ -408,6 +444,25 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Slot> getSourceSlots(GuiContainer gui) {
|
||||
final List<Slot> slots = new ArrayList<>(gui.inventorySlots.inventorySlots);
|
||||
slots.sort(Comparator.comparingLong(slot -> getRemainingDurability(slot.getStack())));
|
||||
return slots;
|
||||
}
|
||||
|
||||
private static long getRemainingDurability(ItemStack stack) {
|
||||
if (stack == null || stack.getMaxStackSize() != 1) return Long.MAX_VALUE;
|
||||
|
||||
final NBTTagCompound tagCompound = stack.getTagCompound();
|
||||
|
||||
if (tagCompound != null && tagCompound.hasKey("GT.ToolStats")) {
|
||||
final NBTTagCompound toolStats = tagCompound.getCompoundTag("GT.ToolStats");
|
||||
return toolStats.getLong("MaxDamage") - toolStats.getLong("Damage");
|
||||
}
|
||||
|
||||
return stack.isItemStackDamageable() ? stack.getMaxDamage() - stack.getItemDamage() : Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
private int getGTToolDamagePerContainerCraft(ItemStack aStack) {
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -167,6 +168,8 @@ public class RecipeHandlerRef {
|
||||
return transferAlghoritmCache.computeIfAbsent(overlayHandler.getClass(), clazz -> {
|
||||
|
||||
try {
|
||||
final Method transferRecipe = overlayHandler.getClass()
|
||||
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class);
|
||||
|
||||
if (overlayHandler instanceof DefaultOverlayHandler) {
|
||||
final Class<?> overlayRecipeClazz = overlayHandler.getClass()
|
||||
@@ -178,14 +181,11 @@ public class RecipeHandlerRef {
|
||||
boolean.class)
|
||||
.getDeclaringClass();
|
||||
|
||||
if (overlayRecipeClazz.equals(DefaultOverlayHandler.class)) {
|
||||
return true;
|
||||
}
|
||||
return overlayRecipeClazz.equals(DefaultOverlayHandler.class)
|
||||
|| !transferRecipe.getDeclaringClass().equals(DefaultOverlayHandler.class);
|
||||
}
|
||||
|
||||
return !overlayHandler.getClass()
|
||||
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class)
|
||||
.isDefault();
|
||||
return !transferRecipe.isDefault();
|
||||
} catch (NoSuchMethodException e) {}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in new issue
Block a user