/* * 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.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import com.google.common.collect.Lists; import net.minecraft.world.World; 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.BaseActionSource; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.me.cluster.implementations.CraftingCPUCluster; public class CraftingTreeNode { // what slot! private final int slot; private final CraftingJob job; private final IItemList used = AEApi.instance().storage().createItemList(); // 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 nodes = new ArrayList(); private int bytes = 0; private boolean canEmit = false; private long missing = 0; private long howManyEmitted = 0; private boolean exhausted = false; private boolean sim; public CraftingTreeNode( final ICraftingGrid cc, final CraftingJob job, final IAEItemStack wat, final CraftingTreeProcess par, final int slot, final int depth ) { this.what = wat; this.parent = par; this.slot = slot; this.world = job.getWorld(); this.job = job; this.sim = false; 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. { if( this.parent == null || this.parent.notRecursive( details ) ) { this.nodes.add( new CraftingTreeProcess( cc, job, details, this, depth + 1 ) ); } } } boolean notRecursive( final ICraftingPatternDetails details ) { IAEItemStack[] o = details.getCondensedOutputs(); for( final IAEItemStack i : o ) { if( i.equals( this.what ) ) { return false; } } o = details.getCondensedInputs(); for( final IAEItemStack i : o ) { if( i.equals( this.what ) ) { return false; } } if( this.parent == null ) { return true; } return this.parent.notRecursive( details ); } IAEItemStack request( final MECraftingInventory inv, long l, final BaseActionSource src ) throws CraftBranchFailure, InterruptedException { this.job.handlePausing(); final List thingsUsed = new LinkedList(); this.what.setStackSize( l ); if( this.getSlot() >= 0 && this.parent != null && this.parent.details.isCraftable() ) { final Collection itemList; final IItemList inventoryList = inv.getItemList(); if( this.parent.details.canSubstitute() ) { itemList = inventoryList.findFuzzy( this.what, FuzzyMode.IGNORE_ALL ); } else { itemList = Lists.newArrayList(); final IAEItemStack item = inventoryList.findPrecise( this.what ); if( item != null ) { itemList.add( item ); } } for( IAEItemStack fuzz : itemList ) { if( this.parent.details.isValidItemForSlot( this.getSlot(), fuzz.getItemStack(), this.world ) ) { fuzz = fuzz.copy(); fuzz.setStackSize( l ); final IAEItemStack 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 ); } } this.bytes += available.getStackSize(); l -= available.getStackSize(); if( l == 0 ) { return available; } } } } } else { final IAEItemStack 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 ); } } this.bytes += available.getStackSize(); l -= available.getStackSize(); if( l == 0 ) { return available; } } } if( this.canEmit ) { final IAEItemStack wat = this.what.copy(); wat.setStackSize( l ); this.howManyEmitted = wat.getStackSize(); this.bytes += wat.getStackSize(); return wat; } 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 ); pro.request( subInv, 1, 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( this.sim ) { this.missing += l; this.bytes += 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 ); } void dive( final CraftingJob job ) { if( this.missing > 0 ) { job.addMissing( this.getStack( this.missing ) ); } // missing = 0; job.addBytes( 8 + 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.sim = true; 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 BaseActionSource 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() ) { throw new CraftBranchFailure( i, i.getStackSize() ); } craftingCPUCluster.addStorage( ex ); } if( this.howManyEmitted > 0 ) { final IAEItemStack i = this.what.copy(); i.setStackSize( this.howManyEmitted ); craftingCPUCluster.addEmitable( i ); } for( final CraftingTreeProcess pro : this.nodes ) { pro.setJob( storage, craftingCPUCluster, src ); } } void getPlan( final IItemList 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; } }