/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. * * Applied Energistics 2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Applied Energistics 2. If not, see . */ package appeng.crafting; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.TimeUnit; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; import com.google.common.base.Stopwatch; import appeng.api.AEApi; import appeng.api.config.Actionable; import appeng.api.networking.IGrid; import appeng.api.networking.crafting.ICraftingCallback; import appeng.api.networking.crafting.ICraftingGrid; import appeng.api.networking.crafting.ICraftingJob; 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; import appeng.core.AELog; import appeng.hooks.TickHandler; public class CraftingJob implements Runnable, ICraftingJob { final IItemList storage; final HashSet prophecies; final MECraftingInventory original; final World world; final IItemList crafting = AEApi.instance().storage().createItemList(); final IItemList missing = AEApi.instance().storage().createItemList(); final HashMap opsAndMultiplier = new HashMap(); private final Object monitor = new Object(); private final Stopwatch watch = Stopwatch.createUnstarted(); public CraftingTreeNode tree; IAEItemStack output; boolean simulate = false; MECraftingInventory availableCheck; long bytes = 0; private BaseActionSource actionSrc; private ICraftingCallback callback; private boolean running = false; private boolean done = false; private int time = 5; private int incTime = Integer.MAX_VALUE; public CraftingJob( World w, NBTTagCompound data ) { this.world = this.wrapWorld( w ); this.storage = AEApi.instance().storage().createItemList(); this.prophecies = new HashSet(); this.original = null; this.availableCheck = null; } private World wrapWorld( World w ) { return w; } public CraftingJob( World w, IGrid grid, BaseActionSource actionSrc, IAEItemStack what, ICraftingCallback callback ) { this.world = this.wrapWorld( w ); this.output = what.copy(); this.storage = AEApi.instance().storage().createItemList(); this.prophecies = new HashSet(); this.actionSrc = actionSrc; this.callback = callback; ICraftingGrid cc = grid.getCache( ICraftingGrid.class ); IStorageGrid sg = grid.getCache( IStorageGrid.class ); this.original = new MECraftingInventory( sg.getItemInventory(), actionSrc, false, false, false ); this.tree = this.getCraftingTree( cc, what ); this.availableCheck = null; } private CraftingTreeNode getCraftingTree( ICraftingGrid cc, IAEItemStack what ) { return new CraftingTreeNode( cc, this, what, null, -1, 0 ); } public void refund( IAEItemStack o ) { this.availableCheck.injectItems( o, Actionable.MODULATE, this.actionSrc ); } public IAEItemStack checkUse( IAEItemStack available ) { return this.availableCheck.extractItems( available, Actionable.MODULATE, this.actionSrc ); } public void writeToNBT( NBTTagCompound out ) { } public void addTask( IAEItemStack what, long crafts, ICraftingPatternDetails details, int depth ) { if( crafts > 0 ) { what = what.copy(); what.setStackSize( what.getStackSize() * crafts ); this.crafting.add( what ); } } public void addMissing( IAEItemStack what ) { what = what.copy(); this.missing.add( what ); } @Override public void run() { try { try { TickHandler.INSTANCE.registerCraftingSimulation( this.world, this ); this.handlePausing(); Stopwatch timer = Stopwatch.createStarted(); MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true ); craftingInventory.ignore( this.output ); this.availableCheck = new MECraftingInventory( this.original, false, false, false ); this.tree.request( craftingInventory, this.output.getStackSize(), this.actionSrc ); this.tree.dive( this ); for( String s : this.opsAndMultiplier.keySet() ) { TwoIntegers ti = this.opsAndMultiplier.get( s ); AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) ); } AELog.crafting( "------------- " + this.bytes + "b real" + timer.elapsed( TimeUnit.MILLISECONDS ) + "ms" ); // if ( mode == Actionable.MODULATE ) // craftingInventory.moveItemsToStorage( storage ); } catch( CraftBranchFailure e ) { this.simulate = true; try { Stopwatch timer = Stopwatch.createStarted(); MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true ); craftingInventory.ignore( this.output ); this.availableCheck = new MECraftingInventory( this.original, false, false, false ); this.tree.setSimulate(); this.tree.request( craftingInventory, this.output.getStackSize(), this.actionSrc ); this.tree.dive( this ); for( String s : this.opsAndMultiplier.keySet() ) { TwoIntegers ti = this.opsAndMultiplier.get( s ); AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) ); } AELog.crafting( "------------- " + this.bytes + "b simulate" + timer.elapsed( TimeUnit.MILLISECONDS ) + "ms" ); } catch( CraftBranchFailure e1 ) { AELog.error( e1 ); } catch( CraftingCalculationFailure f ) { AELog.error( f ); } catch( InterruptedException e1 ) { AELog.crafting( "Crafting calculation canceled." ); this.finish(); return; } } catch( CraftingCalculationFailure f ) { AELog.error( f ); } catch( InterruptedException e1 ) { AELog.crafting( "Crafting calculation canceled." ); this.finish(); return; } this.log( "crafting job now done" ); } catch( Throwable t ) { this.finish(); throw new IllegalStateException( t ); } this.finish(); } public void handlePausing() throws InterruptedException { if( 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 ) { this.log( "crafting job will now sleep" ); while( !this.running ) { this.monitor.wait(); } this.log( "crafting job now active" ); } } if( Thread.interrupted() ) { throw new InterruptedException(); } } this.incTime++; } public void finish() { if( this.callback != null ) { this.callback.calculationComplete( this ); } this.availableCheck = null; synchronized( this.monitor ) { this.running = false; this.done = true; this.monitor.notify(); } } private void log( String string ) { // AELog.crafting( string ); } @Override public boolean isSimulation() { return this.simulate; } @Override public long getByteTotal() { return this.bytes; } @Override public void populatePlan( IItemList plan ) { if( this.tree != null ) { this.tree.getPlan( plan ); } } @Override public IAEItemStack getOutput() { return this.output; } public boolean isDone() { return this.done; } public World getWorld() { return this.world; } /** * returns true if this needs more simulation. * * @param milli milliseconds of simulation * * @return true if this needs more simulation */ public boolean simulateFor( int milli ) { this.time = milli; synchronized( this.monitor ) { if( this.done ) { return false; } this.watch.reset(); this.watch.start(); this.running = true; this.log( "main thread is now going to sleep" ); this.monitor.notify(); while( this.running ) { try { this.monitor.wait(); } catch( InterruptedException ignored ) { } } this.log( "main thread is now active" ); } return true; } public void addBytes( long crafts ) { this.bytes += crafts; } static class TwoIntegers { public final long perOp = 0; public final long times = 0; } }