504 lines
12 KiB
Java
504 lines
12 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 appeng.api.AEApi;
|
|
import appeng.api.config.Actionable;
|
|
import appeng.api.config.FuzzyMode;
|
|
import appeng.api.networking.crafting.ICraftingGrid;
|
|
import appeng.api.networking.crafting.ICraftingPatternDetails;
|
|
import appeng.api.networking.security.IActionSource;
|
|
import appeng.api.storage.channels.IItemStorageChannel;
|
|
import appeng.api.storage.data.IAEItemStack;
|
|
import appeng.api.storage.data.IItemList;
|
|
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
|
import appeng.util.Platform;
|
|
import net.minecraft.util.text.TextComponentString;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.fml.common.Optional;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
|
|
|
|
@Optional.Interface( iface = "gregtech.api.items.IToolItem", modid = "gregtech" )
|
|
public class CraftingTreeNode
|
|
{
|
|
|
|
// what slot!
|
|
private final int slot;
|
|
private final CraftingJob job;
|
|
private final IItemList<IAEItemStack> used = AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ).createList();
|
|
// parent node.
|
|
private final CraftingTreeProcess parent;
|
|
private final World world;
|
|
// what item is this?
|
|
private final IAEItemStack what;
|
|
// what are the crafting patterns for this?
|
|
private final ArrayList<CraftingTreeProcess> nodes = new ArrayList<>();
|
|
private final boolean canEmit;
|
|
private CraftingTreeNode loopHead;
|
|
private int times;
|
|
private int bytes = 0;
|
|
private long missing = 0;
|
|
private long howManyEmitted = 0;
|
|
private boolean exhausted = false;
|
|
|
|
public CraftingTreeNode( final ICraftingGrid cc, final CraftingJob job, final IAEItemStack wat, final CraftingTreeProcess par, final int slot )
|
|
{
|
|
this.what = wat;
|
|
this.parent = par;
|
|
this.slot = slot;
|
|
this.world = job.getWorld();
|
|
this.job = job;
|
|
|
|
this.canEmit = cc.canEmitFor( this.what );
|
|
|
|
if( this.canEmit )
|
|
{
|
|
return; // if you can emit for something, you can't make it with patterns.
|
|
}
|
|
|
|
for( final ICraftingPatternDetails details : cc.getCraftingFor( this.what, this.parent == null ? null : this.parent.details, slot, this.world ) )// in
|
|
// order.
|
|
{
|
|
int times = 1;
|
|
for( IAEItemStack o : details.getCondensedOutputs() )
|
|
{
|
|
if( what.equals( o ) )
|
|
{
|
|
times = (int) ( what.getStackSize() / o.getStackSize() + ( what.getStackSize() % o.getStackSize() != 0 ? 1 : 0 ) );
|
|
}
|
|
}
|
|
|
|
if( this.parent == null )
|
|
{
|
|
this.nodes.add( new CraftingTreeProcess( cc, job, details, times, this ) );
|
|
continue;
|
|
}
|
|
CraftingTreeNode recursive = recursiveNode( this );
|
|
if( recursive == null )
|
|
{
|
|
this.nodes.add( new CraftingTreeProcess( cc, job, details, times, this ) );
|
|
}
|
|
else
|
|
{
|
|
reserveForNode();
|
|
}
|
|
}
|
|
}
|
|
|
|
void reserveForNode()
|
|
{
|
|
if( this.equals( loopHead ) )
|
|
{
|
|
return;
|
|
}
|
|
IAEItemStack available = job.checkAvailable( this.what );
|
|
if( available != null && available.getStackSize() >= this.what.getStackSize() )
|
|
{
|
|
this.job.reserve( this, this.what.copy() );
|
|
}
|
|
else
|
|
{
|
|
parent.reserverForNode();
|
|
}
|
|
}
|
|
|
|
IAEItemStack request( final MECraftingInventory inv, long l, final IActionSource src ) throws CraftBranchFailure, InterruptedException
|
|
{
|
|
this.job.handlePausing();
|
|
if( this.canEmit )
|
|
{
|
|
final IAEItemStack wat = this.what.copy();
|
|
wat.setStackSize( l );
|
|
|
|
this.howManyEmitted = wat.getStackSize();
|
|
this.bytes += wat.getStackSize();
|
|
|
|
return wat;
|
|
}
|
|
|
|
final IItemList<IAEItemStack> inventoryList = inv.getItemList();
|
|
|
|
IAEItemStack reserved = job.getReserved().get( this );
|
|
|
|
final List<IAEItemStack> thingsUsed = new ArrayList<>();
|
|
|
|
this.what.setStackSize( l );
|
|
|
|
if( parent != null )
|
|
{
|
|
if( this.what.equals( job.getOutput() ) )
|
|
{
|
|
job.getNeededForLoop().add( this.what.copy() );
|
|
}
|
|
}
|
|
|
|
if( this.getSlot() >= 0 && this.parent != null && this.parent.details.isCraftable() )
|
|
{
|
|
Collection<IAEItemStack> itemList = new ArrayList<>();
|
|
|
|
if( this.what.getItem().isDamageable() || Platform.isGTDamageableItem( this.what.getItem() ) )
|
|
{
|
|
itemList.addAll( inventoryList.findFuzzy( this.what, FuzzyMode.IGNORE_ALL ) );
|
|
|
|
if( this.parent.details.canSubstitute() )
|
|
{
|
|
for( IAEItemStack is : inventoryList )
|
|
{
|
|
if( is.fuzzyComparison( this.what, FuzzyMode.IGNORE_ALL ) )
|
|
{
|
|
itemList.add( is );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
final IAEItemStack item = inventoryList.findPrecise( this.what );
|
|
if( item != null )
|
|
{
|
|
itemList.add( item );
|
|
}
|
|
if( this.parent.details.canSubstitute() )
|
|
{
|
|
for( IAEItemStack s : parent.details.getSubstituteInputs( this.slot ) )
|
|
{
|
|
if( s != null )
|
|
{
|
|
for( IAEItemStack ss : inventoryList.findFuzzy( s, FuzzyMode.IGNORE_ALL ) )
|
|
{
|
|
if( ss != null && !itemList.contains( ss ) )
|
|
{
|
|
itemList.add( ss );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for( IAEItemStack fuzz : itemList )
|
|
{
|
|
if( this.parent.details.isValidItemForSlot( this.getSlot(), fuzz.copy().getCachedItemStack( 1 ), this.world ) )
|
|
{
|
|
fuzz = fuzz.copy();
|
|
fuzz.setStackSize( l );
|
|
|
|
final IAEItemStack available;
|
|
|
|
if( reserved != null )
|
|
{
|
|
available = reserved;
|
|
}
|
|
else
|
|
{
|
|
available = inv.extractItems( fuzz, Actionable.MODULATE, src );
|
|
}
|
|
|
|
if( available != null )
|
|
{
|
|
if( !this.exhausted )
|
|
{
|
|
final IAEItemStack is = this.job.checkUse( available );
|
|
|
|
if( is != null )
|
|
{
|
|
thingsUsed.add( is.copy() );
|
|
this.used.add( is );
|
|
}
|
|
else if( reserved != null )
|
|
{
|
|
thingsUsed.add( reserved.copy() );
|
|
this.used.add( reserved );
|
|
}
|
|
}
|
|
|
|
this.bytes += available.getStackSize();
|
|
l -= available.getStackSize();
|
|
|
|
if( l == 0 )
|
|
{
|
|
return available;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
final IAEItemStack available;
|
|
|
|
if( parent == null && this.what.equals( job.getOutput() ) )
|
|
{
|
|
available = null;
|
|
}
|
|
else if( reserved != null )
|
|
{
|
|
available = reserved;
|
|
}
|
|
else
|
|
{
|
|
available = inv.extractItems( this.what, Actionable.MODULATE, src );
|
|
}
|
|
|
|
if( available != null )
|
|
{
|
|
if( !this.exhausted )
|
|
{
|
|
final IAEItemStack is = this.job.checkUse( available );
|
|
|
|
if( is != null )
|
|
{
|
|
thingsUsed.add( is.copy() );
|
|
this.used.add( is );
|
|
}
|
|
else if( reserved != null )
|
|
{
|
|
thingsUsed.add( reserved.copy() );
|
|
this.used.add( reserved );
|
|
}
|
|
}
|
|
|
|
this.bytes += available.getStackSize();
|
|
l -= available.getStackSize();
|
|
|
|
if( l == 0 )
|
|
{
|
|
return available;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.exhausted = true;
|
|
|
|
if( this.nodes.size() == 1 )
|
|
{
|
|
final CraftingTreeProcess pro = this.nodes.get( 0 );
|
|
|
|
while ( pro.possible && l > 0 )
|
|
{
|
|
final IAEItemStack madeWhat = pro.getAmountCrafted( this.what );
|
|
pro.request( inv, pro.getTimes( l, madeWhat.getStackSize() ), src );
|
|
|
|
madeWhat.setStackSize( l );
|
|
final IAEItemStack available = inv.extractItems( madeWhat, Actionable.MODULATE, src );
|
|
|
|
if( available != null )
|
|
{
|
|
this.bytes += available.getStackSize();
|
|
l -= available.getStackSize();
|
|
|
|
if( l <= 0 )
|
|
{
|
|
return available;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pro.possible = false; // ;P
|
|
}
|
|
}
|
|
}
|
|
else if( this.nodes.size() > 1 )
|
|
{
|
|
for( final CraftingTreeProcess pro : this.nodes )
|
|
{
|
|
try
|
|
{
|
|
while ( pro.possible && l > 0 )
|
|
{
|
|
final MECraftingInventory subInv = new MECraftingInventory( inv, true, true, true );
|
|
final IAEItemStack madeWhat = pro.getAmountCrafted( this.what );
|
|
|
|
pro.request( subInv, pro.getTimes( l, madeWhat.getStackSize() ), src );
|
|
|
|
this.what.setStackSize( l );
|
|
final IAEItemStack available = subInv.extractItems( this.what, Actionable.MODULATE, src );
|
|
|
|
if( available != null )
|
|
{
|
|
if( !subInv.commit( src ) )
|
|
{
|
|
throw new CraftBranchFailure( this.what, l );
|
|
}
|
|
|
|
this.bytes += available.getStackSize();
|
|
l -= available.getStackSize();
|
|
|
|
if( l <= 0 )
|
|
{
|
|
return available;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pro.possible = false; // ;P
|
|
}
|
|
}
|
|
}
|
|
catch( final CraftBranchFailure fail )
|
|
{
|
|
pro.possible = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( job.isSimulation() )
|
|
{
|
|
this.bytes += l;
|
|
this.missing += l;
|
|
final IAEItemStack rv = this.what.copy();
|
|
rv.setStackSize( l );
|
|
return rv;
|
|
}
|
|
|
|
for( final IAEItemStack o : thingsUsed )
|
|
{
|
|
this.job.refund( o.copy() );
|
|
o.setStackSize( -o.getStackSize() );
|
|
this.used.add( o );
|
|
}
|
|
|
|
throw new CraftBranchFailure( this.what, l );
|
|
}
|
|
|
|
CraftingTreeNode recursiveNode( CraftingTreeNode node )
|
|
{
|
|
if( this.parent == null )
|
|
{
|
|
return null;
|
|
}
|
|
if( node != this )
|
|
{
|
|
if( node.what.equals( this.what ) )
|
|
{
|
|
if( node.what.getStackSize() == this.what.getStackSize() )
|
|
{
|
|
loopHead = this;
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
return this.parent.notRecursive( node );
|
|
}
|
|
|
|
void dive( final CraftingJob job )
|
|
{
|
|
if( this.missing > 0 )
|
|
{
|
|
job.addMissing( this.getStack( this.missing ) );
|
|
}
|
|
// missing = 0;
|
|
|
|
job.addBytes( this.bytes );
|
|
|
|
for( final CraftingTreeProcess pro : this.nodes )
|
|
{
|
|
pro.dive( job );
|
|
}
|
|
}
|
|
|
|
IAEItemStack getStack( final long size )
|
|
{
|
|
final IAEItemStack is = this.what.copy();
|
|
is.setStackSize( size );
|
|
return is;
|
|
}
|
|
|
|
void setSimulate()
|
|
{
|
|
this.missing = 0;
|
|
this.bytes = 0;
|
|
this.used.resetStatus();
|
|
this.exhausted = false;
|
|
|
|
for( final CraftingTreeProcess pro : this.nodes )
|
|
{
|
|
pro.setSimulate();
|
|
}
|
|
}
|
|
|
|
public void setJob( final MECraftingInventory storage, final CraftingCPUCluster craftingCPUCluster, final IActionSource src ) throws CraftBranchFailure
|
|
{
|
|
for( final IAEItemStack i : this.used )
|
|
{
|
|
final IAEItemStack ex = storage.extractItems( i, Actionable.MODULATE, src );
|
|
|
|
if( ex == null || ex.getStackSize() != i.getStackSize() )
|
|
{
|
|
if( src.player().isPresent() )
|
|
{
|
|
src.player().get().sendStatusMessage( new TextComponentString( "System reported " + i.getStackSize() + " " + i.getDefinition().getItem().getItemStackDisplayName( i.getDefinition() ) + " available but could not extract anything" ), false );
|
|
}
|
|
throw new CraftBranchFailure( i, i.getStackSize() );
|
|
}
|
|
|
|
craftingCPUCluster.addStorage( ex );
|
|
}
|
|
|
|
if( this.howManyEmitted > 0 )
|
|
{
|
|
final IAEItemStack i = this.what.copy().reset();
|
|
i.setStackSize( this.howManyEmitted );
|
|
craftingCPUCluster.addEmitable( i );
|
|
}
|
|
|
|
for( final CraftingTreeProcess pro : this.nodes )
|
|
{
|
|
pro.setJob( storage, craftingCPUCluster, src );
|
|
}
|
|
}
|
|
|
|
void getPlan( final IItemList<IAEItemStack> plan )
|
|
{
|
|
if( this.missing > 0 )
|
|
{
|
|
final IAEItemStack o = this.what.copy();
|
|
o.setStackSize( this.missing );
|
|
plan.add( o );
|
|
}
|
|
|
|
if( this.howManyEmitted > 0 )
|
|
{
|
|
final IAEItemStack i = this.what.copy();
|
|
i.setCountRequestable( this.howManyEmitted );
|
|
plan.addRequestable( i );
|
|
}
|
|
|
|
for( final IAEItemStack i : this.used )
|
|
{
|
|
plan.add( i.copy() );
|
|
}
|
|
|
|
for( final CraftingTreeProcess pro : this.nodes )
|
|
{
|
|
pro.getPlan( plan );
|
|
}
|
|
}
|
|
|
|
int getSlot()
|
|
{
|
|
return this.slot;
|
|
}
|
|
}
|