Applied-Energistics-2-tiler.../src/main/java/appeng/crafting/CraftingJob.java
yueh 02ac8cf220 Refactored the logging
Using LogManager instead of FMLRelaunchLog to access the logger instance.
Added logging of the name of failed exports instead of exception.
Improved crafting log to include issuer including their location and the
requested item.
Removed superfluous FMLRelaunchLog instance.
Removed superfluous parameters for PlayerData constructor.
2016-01-01 02:55:36 +01:00

409 lines
10 KiB
Java

/*
* 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 <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.crafting;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Stopwatch;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import appeng.api.AEApi;
import appeng.api.config.Actionable;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
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.security.IActionHost;
import appeng.api.networking.security.MachineSource;
import appeng.api.networking.security.PlayerSource;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.api.util.DimensionalCoord;
import appeng.core.AELog;
import appeng.hooks.TickHandler;
public class CraftingJob implements Runnable, ICraftingJob
{
private static final String LOG_CRAFTING_JOB = "CraftingJob (%s) issued by %s requesting [%s] using %s bytes took %s ms";
private static final String LOG_MACHINE_SOURCE_DETAILS = "Machine[object=%s, %s]";
private final MECraftingInventory original;
private final World world;
private final IItemList<IAEItemStack> crafting = AEApi.instance().storage().createItemList();
private final IItemList<IAEItemStack> missing = AEApi.instance().storage().createItemList();
private final HashMap<String, TwoIntegers> opsAndMultiplier = new HashMap<String, TwoIntegers>();
private final Object monitor = new Object();
private final Stopwatch watch = Stopwatch.createUnstarted();
private CraftingTreeNode tree;
private final IAEItemStack output;
private boolean simulate = false;
private MECraftingInventory availableCheck;
private long bytes = 0;
private final BaseActionSource actionSrc;
private final ICraftingCallback callback;
private boolean running = false;
private boolean done = false;
private int time = 5;
private int incTime = Integer.MAX_VALUE;
private World wrapWorld( final World w )
{
return w;
}
public CraftingJob( final World w, final IGrid grid, final BaseActionSource actionSrc, final IAEItemStack what, final ICraftingCallback callback )
{
this.world = this.wrapWorld( w );
this.output = what.copy();
this.actionSrc = actionSrc;
this.callback = callback;
final ICraftingGrid cc = grid.getCache( ICraftingGrid.class );
final IStorageGrid sg = grid.getCache( IStorageGrid.class );
this.original = new MECraftingInventory( sg.getItemInventory(), actionSrc, false, false, false );
this.setTree( this.getCraftingTree( cc, what ) );
this.availableCheck = null;
}
private CraftingTreeNode getCraftingTree( final ICraftingGrid cc, final IAEItemStack what )
{
return new CraftingTreeNode( cc, this, what, null, -1, 0 );
}
void refund( final IAEItemStack o )
{
this.availableCheck.injectItems( o, Actionable.MODULATE, this.actionSrc );
}
IAEItemStack checkUse( final IAEItemStack available )
{
return this.availableCheck.extractItems( available, Actionable.MODULATE, this.actionSrc );
}
public void writeToNBT( final NBTTagCompound out )
{
}
void addTask( IAEItemStack what, final long crafts, final ICraftingPatternDetails details, final int depth )
{
if( crafts > 0 )
{
what = what.copy();
what.setStackSize( what.getStackSize() * crafts );
this.crafting.add( what );
}
}
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();
final Stopwatch timer = Stopwatch.createStarted();
final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true );
craftingInventory.ignore( this.output );
this.availableCheck = new MECraftingInventory( this.original, false, false, false );
this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc );
this.getTree().dive( this );
for( final String s : this.opsAndMultiplier.keySet() )
{
final TwoIntegers ti = this.opsAndMultiplier.get( s );
AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) );
}
this.logCraftingJob( "real", timer );
// if ( mode == Actionable.MODULATE )
// craftingInventory.moveItemsToStorage( storage );
}
catch( final CraftBranchFailure e )
{
this.simulate = true;
try
{
final Stopwatch timer = Stopwatch.createStarted();
final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true );
craftingInventory.ignore( this.output );
this.availableCheck = new MECraftingInventory( this.original, false, false, false );
this.getTree().setSimulate();
this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc );
this.getTree().dive( this );
for( final String s : this.opsAndMultiplier.keySet() )
{
final TwoIntegers ti = this.opsAndMultiplier.get( s );
AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) );
}
this.logCraftingJob( "simulate", timer );
}
catch( final CraftBranchFailure e1 )
{
AELog.debug( e1 );
}
catch( final CraftingCalculationFailure f )
{
AELog.debug( f );
}
catch( final InterruptedException e1 )
{
AELog.crafting( "Crafting calculation canceled." );
this.finish();
return;
}
}
catch( final CraftingCalculationFailure f )
{
AELog.debug( f );
}
catch( final InterruptedException e1 )
{
AELog.crafting( "Crafting calculation canceled." );
this.finish();
return;
}
AELog.craftingDebug( "crafting job now done" );
}
catch( final Throwable t )
{
this.finish();
throw new IllegalStateException( t );
}
this.finish();
}
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 )
{
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 )
{
this.callback.calculationComplete( this );
}
this.availableCheck = null;
synchronized( this.monitor )
{
this.running = false;
this.done = true;
this.monitor.notify();
}
}
@Override
public boolean isSimulation()
{
return this.simulate;
}
@Override
public long getByteTotal()
{
return this.bytes;
}
@Override
public void populatePlan( final IItemList<IAEItemStack> plan )
{
if( this.getTree() != null )
{
this.getTree().getPlan( plan );
}
}
@Override
public IAEItemStack getOutput()
{
return this.output;
}
public boolean isDone()
{
return this.done;
}
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( final int milli )
{
this.time = milli;
synchronized( this.monitor )
{
if( this.done )
{
return false;
}
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;
}
void addBytes( final long crafts )
{
this.bytes += crafts;
}
public CraftingTreeNode getTree()
{
return this.tree;
}
private void setTree( final CraftingTreeNode tree )
{
this.tree = tree;
}
private void logCraftingJob( String type, Stopwatch timer )
{
if( AELog.isCraftingLogEnabled() )
{
final String itemToOutput = this.output.toString();
final long elapsedTime = timer.elapsed( TimeUnit.MILLISECONDS );
final String actionSource;
if( this.actionSrc instanceof MachineSource )
{
final IActionHost machineSource = ( (MachineSource) this.actionSrc ).via;
final IGridNode actionableNode = machineSource.getActionableNode();
final IGridHost machine = actionableNode.getMachine();
final DimensionalCoord location = actionableNode.getGridBlock().getLocation();
actionSource = String.format( LOG_MACHINE_SOURCE_DETAILS, machine, location );
}
else if( this.actionSrc instanceof PlayerSource )
{
final PlayerSource source = (PlayerSource) this.actionSrc;
final EntityPlayer player = source.player;
actionSource = player.toString();
}
else
{
actionSource = "[unknown source]";
}
AELog.crafting( LOG_CRAFTING_JOB, type, actionSource, itemToOutput, this.bytes, elapsedTime );
}
}
private static class TwoIntegers
{
private final long perOp = 0;
private final long times = 0;
}
}