heavily tweaked auto-crafting
This commit is contained in:
@@ -65,6 +65,7 @@ public class CraftingJob implements Runnable, ICraftingJob
|
||||
private final Object monitor = new Object();
|
||||
private final Stopwatch tickSpreadingWatch = Stopwatch.createUnstarted();
|
||||
private final Stopwatch craftingTreeWatch = Stopwatch.createUnstarted();
|
||||
private final ICraftingGrid cc;
|
||||
private CraftingTreeNode tree;
|
||||
private final IAEItemStack output;
|
||||
private boolean simulate = false;
|
||||
@@ -90,7 +91,7 @@ public class CraftingJob implements Runnable, ICraftingJob
|
||||
|
||||
this.callback = callback;
|
||||
|
||||
final ICraftingGrid cc = grid.getCache( ICraftingGrid.class );
|
||||
this.cc = grid.getCache( ICraftingGrid.class );
|
||||
final GridStorageCache sg = grid.getCache( IStorageGrid.class );
|
||||
this.original = new MECraftingInventory( sg.getExtractableList( actionSrc ) );
|
||||
|
||||
@@ -159,13 +160,13 @@ public class CraftingJob implements Runnable, ICraftingJob
|
||||
TickHandler.INSTANCE.registerCraftingSimulation( this.world, this );
|
||||
this.handlePausing();
|
||||
|
||||
craftingTreeWatch.start();
|
||||
|
||||
final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true );
|
||||
craftingInventory.ignore( this.output );
|
||||
|
||||
this.availableCheck = new MECraftingInventory( this.original, false, false, false );
|
||||
craftingTreeWatch.start();
|
||||
this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc );
|
||||
craftingTreeWatch.stop();
|
||||
this.getTree().dive( this );
|
||||
|
||||
for( final String s : this.opsAndMultiplier.keySet() )
|
||||
@@ -174,8 +175,14 @@ public class CraftingJob implements Runnable, ICraftingJob
|
||||
AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) );
|
||||
}
|
||||
|
||||
craftingTreeWatch.stop();
|
||||
this.logCraftingJob( "real, success", craftingTreeWatch );
|
||||
if( actionSrc.player().isPresent() )
|
||||
{
|
||||
this.logCraftingJob( "simulated, success", craftingTreeWatch );
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logCraftingJob( "real, success", craftingTreeWatch );
|
||||
}
|
||||
}
|
||||
catch( final CraftBranchFailure e )
|
||||
{
|
||||
@@ -185,14 +192,14 @@ public class CraftingJob implements Runnable, ICraftingJob
|
||||
{
|
||||
if( actionSrc.player().isPresent() )
|
||||
{
|
||||
craftingTreeWatch.reset().start();
|
||||
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.availableCheck = new MECraftingInventory( this.original, false, false, false );
|
||||
craftingTreeWatch.reset().start();
|
||||
this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc );
|
||||
craftingTreeWatch.stop();
|
||||
this.getTree().dive( this );
|
||||
|
||||
for( final String s : this.opsAndMultiplier.keySet() )
|
||||
@@ -201,12 +208,10 @@ public class CraftingJob implements Runnable, ICraftingJob
|
||||
AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) );
|
||||
}
|
||||
|
||||
craftingTreeWatch.stop();
|
||||
this.logCraftingJob( "simulate", craftingTreeWatch );
|
||||
this.logCraftingJob( "simulated, failed", craftingTreeWatch );
|
||||
}
|
||||
else
|
||||
{
|
||||
craftingTreeWatch.stop();
|
||||
this.logCraftingJob( "real, failed", craftingTreeWatch );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import appeng.api.config.FuzzyMode;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@@ -53,14 +52,14 @@ public class CraftingTreeNode
|
||||
private final IAEItemStack what;
|
||||
// what are the crafting patterns for this?
|
||||
private final ArrayList<CraftingTreeProcess> nodes = new ArrayList<>();
|
||||
private final ICraftingGrid cc;
|
||||
private final int depth;
|
||||
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;
|
||||
@@ -68,9 +67,18 @@ public class CraftingTreeNode
|
||||
this.slot = slot;
|
||||
this.world = job.getWorld();
|
||||
this.job = job;
|
||||
this.sim = false;
|
||||
this.cc = cc;
|
||||
this.depth = depth;
|
||||
|
||||
this.canEmit = cc.canEmitFor( this.what );
|
||||
}
|
||||
|
||||
public void addNode()
|
||||
{
|
||||
if( !nodes.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( this.canEmit )
|
||||
{
|
||||
@@ -89,19 +97,42 @@ public class CraftingTreeNode
|
||||
|
||||
IAEItemStack request( final MECraftingInventory inv, long l, final IActionSource src ) throws CraftBranchFailure, InterruptedException
|
||||
{
|
||||
addNode();
|
||||
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();
|
||||
final List<IAEItemStack> thingsUsed = new ArrayList<>();
|
||||
|
||||
this.what.setStackSize( l );
|
||||
|
||||
if( this.getSlot() >= 0 && this.parent != null && this.parent.details.isCraftable() )
|
||||
{
|
||||
Collection<IAEItemStack> itemList = new ArrayList<>();
|
||||
|
||||
if( this.parent.getContainerItems() != null && !this.parent.getContainerItems().findFuzzy( this.what, FuzzyMode.IGNORE_ALL ).isEmpty() )
|
||||
if( this.what.getItem().hasContainerItem( this.what.getDefinition() ) )
|
||||
{
|
||||
itemList = inventoryList.findFuzzy( this.what, FuzzyMode.IGNORE_ALL );
|
||||
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
|
||||
{
|
||||
@@ -110,6 +141,10 @@ public class CraftingTreeNode
|
||||
{
|
||||
itemList.add( item );
|
||||
}
|
||||
if( this.parent.details.canSubstitute() )
|
||||
{
|
||||
itemList.addAll( inventoryList.findFuzzy( this.what, FuzzyMode.IGNORE_ALL ) );
|
||||
}
|
||||
}
|
||||
|
||||
for( IAEItemStack fuzz : itemList )
|
||||
@@ -172,17 +207,6 @@ public class CraftingTreeNode
|
||||
}
|
||||
}
|
||||
|
||||
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 )
|
||||
@@ -192,11 +216,9 @@ public class CraftingTreeNode
|
||||
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 )
|
||||
@@ -257,10 +279,10 @@ public class CraftingTreeNode
|
||||
}
|
||||
}
|
||||
|
||||
if( this.sim )
|
||||
if( job.isSimulation() )
|
||||
{
|
||||
this.missing += l;
|
||||
this.bytes += l;
|
||||
this.missing += l;
|
||||
final IAEItemStack rv = this.what.copy();
|
||||
rv.setStackSize( l );
|
||||
return rv;
|
||||
@@ -306,7 +328,6 @@ public class CraftingTreeNode
|
||||
|
||||
void setSimulate()
|
||||
{
|
||||
this.sim = true;
|
||||
this.missing = 0;
|
||||
this.bytes = 0;
|
||||
this.used.resetStatus();
|
||||
|
||||
@@ -20,15 +20,13 @@ package appeng.crafting;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import appeng.api.config.FuzzyMode;
|
||||
import appeng.me.cache.CraftingGridCache;
|
||||
import appeng.util.item.AEItemStack;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.Lists;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongArrayMap;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
@@ -52,11 +50,11 @@ public class CraftingTreeProcess
|
||||
private final CraftingJob job;
|
||||
private final Object2LongArrayMap<CraftingTreeNode> nodes = new Object2LongArrayMap<>();
|
||||
private final int depth;
|
||||
private final ICraftingGrid cc;
|
||||
private final World world;
|
||||
boolean possible = true;
|
||||
private long crafts = 0;
|
||||
private IItemList<IAEItemStack> containerItems;
|
||||
private boolean limitQty;
|
||||
private List<IAEItemStack> containerItemsList;
|
||||
private long bytes = 0;
|
||||
|
||||
public CraftingTreeProcess( final ICraftingGrid cc, final CraftingJob job, final ICraftingPatternDetails details, final CraftingTreeNode craftingTreeNode, final int depth )
|
||||
@@ -65,20 +63,22 @@ public class CraftingTreeProcess
|
||||
this.details = details;
|
||||
this.job = job;
|
||||
this.depth = depth;
|
||||
final World world = job.getWorld();
|
||||
this.cc = cc;
|
||||
this.world = job.getWorld();
|
||||
}
|
||||
|
||||
public void addProcess()
|
||||
{
|
||||
if( !nodes.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
final IAEItemStack[] list = details.getInputs();
|
||||
|
||||
for( final IAEItemStack part : details.getCondensedInputs() )
|
||||
{
|
||||
if( part.getItem().hasContainerItem( part.getDefinition() ) )
|
||||
{
|
||||
if( containerItems == null )
|
||||
{
|
||||
containerItems = AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ).createList();
|
||||
containerItemsList = new ArrayList<>();
|
||||
}
|
||||
containerItems.add( part );
|
||||
this.limitQty = true;
|
||||
//break;
|
||||
}
|
||||
@@ -96,13 +96,33 @@ public class CraftingTreeProcess
|
||||
if( part.equals( comparePart ) )
|
||||
{
|
||||
boolean isPartContainer = false;
|
||||
if( containerItems != null && !containerItems.findFuzzy( list[x], FuzzyMode.IGNORE_ALL ).isEmpty() )
|
||||
if( part.getItem().hasContainerItem( part.getDefinition() ) )
|
||||
{
|
||||
part = list[x];
|
||||
isPartContainer = true;
|
||||
}
|
||||
long wantedSize = part.getStackSize();
|
||||
|
||||
if( isPartContainer && wantedSize > 0 )
|
||||
{
|
||||
if( details.canSubstitute() && cc.getCraftingFor( part, details, x, world ).isEmpty() )
|
||||
{
|
||||
IItemList<IAEItemStack> aa = ( (CraftingGridCache) cc ).getAvailableItems( AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ).createList() );
|
||||
for( IAEItemStack is : aa )
|
||||
{
|
||||
if( is.fuzzyComparison( part, FuzzyMode.IGNORE_ALL ) )
|
||||
{
|
||||
wantedSize -= 1;
|
||||
this.nodes.put( new CraftingTreeNode( cc, job, is.copy().setStackSize( 1 ), this, x, depth + 1 ), 1 );
|
||||
if( wantedSize == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !isPartContainer )
|
||||
{
|
||||
IAEItemStack found = job.checkAvailable( part );
|
||||
@@ -270,11 +290,6 @@ public class CraftingTreeProcess
|
||||
}
|
||||
}
|
||||
|
||||
IItemList<IAEItemStack> getContainerItems()
|
||||
{
|
||||
return this.containerItems;
|
||||
}
|
||||
|
||||
boolean notRecursive()
|
||||
{
|
||||
return this.parent == null || this.parent.notRecursive();
|
||||
@@ -291,28 +306,34 @@ public class CraftingTreeProcess
|
||||
|
||||
void request( final MECraftingInventory inv, final long amountOfTimes, final IActionSource src ) throws CraftBranchFailure, InterruptedException
|
||||
{
|
||||
addProcess();
|
||||
this.job.handlePausing();
|
||||
List<IAEItemStack> containerItems = null;
|
||||
|
||||
// request and remove inputs...
|
||||
for( final Entry<CraftingTreeNode, Long> entry : this.nodes.object2LongEntrySet() )
|
||||
{
|
||||
final IAEItemStack stack = entry.getKey().request( inv, entry.getValue() * amountOfTimes, src );
|
||||
|
||||
if( containerItems != null && !this.containerItems.findFuzzy( stack, FuzzyMode.IGNORE_ALL ).isEmpty() )
|
||||
if( stack.getItem().hasContainerItem( stack.getDefinition() ) )
|
||||
{
|
||||
final ItemStack is = Platform.getContainerItem( stack.createItemStack() );
|
||||
final IAEItemStack o = AEItemStack.fromItemStack( is );
|
||||
if( o != null )
|
||||
{
|
||||
if( containerItems == null )
|
||||
{
|
||||
containerItems = new ArrayList<>();
|
||||
}
|
||||
this.bytes++;
|
||||
containerItemsList.add( o );
|
||||
containerItems.add( o );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( containerItems != null )
|
||||
{
|
||||
for( IAEItemStack i : containerItemsList )
|
||||
for( IAEItemStack i : containerItems )
|
||||
{
|
||||
inv.injectItems( i, Actionable.MODULATE, src );
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class MECraftingInventory implements IMEInventory<IAEItemStack>
|
||||
this.localCache = new ItemListIgnoreCrafting<>( AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ).createList() );
|
||||
for( final IAEItemStack is : target.getStorageList() )
|
||||
{
|
||||
this.localCache.add( target.extractItems( is, Actionable.SIMULATE, src ) );
|
||||
this.localCache.add( target.extractItems( is.copy().setStackSize( Long.MAX_VALUE ), Actionable.SIMULATE, src ) );
|
||||
}
|
||||
|
||||
this.par = null;
|
||||
|
||||
@@ -21,6 +21,7 @@ package appeng.helpers;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import gregtech.common.items.MetaTool;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -247,7 +248,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
|
||||
|
||||
final TestStatus result = this.getStatus( slotIndex, i );
|
||||
|
||||
switch( result )
|
||||
switch ( result )
|
||||
{
|
||||
case ACCEPT:
|
||||
return true;
|
||||
@@ -266,9 +267,11 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
|
||||
this.testFrame.setInventorySlotContents( slotIndex, i );
|
||||
|
||||
// If we cannot substitute, the items must match exactly
|
||||
if (!canSubstitute && slotIndex < inputs.length) {
|
||||
if (!inputs[slotIndex].isSameType(i)) {
|
||||
this.markItemAs(slotIndex, i, TestStatus.DECLINE);
|
||||
if( ( !( i.getItem().isDamageable() || i.getItem() instanceof MetaTool ) && !canSubstitute ) && slotIndex < inputs.length )
|
||||
{
|
||||
if( !inputs[slotIndex].isSameType( i ) )
|
||||
{
|
||||
this.markItemAs( slotIndex, i, TestStatus.DECLINE );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -326,23 +329,26 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IAEItemStack> getSubstituteInputs(int slot) {
|
||||
if (this.inputs[slot] == null) {
|
||||
public List<IAEItemStack> getSubstituteInputs( int slot )
|
||||
{
|
||||
if( this.inputs[slot] == null )
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return this.substituteInputs.computeIfAbsent(slot, value -> {
|
||||
ItemStack[] matchingStacks = getRecipeIngredient(slot).getMatchingStacks();
|
||||
List<IAEItemStack> itemList = new ArrayList<>(matchingStacks.length + 1);
|
||||
for (ItemStack matchingStack : matchingStacks) {
|
||||
itemList.add(AEItemStack.fromItemStack(matchingStack));
|
||||
return this.substituteInputs.computeIfAbsent( slot, value -> {
|
||||
ItemStack[] matchingStacks = getRecipeIngredient( slot ).getMatchingStacks();
|
||||
List<IAEItemStack> itemList = new ArrayList<>( matchingStacks.length + 1 );
|
||||
for( ItemStack matchingStack : matchingStacks )
|
||||
{
|
||||
itemList.add( AEItemStack.fromItemStack( matchingStack ) );
|
||||
}
|
||||
|
||||
// Ensure that the specific item put in by the user is at the beginning,
|
||||
// so that it takes precedence over substitutions
|
||||
itemList.add(0, this.inputs[slot]);
|
||||
itemList.add( 0, this.inputs[slot] );
|
||||
return itemList;
|
||||
});
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,31 +358,40 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
|
||||
* ingredient list will be condensed to the actual recipe's grid size. In addition, in our 3x3 grid, the user can
|
||||
* shift the actual recipe input to the right and down.
|
||||
*/
|
||||
private Ingredient getRecipeIngredient(int slot) {
|
||||
private Ingredient getRecipeIngredient( int slot )
|
||||
{
|
||||
|
||||
if (standardRecipe instanceof IShapedRecipe ) {
|
||||
if( standardRecipe instanceof IShapedRecipe )
|
||||
{
|
||||
IShapedRecipe shapedRecipe = (IShapedRecipe) standardRecipe;
|
||||
|
||||
return getShapedRecipeIngredient(slot, shapedRecipe.getRecipeWidth());
|
||||
} else {
|
||||
return getShapelessRecipeIngredient(slot);
|
||||
return getShapedRecipeIngredient( slot, shapedRecipe.getRecipeWidth() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return getShapelessRecipeIngredient( slot );
|
||||
}
|
||||
}
|
||||
|
||||
private Ingredient getShapedRecipeIngredient(int slot, int recipeWidth) {
|
||||
private Ingredient getShapedRecipeIngredient( int slot, int recipeWidth )
|
||||
{
|
||||
// Compute the offset of the user's input vs. crafting grid origin
|
||||
// Which is >0 if they have empty rows above or to the left of their input
|
||||
int topOffset = 0;
|
||||
if (inputs[0] == null && inputs[1] == null && inputs[2] == null) {
|
||||
if( inputs[0] == null && inputs[1] == null && inputs[2] == null )
|
||||
{
|
||||
topOffset++; // First row is fully empty
|
||||
if (inputs[3] == null && inputs[4] == null && inputs[5] == null) {
|
||||
if( inputs[3] == null && inputs[4] == null && inputs[5] == null )
|
||||
{
|
||||
topOffset++; // Second row is fully empty
|
||||
}
|
||||
}
|
||||
int leftOffset = 0;
|
||||
if (inputs[0] == null && inputs[3] == null && inputs[6] == null) {
|
||||
if( inputs[0] == null && inputs[3] == null && inputs[6] == null )
|
||||
{
|
||||
leftOffset++; // First column is fully empty
|
||||
if (inputs[1] == null && inputs[4] == null && inputs[7] == null) {
|
||||
if( inputs[1] == null && inputs[4] == null && inputs[7] == null )
|
||||
{
|
||||
leftOffset++; // Second column is fully empty
|
||||
}
|
||||
}
|
||||
@@ -390,32 +405,37 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
|
||||
|
||||
NonNullList<Ingredient> ingredients = standardRecipe.getIngredients();
|
||||
|
||||
if (ingredientIndex < 0 || ingredientIndex > ingredients.size()) {
|
||||
if( ingredientIndex < 0 || ingredientIndex > ingredients.size() )
|
||||
{
|
||||
return Ingredient.EMPTY;
|
||||
}
|
||||
|
||||
return ingredients.get(ingredientIndex);
|
||||
return ingredients.get( ingredientIndex );
|
||||
}
|
||||
|
||||
private Ingredient getShapelessRecipeIngredient(int slot) {
|
||||
private Ingredient getShapelessRecipeIngredient( int slot )
|
||||
{
|
||||
// We map the list of *filled* sparse inputs to the shapeless (ergo unordered)
|
||||
// ingredients. While these do not actually correspond to each other,
|
||||
// since both lists have the same length, the mapping is at least stable.
|
||||
int ingredientIndex = 0;
|
||||
for (int i = 0; i < slot; i++) {
|
||||
if (inputs[i] != null) {
|
||||
for( int i = 0; i < slot; i++ )
|
||||
{
|
||||
if( inputs[i] != null )
|
||||
{
|
||||
ingredientIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
NonNullList<Ingredient> ingredients = standardRecipe.getIngredients();
|
||||
if (ingredientIndex < ingredients.size()) {
|
||||
return ingredients.get(ingredientIndex);
|
||||
if( ingredientIndex < ingredients.size() )
|
||||
{
|
||||
return ingredients.get( ingredientIndex );
|
||||
}
|
||||
|
||||
return Ingredient.EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ItemStack getOutput( final InventoryCrafting craftingInv, final World w )
|
||||
{
|
||||
@@ -517,7 +537,9 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
|
||||
|
||||
private enum TestStatus
|
||||
{
|
||||
ACCEPT, DECLINE, TEST
|
||||
ACCEPT,
|
||||
DECLINE,
|
||||
TEST
|
||||
}
|
||||
|
||||
private static final class TestLookup
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package appeng.parts.misc;
|
||||
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.config.AccessRestriction;
|
||||
import appeng.api.config.Actionable;
|
||||
@@ -147,43 +146,64 @@ class ItemHandlerAdapter implements IMEInventory<IAEItemStack>, IBaseMonitor<IAE
|
||||
}
|
||||
|
||||
ItemStack extracted;
|
||||
int stackSizeCurrentSlot = stackInInventorySlot.getCount();
|
||||
int remainingCurrentSlot = Math.min( remainingSize, stackSizeCurrentSlot );
|
||||
|
||||
// We have to loop here because according to the docs, the handler shouldn't return a stack with size >
|
||||
// maxSize, even if we request more. So even if it returns a valid stack, it might have more stuff.
|
||||
do
|
||||
if( !simulate )
|
||||
{
|
||||
extracted = this.itemHandler.extractItem( i, remainingCurrentSlot, simulate );
|
||||
int stackSizeCurrentSlot = stackInInventorySlot.getCount();
|
||||
int remainingCurrentSlot = Math.min( remainingSize, stackSizeCurrentSlot );
|
||||
|
||||
// We have to loop here because according to the docs, the handler shouldn't return a stack with size >
|
||||
// maxSize, even if we request more. So even if it returns a valid stack, it might have more stuff.
|
||||
|
||||
do
|
||||
{
|
||||
extracted = this.itemHandler.extractItem( i, remainingCurrentSlot, false );
|
||||
|
||||
if( !extracted.isEmpty() )
|
||||
{
|
||||
if( extracted.getCount() > remainingCurrentSlot )
|
||||
{
|
||||
// Something broke. It should never return more than we requested...
|
||||
// We're going to silently eat the remainder
|
||||
AELog.warn( "Mod that provided item handler %s is broken. Returned %s items while only requesting %d.", this.itemHandler.getClass().getName(), extracted.toString(), remainingCurrentSlot );
|
||||
extracted.setCount( remainingCurrentSlot );
|
||||
}
|
||||
|
||||
// We're just gonna use the first stack we get our hands on as the template for the rest.
|
||||
// In case some stupid itemhandler (aka forge) returns an internal state we have to do a second
|
||||
// expensive copy again.
|
||||
if( gathered.isEmpty() )
|
||||
{
|
||||
gathered = extracted;
|
||||
}
|
||||
else
|
||||
{
|
||||
gathered.grow( extracted.getCount() );
|
||||
}
|
||||
remainingCurrentSlot -= extracted.getCount();
|
||||
}
|
||||
} while ( !extracted.isEmpty() && remainingCurrentSlot > 0 );
|
||||
|
||||
remainingSize -= stackSizeCurrentSlot - remainingCurrentSlot;
|
||||
}
|
||||
else
|
||||
{
|
||||
extracted = this.itemHandler.extractItem( i, remainingSize, true );
|
||||
|
||||
if( !extracted.isEmpty() )
|
||||
{
|
||||
if( extracted.getCount() > remainingCurrentSlot )
|
||||
{
|
||||
// Something broke. It should never return more than we requested...
|
||||
// We're going to silently eat the remainder
|
||||
AELog.warn( "Mod that provided item handler %s is broken. Returned %s items while only requesting %d.", this.itemHandler.getClass().getName(), extracted.toString(), remainingCurrentSlot );
|
||||
extracted.setCount( remainingCurrentSlot );
|
||||
}
|
||||
|
||||
// We're just gonna use the first stack we get our hands on as the template for the rest.
|
||||
// In case some stupid itemhandler (aka forge) returns an internal state we have to do a second
|
||||
// expensive copy again.
|
||||
extracted.setCount( Math.min( stackInInventorySlot.getCount(), remainingSize ) );
|
||||
if( gathered.isEmpty() )
|
||||
{
|
||||
gathered = extracted.copy();
|
||||
gathered = extracted;
|
||||
}
|
||||
else
|
||||
{
|
||||
gathered.grow( extracted.getCount() );
|
||||
}
|
||||
remainingCurrentSlot -= extracted.getCount();
|
||||
remainingSize -= extracted.getCount();
|
||||
}
|
||||
} while ( !extracted.isEmpty() && remainingCurrentSlot > 0 );
|
||||
|
||||
remainingSize -= stackSizeCurrentSlot - remainingCurrentSlot;
|
||||
|
||||
// Done?
|
||||
}
|
||||
if( remainingSize <= 0 )
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,7 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import gregtech.common.items.MetaTool;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -370,20 +371,44 @@ public final class AEItemStack extends AEStack<IAEItemStack> implements IAEItemS
|
||||
|
||||
private boolean fuzzyItemStackComparison( ItemStack a, ItemStack b, FuzzyMode mode )
|
||||
{
|
||||
if( a.getItem() == b.getItem() && a.getItem().isDamageable() )
|
||||
if( a.getItem() == b.getItem() && ( a.getItem().isDamageable() || a.getItem() instanceof MetaTool ) )
|
||||
{
|
||||
if( mode == FuzzyMode.IGNORE_ALL )
|
||||
{
|
||||
return true;
|
||||
if( a.getItem().isDamageable() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if( a.getItem() instanceof MetaTool )
|
||||
{
|
||||
return a.getItemDamage() == b.getItemDamage();
|
||||
}
|
||||
}
|
||||
else if( mode == FuzzyMode.PERCENT_99 )
|
||||
{
|
||||
return a.getItemDamage() > 1 == b.getItemDamage() > 1;
|
||||
if( a.getItem().isDamageable() )
|
||||
{
|
||||
return a.getItemDamage() > 1 == b.getItemDamage() > 1;
|
||||
}
|
||||
else if( a.getItem() instanceof MetaTool )
|
||||
{
|
||||
return ( (MetaTool) a.getItem() ).getItemDamage( a ) == ( (MetaTool) b.getItem() ).getItemDamage( b );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final float percentDamageOfA = (float) a.getItemDamage() / a.getMaxDamage();
|
||||
final float percentDamageOfB = (float) b.getItemDamage() / b.getMaxDamage();
|
||||
float percentDamageOfA = 0;
|
||||
float percentDamageOfB = 0;
|
||||
if( a.getItem().isDamageable() )
|
||||
{
|
||||
percentDamageOfA = (float) a.getItemDamage() / a.getMaxDamage();
|
||||
percentDamageOfB = (float) b.getItemDamage() / b.getMaxDamage();
|
||||
}
|
||||
else if( a.getItem() instanceof MetaTool )
|
||||
{
|
||||
percentDamageOfA = (float) ( (MetaTool) a.getItem() ).getItemDamage( a ) / ( (MetaTool) a.getItem() ).getMaxItemDamage( a );
|
||||
percentDamageOfB = (float) ( (MetaTool) b.getItem() ).getItemDamage( b ) / ( (MetaTool) b.getItem() ).getMaxItemDamage( b );
|
||||
}
|
||||
|
||||
return percentDamageOfA > mode.breakPoint == percentDamageOfB > mode.breakPoint;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ import java.util.Map;
|
||||
import appeng.util.Platform;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import gregtech.api.items.toolitem.ToolMetaItem;
|
||||
import gregtech.common.items.MetaTool;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectCollection;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap;
|
||||
@@ -34,159 +36,187 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
|
||||
import appeng.api.config.FuzzyMode;
|
||||
import appeng.api.storage.data.IAEItemStack;
|
||||
|
||||
|
||||
/**
|
||||
* This variant list is optimized for damageable items, and supports selecting durability ranges with
|
||||
* {@link #findFuzzy(IAEItemStack, FuzzyMode)}.
|
||||
*/
|
||||
class FuzzyItemVariantList extends ItemVariantList {
|
||||
class FuzzyItemVariantList extends ItemVariantList
|
||||
{
|
||||
|
||||
static final SharedStackComparator COMPARATOR = new SharedStackComparator();
|
||||
static final SharedStackComparator COMPARATOR = new SharedStackComparator();
|
||||
|
||||
// NOTE: We only use Object as they key here so we can pass our special DamageBounds to the subMap method.
|
||||
// We NEVER put any keys in this map that are not AESharedItemStacks.
|
||||
private final Object2ObjectSortedMap<Object, IAEItemStack> records = new Object2ObjectAVLTreeMap<>(COMPARATOR);
|
||||
// NOTE: We only use Object as they key here so we can pass our special DamageBounds to the subMap method.
|
||||
// We NEVER put any keys in this map that are not AESharedItemStacks.
|
||||
private final Object2ObjectSortedMap<Object, IAEItemStack> records = new Object2ObjectAVLTreeMap<>( COMPARATOR );
|
||||
|
||||
@Override
|
||||
public Collection<IAEItemStack> findFuzzy(final IAEItemStack filter, final FuzzyMode fuzzy) {
|
||||
ItemStack itemStack = filter.getDefinition();
|
||||
@Override
|
||||
public Collection<IAEItemStack> findFuzzy( final IAEItemStack filter, final FuzzyMode fuzzy )
|
||||
{
|
||||
ItemStack itemStack = filter.getDefinition();
|
||||
|
||||
ItemDamageBound lowerBound = makeLowerBound(itemStack, fuzzy);
|
||||
ItemDamageBound upperBound = makeUpperBound(itemStack, fuzzy);
|
||||
Preconditions.checkState(lowerBound.itemDamage > upperBound.itemDamage);
|
||||
ItemDamageBound lowerBound = makeLowerBound( itemStack, fuzzy );
|
||||
ItemDamageBound upperBound = makeUpperBound( itemStack, fuzzy );
|
||||
Preconditions.checkState( lowerBound.itemDamage > upperBound.itemDamage );
|
||||
|
||||
return this.records.subMap(lowerBound, upperBound).values();
|
||||
}
|
||||
return this.records.subMap( lowerBound, upperBound ).values();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
Map<AESharedItemStack, IAEItemStack> getRecords() {
|
||||
// We ensure on our end that we NEVER use anything but AESharedItemStack as the key in this map
|
||||
return (Map<AESharedItemStack, IAEItemStack>) (Object) this.records;
|
||||
}
|
||||
@SuppressWarnings( "unchecked" )
|
||||
@Override
|
||||
Map<AESharedItemStack, IAEItemStack> getRecords()
|
||||
{
|
||||
// We ensure on our end that we NEVER use anything but AESharedItemStack as the key in this map
|
||||
return (Map<AESharedItemStack, IAEItemStack>) (Object) this.records;
|
||||
}
|
||||
|
||||
static class ItemDamageBound {
|
||||
final int itemDamage;
|
||||
static class ItemDamageBound
|
||||
{
|
||||
final int itemDamage;
|
||||
|
||||
public ItemDamageBound(int itemDamage) {
|
||||
this.itemDamage = itemDamage;
|
||||
}
|
||||
}
|
||||
public ItemDamageBound( int itemDamage )
|
||||
{
|
||||
this.itemDamage = itemDamage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This comparator creates a strict and total ordering over all {@link AESharedItemStack} of the same item. To
|
||||
* support selecting ranges of durability, it is defined for type {@link Object} and also accepts
|
||||
* {@link ItemDamageBound} as an argument to compare against.
|
||||
*/
|
||||
static class SharedStackComparator implements Comparator<Object> {
|
||||
@Override
|
||||
public int compare(Object a, Object b) {
|
||||
// Either argument can either be a damage bound or a shared item stack
|
||||
// Since we never put damage bounds into the map as keys, only one
|
||||
// of the two arguments can possibly be a bound
|
||||
ItemDamageBound boundA = null;
|
||||
AESharedItemStack stackA = null;
|
||||
int itemDamageA;
|
||||
if (a instanceof ItemDamageBound) {
|
||||
boundA = (ItemDamageBound) a;
|
||||
itemDamageA = boundA.itemDamage;
|
||||
} else {
|
||||
stackA = (AESharedItemStack) a;
|
||||
itemDamageA = stackA.getItemDamage();
|
||||
}
|
||||
ItemDamageBound boundB = null;
|
||||
AESharedItemStack stackB = null;
|
||||
int itemDamageB;
|
||||
if (b instanceof ItemDamageBound) {
|
||||
boundB = (ItemDamageBound) b;
|
||||
itemDamageB = boundB.itemDamage;
|
||||
} else {
|
||||
stackB = (AESharedItemStack) b;
|
||||
itemDamageB = stackB.getItemDamage();
|
||||
}
|
||||
/**
|
||||
* This comparator creates a strict and total ordering over all {@link AESharedItemStack} of the same item. To
|
||||
* support selecting ranges of durability, it is defined for type {@link Object} and also accepts
|
||||
* {@link ItemDamageBound} as an argument to compare against.
|
||||
*/
|
||||
static class SharedStackComparator implements Comparator<Object>
|
||||
{
|
||||
@Override
|
||||
public int compare( Object a, Object b )
|
||||
{
|
||||
// Either argument can either be a damage bound or a shared item stack
|
||||
// Since we never put damage bounds into the map as keys, only one
|
||||
// of the two arguments can possibly be a bound
|
||||
ItemDamageBound boundA = null;
|
||||
AESharedItemStack stackA = null;
|
||||
int itemDamageA;
|
||||
if( a instanceof ItemDamageBound )
|
||||
{
|
||||
boundA = (ItemDamageBound) a;
|
||||
itemDamageA = boundA.itemDamage;
|
||||
}
|
||||
else
|
||||
{
|
||||
stackA = (AESharedItemStack) a;
|
||||
itemDamageA = stackA.getItemDamage();
|
||||
}
|
||||
ItemDamageBound boundB = null;
|
||||
AESharedItemStack stackB = null;
|
||||
int itemDamageB;
|
||||
if( b instanceof ItemDamageBound )
|
||||
{
|
||||
boundB = (ItemDamageBound) b;
|
||||
itemDamageB = boundB.itemDamage;
|
||||
}
|
||||
else
|
||||
{
|
||||
stackB = (AESharedItemStack) b;
|
||||
itemDamageB = stackB.getItemDamage();
|
||||
}
|
||||
|
||||
// When either argument is a damage bound, we just compare the damage values because it is used
|
||||
// only to get a certain damage range out of the map.
|
||||
if (boundA != null || boundB != null) {
|
||||
return Integer.compare(itemDamageB, itemDamageA);
|
||||
}
|
||||
// When either argument is a damage bound, we just compare the damage values because it is used
|
||||
// only to get a certain damage range out of the map.
|
||||
if( boundA != null || boundB != null )
|
||||
{
|
||||
return Integer.compare( itemDamageB, itemDamageA );
|
||||
}
|
||||
|
||||
ItemStack itemStackA = stackA.getDefinition();
|
||||
ItemStack itemStackB = stackB.getDefinition();
|
||||
Preconditions.checkState(itemStackA.getCount() == 1, "ItemStack#getCount() has to be 1");
|
||||
Preconditions.checkArgument(itemStackB.getCount() == 1, "ItemStack#getCount() has to be 1");
|
||||
ItemStack itemStackA = stackA.getDefinition();
|
||||
ItemStack itemStackB = stackB.getDefinition();
|
||||
Preconditions.checkState( itemStackA.getCount() == 1, "ItemStack#getCount() has to be 1" );
|
||||
Preconditions.checkArgument( itemStackB.getCount() == 1, "ItemStack#getCount() has to be 1" );
|
||||
|
||||
if (itemStackA == itemStackB) {
|
||||
return 0;
|
||||
}
|
||||
if( itemStackA == itemStackB )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Damaged items are sorted before undamaged items
|
||||
final int damageValue = Integer.compare(itemDamageB, itemDamageA);
|
||||
if (damageValue != 0) {
|
||||
return damageValue;
|
||||
}
|
||||
// Damaged items are sorted before undamaged items
|
||||
final int damageValue = Integer.compare( itemDamageB, itemDamageA );
|
||||
if( damageValue != 0 )
|
||||
{
|
||||
return damageValue;
|
||||
}
|
||||
|
||||
// As a final tie breaker, order by the object identity of the item stack
|
||||
// While this will order seemingly at random, we only need the order of
|
||||
// damage values to be predictable, while still having to satisfy the
|
||||
// complete order requirements of the sorted map
|
||||
return Long.compare(System.identityHashCode(itemStackA), System.identityHashCode(itemStackB));
|
||||
}
|
||||
}
|
||||
// As a final tie breaker, order by the object identity of the item stack
|
||||
// While this will order seemingly at random, we only need the order of
|
||||
// damage values to be predictable, while still having to satisfy the
|
||||
// complete order requirements of the sorted map
|
||||
return Long.compare( System.identityHashCode( itemStackA ), System.identityHashCode( itemStackB ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Minecraft reverses the damage values. So anything with a damage of 0 is undamaged and increases the more damaged
|
||||
* the item is.
|
||||
* <p>
|
||||
* Further the used subMap follows [MAX_DAMAGE, MIN_DAMAGE), so to include undamaged items, we have to start with a
|
||||
* lower damage value than 0, while it is fine to use {@link ItemStack#getMaxDamage()} for the upper bound.
|
||||
*/
|
||||
private static final int MIN_DAMAGE_VALUE = -1;
|
||||
/**
|
||||
* Minecraft reverses the damage values. So anything with a damage of 0 is undamaged and increases the more damaged
|
||||
* the item is.
|
||||
* <p>
|
||||
* Further the used subMap follows [MAX_DAMAGE, MIN_DAMAGE), so to include undamaged items, we have to start with a
|
||||
* lower damage value than 0, while it is fine to use {@link ItemStack#getMaxDamage()} for the upper bound.
|
||||
*/
|
||||
private static final int MIN_DAMAGE_VALUE = -1;
|
||||
|
||||
/*
|
||||
* Keep in mind that the stack order is from most damaged to least damaged, so this lower bound will actually be a
|
||||
* higher number than the upper bound.
|
||||
*/
|
||||
static ItemDamageBound makeLowerBound(final ItemStack stack, final FuzzyMode fuzzy)
|
||||
{
|
||||
Preconditions.checkState( stack.getItem().isDamageable() , "Item#isDamageable() has to be true" );
|
||||
/*
|
||||
* Keep in mind that the stack order is from most damaged to least damaged, so this lower bound will actually be a
|
||||
* higher number than the upper bound.
|
||||
*/
|
||||
static ItemDamageBound makeLowerBound( final ItemStack stack, final FuzzyMode fuzzy )
|
||||
{
|
||||
Preconditions.checkState( stack.getItem().isDamageable() || stack.getItem() instanceof MetaTool, "Item#isDamageable() has to be true" );
|
||||
|
||||
int damage;
|
||||
if( fuzzy == FuzzyMode.IGNORE_ALL )
|
||||
{
|
||||
if( stack.getMaxDamage() == 0 )
|
||||
{
|
||||
damage = stack.getItemDamage();
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = stack.getMaxDamage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final int breakpoint = fuzzy.calculateBreakPoint( stack.getMaxDamage() );
|
||||
damage = stack.getItemDamage() <= breakpoint ? breakpoint : stack.getMaxDamage();
|
||||
}
|
||||
int damage;
|
||||
if( fuzzy == FuzzyMode.IGNORE_ALL )
|
||||
{
|
||||
int maxDamage;
|
||||
if( stack.getItem() instanceof MetaTool )
|
||||
{
|
||||
maxDamage = ( (MetaTool) stack.getItem() ).getMaxItemDamage( stack );
|
||||
damage = ( (MetaTool) stack.getItem() ).getItemDamage( stack );
|
||||
}
|
||||
else
|
||||
{
|
||||
maxDamage = stack.getMaxDamage();
|
||||
damage = stack.getItemDamage();
|
||||
}
|
||||
if( maxDamage != 0 )
|
||||
{
|
||||
damage = maxDamage;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final int breakpoint = fuzzy.calculateBreakPoint( stack.getItem().isDamageable() ? stack.getMaxDamage() : ( (MetaTool) stack.getItem() ).getMaxItemDamage( stack ) );
|
||||
damage = stack.getItem().isDamageable() ? stack.getItemDamage() : ( (MetaTool) stack.getItem() ).getItemDamage( stack ) <= breakpoint ? breakpoint : stack.getItem().isDamageable() ? stack.getMaxDamage() : ( (MetaTool) stack.getItem() ).getMaxItemDamage( stack );
|
||||
}
|
||||
|
||||
return new ItemDamageBound( damage );
|
||||
}
|
||||
return new ItemDamageBound( damage );
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep in mind that the stack order is from most damaged to least damaged, so this upper bound will actually be a
|
||||
* lower number than the lower bound. It also is exclusive.
|
||||
*/
|
||||
static ItemDamageBound makeUpperBound(final ItemStack stack, final FuzzyMode fuzzy) {
|
||||
Preconditions.checkState(stack.getItem().isDamageable() , "Item#isDamageable() has to be true");
|
||||
/*
|
||||
* Keep in mind that the stack order is from most damaged to least damaged, so this upper bound will actually be a
|
||||
* lower number than the lower bound. It also is exclusive.
|
||||
*/
|
||||
static ItemDamageBound makeUpperBound( final ItemStack stack, final FuzzyMode fuzzy )
|
||||
{
|
||||
Preconditions.checkState( stack.getItem().isDamageable() || stack.getItem() instanceof MetaTool, "Item#isDamageable() has to be true" );
|
||||
|
||||
int damage;
|
||||
if (fuzzy == FuzzyMode.IGNORE_ALL) {
|
||||
damage = MIN_DAMAGE_VALUE;
|
||||
} else {
|
||||
final int breakpoint = fuzzy.calculateBreakPoint(stack.getMaxDamage());
|
||||
damage = stack.getItemDamage() <= breakpoint ? MIN_DAMAGE_VALUE : breakpoint;
|
||||
}
|
||||
int damage;
|
||||
if( fuzzy == FuzzyMode.IGNORE_ALL )
|
||||
{
|
||||
damage = MIN_DAMAGE_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
final int breakpoint = fuzzy.calculateBreakPoint( stack.getItem().isDamageable() ? stack.getMaxDamage() : ( (MetaTool) stack.getItem() ).getMaxItemDamage( stack ) );
|
||||
damage = stack.getItem().isDamageable() ? stack.getItemDamage() : ( (MetaTool) stack.getItem() ).getItemDamage( stack ) <= breakpoint ? MIN_DAMAGE_VALUE : breakpoint;
|
||||
}
|
||||
|
||||
return new ItemDamageBound(damage);
|
||||
}
|
||||
return new ItemDamageBound( damage );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,180 +36,217 @@ import appeng.api.config.FuzzyMode;
|
||||
import appeng.api.storage.data.IAEItemStack;
|
||||
import appeng.api.storage.data.IItemList;
|
||||
|
||||
public final class ItemList implements IItemList<IAEItemStack> {
|
||||
|
||||
private final Reference2ObjectMap<Item, ItemVariantList> records = new Reference2ObjectOpenHashMap<>();
|
||||
/**
|
||||
* We increment this version field everytime an attempt to mutate this item list (or potentially one of its
|
||||
* sub-lists) is made. Iterators will copy the version when they are created and compare it against the current
|
||||
* version whenever they advance to trigger a {@link ConcurrentModificationException}.
|
||||
*/
|
||||
private final AtomicInteger version = new AtomicInteger(0);
|
||||
public final class ItemList implements IItemList<IAEItemStack>
|
||||
{
|
||||
|
||||
@Override
|
||||
public IAEItemStack findPrecise(final IAEItemStack itemStack) {
|
||||
if (itemStack == null) {
|
||||
return null;
|
||||
}
|
||||
private final Reference2ObjectMap<Item, ItemVariantList> records = new Reference2ObjectOpenHashMap<>();
|
||||
/**
|
||||
* We increment this version field everytime an attempt to mutate this item list (or potentially one of its
|
||||
* sub-lists) is made. Iterators will copy the version when they are created and compare it against the current
|
||||
* version whenever they advance to trigger a {@link ConcurrentModificationException}.
|
||||
*/
|
||||
private final AtomicInteger version = new AtomicInteger( 0 );
|
||||
|
||||
ItemVariantList record = this.records.get(itemStack.getItem());
|
||||
return record != null ? record.findPrecise(itemStack) : null;
|
||||
}
|
||||
@Override
|
||||
public IAEItemStack findPrecise( final IAEItemStack itemStack )
|
||||
{
|
||||
if( itemStack == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IAEItemStack> findFuzzy(final IAEItemStack filter, final FuzzyMode fuzzy) {
|
||||
if (filter == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ItemVariantList record = this.records.get( itemStack.getItem() );
|
||||
return record != null ? record.findPrecise( itemStack ) : null;
|
||||
}
|
||||
|
||||
ItemVariantList record = this.records.get(filter.getItem());
|
||||
return record != null ? record.findFuzzy(filter, fuzzy) : Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public Collection<IAEItemStack> findFuzzy( final IAEItemStack filter, final FuzzyMode fuzzy )
|
||||
{
|
||||
if( filter == null )
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !this.iterator().hasNext();
|
||||
}
|
||||
ItemVariantList record = this.records.get( filter.getItem() );
|
||||
return record != null ? record.findFuzzy( filter, fuzzy ) : Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final IAEItemStack itemStack) {
|
||||
version.incrementAndGet();
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return !this.iterator().hasNext();
|
||||
}
|
||||
|
||||
if (itemStack == null) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void add( final IAEItemStack itemStack )
|
||||
{
|
||||
version.incrementAndGet();
|
||||
|
||||
this.getOrCreateRecord(itemStack.getItem()).add(itemStack);
|
||||
}
|
||||
if( itemStack == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStorage(final IAEItemStack itemStack) {
|
||||
version.incrementAndGet();
|
||||
this.getOrCreateRecord( itemStack.getItem() ).add( itemStack );
|
||||
}
|
||||
|
||||
if (itemStack == null) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void addStorage( final IAEItemStack itemStack )
|
||||
{
|
||||
version.incrementAndGet();
|
||||
|
||||
this.getOrCreateRecord(itemStack.getItem()).addStorage(itemStack);
|
||||
}
|
||||
if( itemStack == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCrafting(final IAEItemStack itemStack) {
|
||||
version.incrementAndGet();
|
||||
this.getOrCreateRecord( itemStack.getItem() ).addStorage( itemStack );
|
||||
}
|
||||
|
||||
if (itemStack == null) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void addCrafting( final IAEItemStack itemStack )
|
||||
{
|
||||
version.incrementAndGet();
|
||||
|
||||
this.getOrCreateRecord(itemStack.getItem()).addCrafting(itemStack);
|
||||
}
|
||||
if( itemStack == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequestable(final IAEItemStack itemStack) {
|
||||
version.incrementAndGet();
|
||||
this.getOrCreateRecord( itemStack.getItem() ).addCrafting( itemStack );
|
||||
}
|
||||
|
||||
if (itemStack == null) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void addRequestable( final IAEItemStack itemStack )
|
||||
{
|
||||
version.incrementAndGet();
|
||||
|
||||
this.getOrCreateRecord(itemStack.getItem()).addRequestable(itemStack);
|
||||
}
|
||||
if( itemStack == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack getFirstItem() {
|
||||
for (final IAEItemStack stackType : this) {
|
||||
return stackType;
|
||||
}
|
||||
this.getOrCreateRecord( itemStack.getItem() ).addRequestable( itemStack );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public IAEItemStack getFirstItem()
|
||||
{
|
||||
for( final IAEItemStack stackType : this )
|
||||
{
|
||||
return stackType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (ItemVariantList entry : records.values()) {
|
||||
size += entry.size();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
int size = 0;
|
||||
for( ItemVariantList entry : records.values() )
|
||||
{
|
||||
size += entry.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<IAEItemStack> iterator() {
|
||||
return new ChainedIterator(this.records.values().iterator(), version);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetStatus() {
|
||||
for (final IAEItemStack i : this) {
|
||||
i.reset();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Iterator<IAEItemStack> iterator()
|
||||
{
|
||||
return new ChainedIterator( this.records.values().iterator(), version );
|
||||
}
|
||||
|
||||
private ItemVariantList getOrCreateRecord(Item item) {
|
||||
return this.records.computeIfAbsent(item, this::makeRecordMap);
|
||||
}
|
||||
@Override
|
||||
public void resetStatus()
|
||||
{
|
||||
for( final IAEItemStack i : this )
|
||||
{
|
||||
i.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private ItemVariantList makeRecordMap(Item item) {
|
||||
if (item.isDamageable() ) {
|
||||
return new FuzzyItemVariantList();
|
||||
} else {
|
||||
return new NormalItemVariantList();
|
||||
}
|
||||
}
|
||||
private ItemVariantList getOrCreateRecord( Item item )
|
||||
{
|
||||
return this.records.computeIfAbsent( item, this::makeRecordMap );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over multiple item lists as if they were one list.
|
||||
*/
|
||||
private static class ChainedIterator implements Iterator<IAEItemStack> {
|
||||
private ItemVariantList makeRecordMap( Item item )
|
||||
{
|
||||
if( item.isDamageable() || item instanceof MetaTool )
|
||||
{
|
||||
return new FuzzyItemVariantList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new NormalItemVariantList();
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicInteger parentVersion;
|
||||
private final int version;
|
||||
private final Iterator<ItemVariantList> parent;
|
||||
private Iterator<IAEItemStack> next;
|
||||
/**
|
||||
* Iterates over multiple item lists as if they were one list.
|
||||
*/
|
||||
private static class ChainedIterator implements Iterator<IAEItemStack>
|
||||
{
|
||||
|
||||
public ChainedIterator(Iterator<ItemVariantList> iterator, AtomicInteger parentVersion) {
|
||||
this.parent = iterator;
|
||||
this.parentVersion = parentVersion;
|
||||
this.version = parentVersion.get();
|
||||
this.ensureItems();
|
||||
}
|
||||
private final AtomicInteger parentVersion;
|
||||
private final int version;
|
||||
private final Iterator<ItemVariantList> parent;
|
||||
private Iterator<IAEItemStack> next;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null && next.hasNext();
|
||||
}
|
||||
public ChainedIterator( Iterator<ItemVariantList> iterator, AtomicInteger parentVersion )
|
||||
{
|
||||
this.parent = iterator;
|
||||
this.parentVersion = parentVersion;
|
||||
this.version = parentVersion.get();
|
||||
this.ensureItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack next() {
|
||||
if (this.next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if (this.version != this.parentVersion.get()) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return next != null && next.hasNext();
|
||||
}
|
||||
|
||||
IAEItemStack result = this.next.next();
|
||||
this.ensureItems();
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public IAEItemStack next()
|
||||
{
|
||||
if( this.next == null )
|
||||
{
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if( this.version != this.parentVersion.get() )
|
||||
{
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
private void ensureItems() {
|
||||
if (hasNext()) {
|
||||
return; // Still items left in the current one
|
||||
}
|
||||
IAEItemStack result = this.next.next();
|
||||
this.ensureItems();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Find the next iterator willing to return some items...
|
||||
while (this.parent.hasNext()) {
|
||||
this.next = this.parent.next().iterator();
|
||||
private void ensureItems()
|
||||
{
|
||||
if( hasNext() )
|
||||
{
|
||||
return; // Still items left in the current one
|
||||
}
|
||||
|
||||
if (this.next.hasNext()) {
|
||||
return; // Found one!
|
||||
}
|
||||
}
|
||||
// Find the next iterator willing to return some items...
|
||||
while ( this.parent.hasNext() )
|
||||
{
|
||||
this.next = this.parent.next().iterator();
|
||||
|
||||
// No more items
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
if( this.next.hasNext() )
|
||||
{
|
||||
return; // Found one!
|
||||
}
|
||||
}
|
||||
|
||||
// No more items
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user