diff --git a/src/main/java/appeng/crafting/CraftingJob.java b/src/main/java/appeng/crafting/CraftingJob.java index 4976861de..b2377c4d1 100644 --- a/src/main/java/appeng/crafting/CraftingJob.java +++ b/src/main/java/appeng/crafting/CraftingJob.java @@ -59,6 +59,7 @@ public class CraftingJob implements Runnable, ICraftingJob private final IItemList missing = AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ).createList(); private final HashMap opsAndMultiplier = new HashMap<>(); private final Object monitor = new Object(); + private final Stopwatch watch = Stopwatch.createUnstarted(); private CraftingTreeNode tree; private final IAEItemStack output; private boolean simulate = false; @@ -66,7 +67,10 @@ public class CraftingJob implements Runnable, ICraftingJob private long bytes = 0; private final IActionSource actionSrc; private final ICraftingCallback callback; + private boolean running = false; private boolean done = false; + private int time; + private int incTime; private World wrapWorld( final World w ) { @@ -133,6 +137,7 @@ public class CraftingJob implements Runnable, ICraftingJob try { TickHandler.INSTANCE.registerCraftingSimulation( this.world, this ); + this.handlePausing(); final Stopwatch timer = Stopwatch.createStarted(); @@ -214,6 +219,41 @@ public class CraftingJob implements Runnable, ICraftingJob this.finish(); } + void handlePausing() throws InterruptedException + { + if( !this.actionSrc.player().isPresent() && this.incTime > 100 ) + { + this.incTime = 0; + synchronized ( this.monitor ) + { + if( this.watch.elapsed( TimeUnit.MICROSECONDS ) > this.time ) + { + this.running = false; + this.watch.stop(); + this.monitor.notify(); + } + + if( !this.running ) + { + AELog.craftingDebug( "crafting job will now sleep" ); + + while ( !this.running ) + { + this.monitor.wait(); + } + + AELog.craftingDebug( "crafting job now active" ); + } + } + + if( Thread.interrupted() ) + { + throw new InterruptedException(); + } + } + this.incTime++; + } + private void finish() { if( this.callback != null ) @@ -225,6 +265,7 @@ public class CraftingJob implements Runnable, ICraftingJob synchronized( this.monitor ) { + this.running = false; this.done = true; this.monitor.notify(); } @@ -270,13 +311,41 @@ public class CraftingJob implements Runnable, ICraftingJob /** * @return true if this needs more simulation */ - public boolean simulateFor() + public boolean simulateFor(final int milli) { - synchronized( this.monitor ) { - if ( this.done ) { + this.time = milli; + + synchronized ( this.monitor ) + { + if( this.done ) + { return false; } + if( !this.actionSrc.player().isPresent() ) + { + this.watch.reset(); + this.watch.start(); + this.running = true; + + AELog.craftingDebug( "main thread is now going to sleep" ); + + this.monitor.notify(); + + while ( this.running ) + { + try + { + this.monitor.wait(); + } + catch( final InterruptedException ignored ) + { + } + } + + AELog.craftingDebug( "main thread is now active" ); + } } + return true; } diff --git a/src/main/java/appeng/crafting/CraftingTreeNode.java b/src/main/java/appeng/crafting/CraftingTreeNode.java index bf285c4df..b7cba1bf1 100644 --- a/src/main/java/appeng/crafting/CraftingTreeNode.java +++ b/src/main/java/appeng/crafting/CraftingTreeNode.java @@ -119,7 +119,7 @@ public class CraftingTreeNode IAEItemStack request( final MECraftingInventory inv, long l, final IActionSource src ) throws CraftBranchFailure, InterruptedException { - + this.job.handlePausing(); final List thingsUsed = new ArrayList<>(); this.what.setStackSize( l ); diff --git a/src/main/java/appeng/crafting/CraftingTreeProcess.java b/src/main/java/appeng/crafting/CraftingTreeProcess.java index 5bddbd27e..43aa7bf5a 100644 --- a/src/main/java/appeng/crafting/CraftingTreeProcess.java +++ b/src/main/java/appeng/crafting/CraftingTreeProcess.java @@ -185,7 +185,7 @@ public class CraftingTreeProcess void request( final MECraftingInventory inv, final long i, final IActionSource src ) throws CraftBranchFailure, InterruptedException { - + this.job.handlePausing(); if( this.fullSimulation ) { final InventoryCrafting ic = new InventoryCrafting( new ContainerNull(), 3, 3 ); diff --git a/src/main/java/appeng/hooks/TickHandler.java b/src/main/java/appeng/hooks/TickHandler.java index 76f70df2c..bf6af3b0e 100644 --- a/src/main/java/appeng/hooks/TickHandler.java +++ b/src/main/java/appeng/hooks/TickHandler.java @@ -187,20 +187,15 @@ public class TickHandler if( ev.type == Type.WORLD && ev.phase == Phase.END ) { final WorldTickEvent wte = (WorldTickEvent) ev; - synchronized( this.craftingJobs ) + synchronized ( this.craftingJobs ) { final Collection jobSet = this.craftingJobs.get( wte.world ); - if( !jobSet.isEmpty() ) - { - final Iterator i = jobSet.iterator(); - while( i.hasNext() ) - { - final CraftingJob cj = i.next(); - if( !cj.simulateFor() ) - { - i.remove(); - } - } + if (!jobSet.isEmpty()) { + final int jobSize = jobSet.size(); + final int microSecondsPerTick = AEConfig.instance().getCraftingCalculationTimePerTick() * 1000; + final int simTime = Math.max(1, microSecondsPerTick / jobSize); + + jobSet.removeIf( cj -> !cj.simulateFor( simTime ) ); } } }