From 32ee57c3a9d815d7d22c39b0a31c90aa0aa66465 Mon Sep 17 00:00:00 2001 From: AlgorithmX2 Date: Wed, 18 Jun 2014 01:23:37 -0500 Subject: [PATCH] Separated Crafting job into a worker thread. Crafting calculation can be canceled. Hooked up a crafting calculation launch and the confirmation GUI. --- .../gui/implementations/GuiCraftConfirm.java | 46 +++++++ .../ContainerCraftConfirm.java | 98 ++++++++++++++ core/localization/GuiText.java | 2 +- core/sync/GuiBridge.java | 5 +- core/sync/packets/PacketCraftRequest.java | 38 ++++++ crafting/CraftingJob.java | 125 +++++++++++++----- crafting/CraftingTreeNode.java | 27 +++- crafting/CraftingTreeProcess.java | 13 +- helpers/PatternHelper.java | 3 +- .../CraftingCPUCalculator.java | 2 +- tile/crafting/TileCraftingTile.java | 5 +- 11 files changed, 320 insertions(+), 44 deletions(-) create mode 100644 client/gui/implementations/GuiCraftConfirm.java create mode 100644 container/implementations/ContainerCraftConfirm.java diff --git a/client/gui/implementations/GuiCraftConfirm.java b/client/gui/implementations/GuiCraftConfirm.java new file mode 100644 index 00000000..7cbe5492 --- /dev/null +++ b/client/gui/implementations/GuiCraftConfirm.java @@ -0,0 +1,46 @@ +package appeng.client.gui.implementations; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import appeng.api.storage.ITerminalHost; +import appeng.client.gui.AEBaseGui; +import appeng.container.implementations.ContainerCraftConfirm; +import appeng.core.localization.GuiText; + +public class GuiCraftConfirm extends AEBaseGui +{ + + public GuiCraftConfirm(InventoryPlayer inventoryPlayer, ITerminalHost te) { + super( new ContainerCraftConfirm( inventoryPlayer, te ) ); + } + + @Override + public void initGui() + { + super.initGui(); + } + + @Override + protected void actionPerformed(GuiButton btn) + { + super.actionPerformed( btn ); + } + + @Override + public void drawBG(int offsetX, int offsetY, int mouseX, int mouseY) + { + bindTexture( "guis/craftingreport.png" ); + this.drawTexturedModalRect( offsetX, offsetY, 0, 0, xSize, ySize ); + } + + protected String getBackground() + { + return "guis/craftingreport.png"; + } + + @Override + public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) + { + fontRendererObj.drawString( GuiText.ConfirmCrafting.getLocal(), 8, 6, 4210752 ); + } +} diff --git a/container/implementations/ContainerCraftConfirm.java b/container/implementations/ContainerCraftConfirm.java new file mode 100644 index 00000000..e6126730 --- /dev/null +++ b/container/implementations/ContainerCraftConfirm.java @@ -0,0 +1,98 @@ +package appeng.container.implementations; + +import java.util.concurrent.Future; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.util.ChatComponentText; +import net.minecraft.world.World; +import appeng.api.config.SecurityPermissions; +import appeng.api.networking.IGrid; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.IActionHost; +import appeng.api.networking.security.PlayerSource; +import appeng.api.storage.ITerminalHost; +import appeng.container.AEBaseContainer; +import appeng.core.AELog; +import appeng.crafting.CraftingJob; +import appeng.crafting.ICraftingHost; + +public class ContainerCraftConfirm extends AEBaseContainer implements ICraftingHost +{ + + ITerminalHost priHost; + public Future job; + public CraftingJob result; + + public ContainerCraftConfirm(InventoryPlayer ip, ITerminalHost te) { + super( ip, te ); + priHost = te; + } + + @Override + public void detectAndSendChanges() + { + super.detectAndSendChanges(); + if ( job != null && job.isDone() ) + { + try + { + result = job.get(); + AELog.info( "Job info is ready!" ); + } + catch (Throwable e) + { + getPlayerInv().player.addChatMessage( new ChatComponentText( "Error: " + e.toString() ) ); + AELog.error( e ); + this.isContainerValid = false; + result = null; + } + + job = null; + } + verifyPermissions( SecurityPermissions.CRAFT, false ); + } + + @Override + public void onContainerClosed(EntityPlayer par1EntityPlayer) + { + super.onContainerClosed( par1EntityPlayer ); + if ( job != null ) + { + job.cancel( true ); + job = null; + } + } + + @Override + public void removeCraftingFromCrafters(ICrafting c) + { + super.removeCraftingFromCrafters( c ); + if ( job != null ) + { + job.cancel( true ); + job = null; + } + } + + @Override + public IGrid getGrid() + { + IActionHost h = ((IActionHost) this.getTarget()); + return h.getActionableNode().getGrid(); + } + + @Override + public World getWorld() + { + return getPlayerInv().player.worldObj; + } + + @Override + public BaseActionSource getActionSrc() + { + return new PlayerSource( getPlayerInv().player, (IActionHost) getTarget() ); + } + +} diff --git a/core/localization/GuiText.java b/core/localization/GuiText.java index 0da253d2..075463a6 100644 --- a/core/localization/GuiText.java +++ b/core/localization/GuiText.java @@ -30,7 +30,7 @@ public enum GuiText StoredPower, MaxPower, RequiredPower, Efficiency, InWorldCrafting, inWorldFluix, inWorldPurificationCertus, inWorldPurificationNether, inWorldPurificationFluix, inWorldSingularity, ChargedQuartz, - OfSecondOutput, NoSecondOutput, RFTunnel, Stores, Next, SelectAmount, Lumen, Empty; + OfSecondOutput, NoSecondOutput, RFTunnel, Stores, Next, SelectAmount, Lumen, Empty, ConfirmCrafting; String root; diff --git a/core/sync/GuiBridge.java b/core/sync/GuiBridge.java index 00814249..f87166f5 100644 --- a/core/sync/GuiBridge.java +++ b/core/sync/GuiBridge.java @@ -36,6 +36,7 @@ import appeng.container.implementations.ContainerCellWorkbench; import appeng.container.implementations.ContainerChest; import appeng.container.implementations.ContainerCondenser; import appeng.container.implementations.ContainerCraftAmount; +import appeng.container.implementations.ContainerCraftConfirm; import appeng.container.implementations.ContainerCraftingTerm; import appeng.container.implementations.ContainerDrive; import appeng.container.implementations.ContainerFormationPlane; @@ -150,7 +151,9 @@ public enum GuiBridge implements IGuiHandler GUI_MAC(ContainerMAC.class, TileMolecularAssembler.class, WORLD, null), - GUI_CRAFTING_AMOUNT(ContainerCraftAmount.class, ITerminalHost.class, ITEM_OR_WORLD, SecurityPermissions.CRAFT); + GUI_CRAFTING_AMOUNT(ContainerCraftAmount.class, ITerminalHost.class, ITEM_OR_WORLD, SecurityPermissions.CRAFT), + + GUI_CRAFTING_CONFIRM(ContainerCraftConfirm.class, ITerminalHost.class, ITEM_OR_WORLD, SecurityPermissions.CRAFT); private Class Tile; private Class Gui; diff --git a/core/sync/packets/PacketCraftRequest.java b/core/sync/packets/PacketCraftRequest.java index 0288a1b1..b104850e 100644 --- a/core/sync/packets/PacketCraftRequest.java +++ b/core/sync/packets/PacketCraftRequest.java @@ -4,9 +4,13 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; import appeng.api.AEApi; import appeng.api.config.Actionable; @@ -14,18 +18,38 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; import appeng.api.storage.data.IAEItemStack; +import appeng.container.ContainerOpenContext; import appeng.container.implementations.ContainerCraftAmount; +import appeng.container.implementations.ContainerCraftConfirm; import appeng.core.AELog; import appeng.core.sync.AppEngPacket; +import appeng.core.sync.GuiBridge; import appeng.core.sync.network.INetworkInfo; import appeng.crafting.CraftingJob; import appeng.me.cache.CraftingCache; +import appeng.util.Platform; import appeng.util.item.AEItemStack; public class PacketCraftRequest extends AppEngPacket { final public IAEItemStack slotItem; + final public static ExecutorService craftingPool; + + static + { + ThreadFactory factory = new ThreadFactory() { + + @Override + public Thread newThread(Runnable ar) + { + return new Thread( ar, "AE Crafting Calculator" ); + } + + }; + + craftingPool = Executors.newCachedThreadPool( factory ); + } // automatic. public PacketCraftRequest(ByteBuf stream) throws IOException { @@ -56,6 +80,20 @@ public class PacketCraftRequest extends AppEngPacket { CraftingJob cj = new CraftingJob( cca, slotItem, Actionable.SIMULATE ); + ContainerOpenContext context = cca.openContext; + if ( context != null ) + { + TileEntity te = context.w.getTileEntity( context.x, context.y, context.z ); + Platform.openGUI( player, te, cca.openContext.side, GuiBridge.GUI_CRAFTING_CONFIRM ); + + if ( player.openContainer instanceof ContainerCraftConfirm ) + { + ContainerCraftConfirm ccc = (ContainerCraftConfirm) player.openContainer; + ccc.job = craftingPool.submit( cj, cj ); + cca.detectAndSendChanges(); + } + } + } catch (Throwable e) { diff --git a/crafting/CraftingJob.java b/crafting/CraftingJob.java index 756ab7cc..e91c48ad 100644 --- a/crafting/CraftingJob.java +++ b/crafting/CraftingJob.java @@ -8,6 +8,7 @@ import net.minecraft.nbt.NBTTagCompound; import appeng.api.AEApi; import appeng.api.config.Actionable; import appeng.api.networking.crafting.ICraftingPatternDetails; +import appeng.api.networking.security.BaseActionSource; import appeng.api.networking.storage.IStorageGrid; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; @@ -17,7 +18,7 @@ import appeng.util.Platform; import com.google.common.base.Stopwatch; -public class CraftingJob +public class CraftingJob implements Runnable { IAEItemStack output; @@ -28,10 +29,17 @@ public class CraftingJob ICraftingHost jobHost; + boolean simulate = false; + final MECraftingInventory original; + + public CraftingTreeNode tree; + private BaseActionSource actionSrc; + public CraftingJob(ICraftingHost host, NBTTagCompound data) { jobHost = host; storage = AEApi.instance().storage().createItemList(); prophecies = new HashSet(); + original = null; } public CraftingJob(ICraftingHost host, IAEItemStack what, Actionable mode) { @@ -39,41 +47,13 @@ public class CraftingJob output = what.copy(); storage = AEApi.instance().storage().createItemList(); prophecies = new HashSet(); + actionSrc = host.getActionSrc(); CraftingCache cc = host.getGrid().getCache( CraftingCache.class ); IStorageGrid sg = host.getGrid().getCache( IStorageGrid.class ); + original = new MECraftingInventory( sg.getItemInventory(), false, false, false ); - IItemList missing = AEApi.instance().storage().createItemList(); - - MECraftingInventory meci = new MECraftingInventory( sg.getItemInventory(), true, false, true ); - meci.ignore( what ); - - CraftingTreeNode tree = getCraftingTree( cc, what ); - - try - { - Stopwatch timer = Stopwatch.createStarted(); - tree.request( meci, what.getStackSize(), host.getActionSrc() ); - tree.dive( this ); - - for (String s : opsAndMultiplier.keySet()) - { - twoIntegers ti = opsAndMultiplier.get( s ); - AELog.info( s + " * " + ti.times + " = " + (ti.perOp * ti.times) ); - } - - AELog.info( "-------------" + timer.elapsed( TimeUnit.MILLISECONDS ) + "ms" ); - // if ( mode == Actionable.MODULATE ) - // meci.moveItemsToStorage( storage ); - } - catch (CraftBranchFailure e) - { - AELog.error( e ); - } - catch (CraftingCalculationFailure f) - { - AELog.error( f ); - } + tree = getCraftingTree( cc, what ); } private CraftingTreeNode getCraftingTree(CraftingCache cc, IAEItemStack what) @@ -86,16 +66,25 @@ public class CraftingJob } + IItemList crafting = AEApi.instance().storage().createItemList(); + IItemList missing = AEApi.instance().storage().createItemList(); + public void addTask(IAEItemStack what, long crafts, ICraftingPatternDetails details, int depth) { if ( crafts > 0 ) { postOp( "new task: " + Platform.getItemDisplayName( what ) + " x " + what.getStackSize(), what.getStackSize(), crafts ); + what = what.copy(); + what.setStackSize( what.getStackSize() * crafts ); + crafting.add( what ); } } public void addMissing(IAEItemStack what) { + what = what.copy(); + missing.add( what ); + postOp( "required material: " + Platform.getItemDisplayName( what ), 1, what.getStackSize() ); } @@ -117,4 +106,76 @@ public class CraftingJob ti.perOp = stackSize; ti.times += crafts; } + + @Override + public void run() + { + + try + { + Stopwatch timer = Stopwatch.createStarted(); + + MECraftingInventory meci = new MECraftingInventory( original, true, false, true ); + meci.ignore( output ); + + tree.request( meci, output.getStackSize(), actionSrc ); + tree.dive( this ); + + for (String s : opsAndMultiplier.keySet()) + { + twoIntegers ti = opsAndMultiplier.get( s ); + AELog.info( s + " * " + ti.times + " = " + (ti.perOp * ti.times) ); + } + + AELog.info( "------------- real" + timer.elapsed( TimeUnit.MILLISECONDS ) + "ms" ); + // if ( mode == Actionable.MODULATE ) + // meci.moveItemsToStorage( storage ); + } + catch (CraftBranchFailure e) + { + simulate = true; + + try + { + Stopwatch timer = Stopwatch.createStarted(); + MECraftingInventory meci = new MECraftingInventory( original, true, false, true ); + meci.ignore( output ); + + tree.setSimulate(); + tree.request( meci, output.getStackSize(), actionSrc ); + tree.dive( this ); + + for (String s : opsAndMultiplier.keySet()) + { + twoIntegers ti = opsAndMultiplier.get( s ); + AELog.info( s + " * " + ti.times + " = " + (ti.perOp * ti.times) ); + } + + AELog.info( "------------- simulate" + timer.elapsed( TimeUnit.MILLISECONDS ) + "ms" ); + } + catch (CraftBranchFailure e1) + { + AELog.error( e1 ); + } + catch (CraftingCalculationFailure f) + { + AELog.error( f ); + } + catch (InterruptedException e1) + { + AELog.info( "Crafting calculation canceled." ); + return; + } + } + catch (CraftingCalculationFailure f) + { + AELog.error( f ); + } + catch (InterruptedException e1) + { + AELog.info( "Crafting calculation canceled." ); + return; + } + + } } diff --git a/crafting/CraftingTreeNode.java b/crafting/CraftingTreeNode.java index 64ced342..cf9b9291 100644 --- a/crafting/CraftingTreeNode.java +++ b/crafting/CraftingTreeNode.java @@ -29,11 +29,14 @@ public class CraftingTreeNode boolean cannotUse = false; long missing = 0; + boolean sim; + public CraftingTreeNode(CraftingCache cc, CraftingJob job, IAEItemStack wat, CraftingTreeProcess par, int slot, int depth) { what = wat; parent = par; this.slot = slot; this.world = job.jobHost.getWorld(); + sim = false; for (ICraftingPatternDetails details : cc.getCraftingFor( what ))// in order. { @@ -62,8 +65,11 @@ public class CraftingTreeNode return parent.notRecurive( details ); } - public IAEItemStack request(MECraftingInventory inv, long l, BaseActionSource src) throws CraftBranchFailure + public IAEItemStack request(MECraftingInventory inv, long l, BaseActionSource src) throws CraftBranchFailure, InterruptedException { + if ( Thread.interrupted() ) + throw new InterruptedException(); + what.setStackSize( l ); if ( slot >= 0 && parent != null && parent.details.isCraftable() ) { @@ -153,9 +159,13 @@ public class CraftingTreeNode } } - missing += l; - return what; - // throw new CraftBranchFailure( what, l ); + if ( sim ) + { + missing += l; + return what; + } + + throw new CraftBranchFailure( what, l ); } public void dive(CraftingJob job) @@ -167,4 +177,13 @@ public class CraftingTreeNode for (CraftingTreeProcess pro : nodes) pro.dive( job ); } + + public void setSimulate() + { + sim = true; + missing = 0; + + for (CraftingTreeProcess pro : nodes) + pro.setSimulate(); + } } diff --git a/crafting/CraftingTreeProcess.java b/crafting/CraftingTreeProcess.java index 21acbd37..9399293d 100644 --- a/crafting/CraftingTreeProcess.java +++ b/crafting/CraftingTreeProcess.java @@ -86,8 +86,11 @@ public class CraftingTreeProcess throw new RuntimeException( "Crafting Tree construction failed." ); } - public void request(MECraftingInventory inv, long i, BaseActionSource src) throws CraftBranchFailure + public void request(MECraftingInventory inv, long i, BaseActionSource src) throws CraftBranchFailure, InterruptedException { + if ( Thread.interrupted() ) + throw new InterruptedException(); + // request and remove inputs... for (Entry entry : nodes.entrySet()) { @@ -129,4 +132,12 @@ public class CraftingTreeProcess for (CraftingTreeNode pro : nodes.keySet()) pro.dive( job ); } + + public void setSimulate() + { + crafts = 0; + + for (CraftingTreeNode pro : nodes.keySet()) + pro.setSimulate(); + } } diff --git a/helpers/PatternHelper.java b/helpers/PatternHelper.java index 7b4687ec..22bd14d4 100644 --- a/helpers/PatternHelper.java +++ b/helpers/PatternHelper.java @@ -195,11 +195,10 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable