heavily tweaked auto-crafting

This commit is contained in:
PrototypeTrousers
2022-03-09 23:08:47 -03:00
parent 146d3f5628
commit 036f4e1c5b
11 changed files with 571 additions and 387 deletions
+2 -1
View File
@@ -4,7 +4,7 @@ aebuild=7
aegroup=appeng aegroup=appeng
aebasename=appliedenergistics2 aebasename=appliedenergistics2
extended=extended_life extended=extended_life
extendedversion=v50l extendedversion=v51
######################################################### #########################################################
# Versions # # Versions #
######################################################### #########################################################
@@ -26,6 +26,7 @@ cofhcore_version=1.12.2-4.5.2.19
crafttweaker_version=4.1.8.9 crafttweaker_version=4.1.8.9
inventorytweaks_version=1.63 inventorytweaks_version=1.63
ctm_version=MC1.12.2-0.3.1.16 ctm_version=MC1.12.2-0.3.1.16
forestry_version=5.8.2.387
######################################################### #########################################################
# Deployment # # Deployment #
######################################################### #########################################################
+2
View File
@@ -97,6 +97,8 @@ dependencies {
compileOnly "inventory-tweaks:InventoryTweaks:${inventorytweaks_version}:api" compileOnly "inventory-tweaks:InventoryTweaks:${inventorytweaks_version}:api"
compileOnly "team.chisel.ctm:CTM:${ctm_version}" compileOnly "team.chisel.ctm:CTM:${ctm_version}"
compileOnly "de.ellpeck.actuallyadditions:ActuallyAdditions:1.12.2-r152.16:api" compileOnly "de.ellpeck.actuallyadditions:ActuallyAdditions:1.12.2-r152.16:api"
deobfCompile "net.sengir.forestry:forestry_${minecraft_version}:$forestry_version"
// at runtime, use the full JEI jar // at runtime, use the full JEI jar
runtime "mezz.jei:jei_${minecraft_version}:${jei_version}" runtime "mezz.jei:jei_${minecraft_version}:${jei_version}"
+16 -11
View File
@@ -65,6 +65,7 @@ public class CraftingJob implements Runnable, ICraftingJob
private final Object monitor = new Object(); private final Object monitor = new Object();
private final Stopwatch tickSpreadingWatch = Stopwatch.createUnstarted(); private final Stopwatch tickSpreadingWatch = Stopwatch.createUnstarted();
private final Stopwatch craftingTreeWatch = Stopwatch.createUnstarted(); private final Stopwatch craftingTreeWatch = Stopwatch.createUnstarted();
private final ICraftingGrid cc;
private CraftingTreeNode tree; private CraftingTreeNode tree;
private final IAEItemStack output; private final IAEItemStack output;
private boolean simulate = false; private boolean simulate = false;
@@ -90,7 +91,7 @@ public class CraftingJob implements Runnable, ICraftingJob
this.callback = callback; this.callback = callback;
final ICraftingGrid cc = grid.getCache( ICraftingGrid.class ); this.cc = grid.getCache( ICraftingGrid.class );
final GridStorageCache sg = grid.getCache( IStorageGrid.class ); final GridStorageCache sg = grid.getCache( IStorageGrid.class );
this.original = new MECraftingInventory( sg.getExtractableList( actionSrc ) ); this.original = new MECraftingInventory( sg.getExtractableList( actionSrc ) );
@@ -159,13 +160,13 @@ public class CraftingJob implements Runnable, ICraftingJob
TickHandler.INSTANCE.registerCraftingSimulation( this.world, this ); TickHandler.INSTANCE.registerCraftingSimulation( this.world, this );
this.handlePausing(); this.handlePausing();
craftingTreeWatch.start();
final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true ); final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true );
craftingInventory.ignore( this.output ); craftingInventory.ignore( this.output );
this.availableCheck = new MECraftingInventory( this.original, false, false, false ); this.availableCheck = new MECraftingInventory( this.original, false, false, false );
craftingTreeWatch.start();
this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc ); this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc );
craftingTreeWatch.stop();
this.getTree().dive( this ); this.getTree().dive( this );
for( final String s : this.opsAndMultiplier.keySet() ) 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 ) ); AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) );
} }
craftingTreeWatch.stop(); if( actionSrc.player().isPresent() )
this.logCraftingJob( "real, success", craftingTreeWatch ); {
this.logCraftingJob( "simulated, success", craftingTreeWatch );
}
else
{
this.logCraftingJob( "real, success", craftingTreeWatch );
}
} }
catch( final CraftBranchFailure e ) catch( final CraftBranchFailure e )
{ {
@@ -185,14 +192,14 @@ public class CraftingJob implements Runnable, ICraftingJob
{ {
if( actionSrc.player().isPresent() ) if( actionSrc.player().isPresent() )
{ {
craftingTreeWatch.reset().start();
final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true ); final MECraftingInventory craftingInventory = new MECraftingInventory( this.original, true, false, true );
craftingInventory.ignore( this.output ); craftingInventory.ignore( this.output );
this.availableCheck = new MECraftingInventory( this.original, false, false, false );
this.getTree().setSimulate(); this.getTree().setSimulate();
this.availableCheck = new MECraftingInventory( this.original, false, false, false );
craftingTreeWatch.reset().start();
this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc ); this.getTree().request( craftingInventory, this.output.getStackSize(), this.actionSrc );
craftingTreeWatch.stop();
this.getTree().dive( this ); this.getTree().dive( this );
for( final String s : this.opsAndMultiplier.keySet() ) 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 ) ); AELog.crafting( s + " * " + ti.times + " = " + ( ti.perOp * ti.times ) );
} }
craftingTreeWatch.stop(); this.logCraftingJob( "simulated, failed", craftingTreeWatch );
this.logCraftingJob( "simulate", craftingTreeWatch );
} }
else else
{ {
craftingTreeWatch.stop();
this.logCraftingJob( "real, failed", craftingTreeWatch ); this.logCraftingJob( "real, failed", craftingTreeWatch );
} }
} }
@@ -24,7 +24,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import appeng.api.config.FuzzyMode; import appeng.api.config.FuzzyMode;
import com.google.common.collect.Lists;
import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.World; import net.minecraft.world.World;
@@ -53,14 +52,14 @@ public class CraftingTreeNode
private final IAEItemStack what; private final IAEItemStack what;
// what are the crafting patterns for this? // what are the crafting patterns for this?
private final ArrayList<CraftingTreeProcess> nodes = new ArrayList<>(); private final ArrayList<CraftingTreeProcess> nodes = new ArrayList<>();
private final ICraftingGrid cc;
private final int depth;
private int bytes = 0; private int bytes = 0;
private boolean canEmit = false; private boolean canEmit = false;
private long missing = 0; private long missing = 0;
private long howManyEmitted = 0; private long howManyEmitted = 0;
private boolean exhausted = false; 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 ) public CraftingTreeNode( final ICraftingGrid cc, final CraftingJob job, final IAEItemStack wat, final CraftingTreeProcess par, final int slot, final int depth )
{ {
this.what = wat; this.what = wat;
@@ -68,9 +67,18 @@ public class CraftingTreeNode
this.slot = slot; this.slot = slot;
this.world = job.getWorld(); this.world = job.getWorld();
this.job = job; this.job = job;
this.sim = false; this.cc = cc;
this.depth = depth;
this.canEmit = cc.canEmitFor( this.what ); this.canEmit = cc.canEmitFor( this.what );
}
public void addNode()
{
if( !nodes.isEmpty() )
{
return;
}
if( this.canEmit ) if( this.canEmit )
{ {
@@ -89,19 +97,42 @@ public class CraftingTreeNode
IAEItemStack request( final MECraftingInventory inv, long l, final IActionSource src ) throws CraftBranchFailure, InterruptedException IAEItemStack request( final MECraftingInventory inv, long l, final IActionSource src ) throws CraftBranchFailure, InterruptedException
{ {
addNode();
this.job.handlePausing(); 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 IItemList<IAEItemStack> inventoryList = inv.getItemList();
final List<IAEItemStack> thingsUsed = new ArrayList<>(); final List<IAEItemStack> thingsUsed = new ArrayList<>();
this.what.setStackSize( l ); this.what.setStackSize( l );
if( this.getSlot() >= 0 && this.parent != null && this.parent.details.isCraftable() ) if( this.getSlot() >= 0 && this.parent != null && this.parent.details.isCraftable() )
{ {
Collection<IAEItemStack> itemList = new ArrayList<>(); 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 else
{ {
@@ -110,6 +141,10 @@ public class CraftingTreeNode
{ {
itemList.add( item ); itemList.add( item );
} }
if( this.parent.details.canSubstitute() )
{
itemList.addAll( inventoryList.findFuzzy( this.what, FuzzyMode.IGNORE_ALL ) );
}
} }
for( IAEItemStack fuzz : itemList ) 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; this.exhausted = true;
if( this.nodes.size() == 1 ) if( this.nodes.size() == 1 )
@@ -192,11 +216,9 @@ public class CraftingTreeNode
while ( pro.possible && l > 0 ) while ( pro.possible && l > 0 )
{ {
final IAEItemStack madeWhat = pro.getAmountCrafted( this.what ); final IAEItemStack madeWhat = pro.getAmountCrafted( this.what );
pro.request( inv, pro.getTimes( l, madeWhat.getStackSize() ), src ); pro.request( inv, pro.getTimes( l, madeWhat.getStackSize() ), src );
madeWhat.setStackSize( l ); madeWhat.setStackSize( l );
final IAEItemStack available = inv.extractItems( madeWhat, Actionable.MODULATE, src ); final IAEItemStack available = inv.extractItems( madeWhat, Actionable.MODULATE, src );
if( available != null ) if( available != null )
@@ -257,10 +279,10 @@ public class CraftingTreeNode
} }
} }
if( this.sim ) if( job.isSimulation() )
{ {
this.missing += l;
this.bytes += l; this.bytes += l;
this.missing += l;
final IAEItemStack rv = this.what.copy(); final IAEItemStack rv = this.what.copy();
rv.setStackSize( l ); rv.setStackSize( l );
return rv; return rv;
@@ -306,7 +328,6 @@ public class CraftingTreeNode
void setSimulate() void setSimulate()
{ {
this.sim = true;
this.missing = 0; this.missing = 0;
this.bytes = 0; this.bytes = 0;
this.used.resetStatus(); this.used.resetStatus();
@@ -20,15 +20,13 @@ package appeng.crafting;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import appeng.api.config.FuzzyMode; import appeng.api.config.FuzzyMode;
import appeng.me.cache.CraftingGridCache;
import appeng.util.item.AEItemStack; import appeng.util.item.AEItemStack;
import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.objects.Object2LongArrayMap; import it.unimi.dsi.fastutil.objects.Object2LongArrayMap;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.world.World; import net.minecraft.world.World;
@@ -52,11 +50,11 @@ public class CraftingTreeProcess
private final CraftingJob job; private final CraftingJob job;
private final Object2LongArrayMap<CraftingTreeNode> nodes = new Object2LongArrayMap<>(); private final Object2LongArrayMap<CraftingTreeNode> nodes = new Object2LongArrayMap<>();
private final int depth; private final int depth;
private final ICraftingGrid cc;
private final World world;
boolean possible = true; boolean possible = true;
private long crafts = 0; private long crafts = 0;
private IItemList<IAEItemStack> containerItems;
private boolean limitQty; private boolean limitQty;
private List<IAEItemStack> containerItemsList;
private long bytes = 0; private long bytes = 0;
public CraftingTreeProcess( final ICraftingGrid cc, final CraftingJob job, final ICraftingPatternDetails details, final CraftingTreeNode craftingTreeNode, final int depth ) 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.details = details;
this.job = job; this.job = job;
this.depth = depth; 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(); final IAEItemStack[] list = details.getInputs();
for( final IAEItemStack part : details.getCondensedInputs() ) for( final IAEItemStack part : details.getCondensedInputs() )
{ {
if( part.getItem().hasContainerItem( part.getDefinition() ) ) 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; this.limitQty = true;
//break; //break;
} }
@@ -96,13 +96,33 @@ public class CraftingTreeProcess
if( part.equals( comparePart ) ) if( part.equals( comparePart ) )
{ {
boolean isPartContainer = false; boolean isPartContainer = false;
if( containerItems != null && !containerItems.findFuzzy( list[x], FuzzyMode.IGNORE_ALL ).isEmpty() ) if( part.getItem().hasContainerItem( part.getDefinition() ) )
{ {
part = list[x]; part = list[x];
isPartContainer = true; isPartContainer = true;
} }
long wantedSize = part.getStackSize(); 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 ) if( !isPartContainer )
{ {
IAEItemStack found = job.checkAvailable( part ); IAEItemStack found = job.checkAvailable( part );
@@ -270,11 +290,6 @@ public class CraftingTreeProcess
} }
} }
IItemList<IAEItemStack> getContainerItems()
{
return this.containerItems;
}
boolean notRecursive() boolean notRecursive()
{ {
return this.parent == null || this.parent.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 void request( final MECraftingInventory inv, final long amountOfTimes, final IActionSource src ) throws CraftBranchFailure, InterruptedException
{ {
addProcess();
this.job.handlePausing(); this.job.handlePausing();
List<IAEItemStack> containerItems = null;
// request and remove inputs... // request and remove inputs...
for( final Entry<CraftingTreeNode, Long> entry : this.nodes.object2LongEntrySet() ) for( final Entry<CraftingTreeNode, Long> entry : this.nodes.object2LongEntrySet() )
{ {
final IAEItemStack stack = entry.getKey().request( inv, entry.getValue() * amountOfTimes, src ); 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 ItemStack is = Platform.getContainerItem( stack.createItemStack() );
final IAEItemStack o = AEItemStack.fromItemStack( is ); final IAEItemStack o = AEItemStack.fromItemStack( is );
if( o != null ) if( o != null )
{ {
if( containerItems == null )
{
containerItems = new ArrayList<>();
}
this.bytes++; this.bytes++;
containerItemsList.add( o ); containerItems.add( o );
} }
} }
} }
if( containerItems != null ) if( containerItems != null )
{ {
for( IAEItemStack i : containerItemsList ) for( IAEItemStack i : containerItems )
{ {
inv.injectItems( i, Actionable.MODULATE, src ); 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() ); this.localCache = new ItemListIgnoreCrafting<>( AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ).createList() );
for( final IAEItemStack is : target.getStorageList() ) 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; this.par = null;
+53 -31
View File
@@ -21,6 +21,7 @@ package appeng.helpers;
import java.util.*; import java.util.*;
import gregtech.common.items.MetaTool;
import net.minecraft.inventory.InventoryCrafting; import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -247,7 +248,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
final TestStatus result = this.getStatus( slotIndex, i ); final TestStatus result = this.getStatus( slotIndex, i );
switch( result ) switch ( result )
{ {
case ACCEPT: case ACCEPT:
return true; return true;
@@ -266,9 +267,11 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
this.testFrame.setInventorySlotContents( slotIndex, i ); this.testFrame.setInventorySlotContents( slotIndex, i );
// If we cannot substitute, the items must match exactly // If we cannot substitute, the items must match exactly
if (!canSubstitute && slotIndex < inputs.length) { if( ( !( i.getItem().isDamageable() || i.getItem() instanceof MetaTool ) && !canSubstitute ) && slotIndex < inputs.length )
if (!inputs[slotIndex].isSameType(i)) { {
this.markItemAs(slotIndex, i, TestStatus.DECLINE); if( !inputs[slotIndex].isSameType( i ) )
{
this.markItemAs( slotIndex, i, TestStatus.DECLINE );
return false; return false;
} }
} }
@@ -326,23 +329,26 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
} }
@Override @Override
public List<IAEItemStack> getSubstituteInputs(int slot) { public List<IAEItemStack> getSubstituteInputs( int slot )
if (this.inputs[slot] == null) { {
if( this.inputs[slot] == null )
{
return Collections.emptyList(); return Collections.emptyList();
} }
return this.substituteInputs.computeIfAbsent(slot, value -> { return this.substituteInputs.computeIfAbsent( slot, value -> {
ItemStack[] matchingStacks = getRecipeIngredient(slot).getMatchingStacks(); ItemStack[] matchingStacks = getRecipeIngredient( slot ).getMatchingStacks();
List<IAEItemStack> itemList = new ArrayList<>(matchingStacks.length + 1); List<IAEItemStack> itemList = new ArrayList<>( matchingStacks.length + 1 );
for (ItemStack matchingStack : matchingStacks) { for( ItemStack matchingStack : matchingStacks )
itemList.add(AEItemStack.fromItemStack(matchingStack)); {
itemList.add( AEItemStack.fromItemStack( matchingStack ) );
} }
// Ensure that the specific item put in by the user is at the beginning, // Ensure that the specific item put in by the user is at the beginning,
// so that it takes precedence over substitutions // so that it takes precedence over substitutions
itemList.add(0, this.inputs[slot]); itemList.add( 0, this.inputs[slot] );
return itemList; 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 * 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. * 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; IShapedRecipe shapedRecipe = (IShapedRecipe) standardRecipe;
return getShapedRecipeIngredient(slot, shapedRecipe.getRecipeWidth()); return getShapedRecipeIngredient( slot, shapedRecipe.getRecipeWidth() );
} else { }
return getShapelessRecipeIngredient(slot); 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 // 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 // Which is >0 if they have empty rows above or to the left of their input
int topOffset = 0; 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 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 topOffset++; // Second row is fully empty
} }
} }
int leftOffset = 0; 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 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 leftOffset++; // Second column is fully empty
} }
} }
@@ -390,27 +405,32 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
NonNullList<Ingredient> ingredients = standardRecipe.getIngredients(); NonNullList<Ingredient> ingredients = standardRecipe.getIngredients();
if (ingredientIndex < 0 || ingredientIndex > ingredients.size()) { if( ingredientIndex < 0 || ingredientIndex > ingredients.size() )
{
return Ingredient.EMPTY; 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) // We map the list of *filled* sparse inputs to the shapeless (ergo unordered)
// ingredients. While these do not actually correspond to each other, // ingredients. While these do not actually correspond to each other,
// since both lists have the same length, the mapping is at least stable. // since both lists have the same length, the mapping is at least stable.
int ingredientIndex = 0; int ingredientIndex = 0;
for (int i = 0; i < slot; i++) { for( int i = 0; i < slot; i++ )
if (inputs[i] != null) { {
if( inputs[i] != null )
{
ingredientIndex++; ingredientIndex++;
} }
} }
NonNullList<Ingredient> ingredients = standardRecipe.getIngredients(); NonNullList<Ingredient> ingredients = standardRecipe.getIngredients();
if (ingredientIndex < ingredients.size()) { if( ingredientIndex < ingredients.size() )
return ingredients.get(ingredientIndex); {
return ingredients.get( ingredientIndex );
} }
return Ingredient.EMPTY; return Ingredient.EMPTY;
@@ -517,7 +537,9 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
private enum TestStatus private enum TestStatus
{ {
ACCEPT, DECLINE, TEST ACCEPT,
DECLINE,
TEST
} }
private static final class TestLookup private static final class TestLookup
@@ -19,7 +19,6 @@
package appeng.parts.misc; package appeng.parts.misc;
import javax.annotation.Nullable;
import appeng.api.AEApi; import appeng.api.AEApi;
import appeng.api.config.AccessRestriction; import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable; import appeng.api.config.Actionable;
@@ -147,43 +146,64 @@ class ItemHandlerAdapter implements IMEInventory<IAEItemStack>, IBaseMonitor<IAE
} }
ItemStack extracted; 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 > if( !simulate )
// 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, 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.isEmpty() )
{ {
if( extracted.getCount() > remainingCurrentSlot ) extracted.setCount( Math.min( stackInInventorySlot.getCount(), remainingSize ) );
{
// 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() ) if( gathered.isEmpty() )
{ {
gathered = extracted.copy(); gathered = extracted;
} }
else else
{ {
gathered.grow( extracted.getCount() ); gathered.grow( extracted.getCount() );
} }
remainingCurrentSlot -= extracted.getCount(); remainingSize -= extracted.getCount();
} }
} while ( !extracted.isEmpty() && remainingCurrentSlot > 0 ); }
remainingSize -= stackSizeCurrentSlot - remainingCurrentSlot;
// Done?
if( remainingSize <= 0 ) if( remainingSize <= 0 )
{ {
break; break;
@@ -27,6 +27,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import gregtech.common.items.MetaTool;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; 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 ) 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 ) 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 ) 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 else
{ {
final float percentDamageOfA = (float) a.getItemDamage() / a.getMaxDamage(); float percentDamageOfA = 0;
final float percentDamageOfB = (float) b.getItemDamage() / b.getMaxDamage(); 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; return percentDamageOfA > mode.breakPoint == percentDamageOfB > mode.breakPoint;
} }
@@ -25,7 +25,9 @@ import java.util.Map;
import appeng.util.Platform; import appeng.util.Platform;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import gregtech.api.items.toolitem.ToolMetaItem;
import gregtech.common.items.MetaTool; import gregtech.common.items.MetaTool;
import it.unimi.dsi.fastutil.objects.ObjectCollection;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap; 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.config.FuzzyMode;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
/** /**
* This variant list is optimized for damageable items, and supports selecting durability ranges with * This variant list is optimized for damageable items, and supports selecting durability ranges with
* {@link #findFuzzy(IAEItemStack, FuzzyMode)}. * {@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. // 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. // We NEVER put any keys in this map that are not AESharedItemStacks.
private final Object2ObjectSortedMap<Object, IAEItemStack> records = new Object2ObjectAVLTreeMap<>(COMPARATOR); private final Object2ObjectSortedMap<Object, IAEItemStack> records = new Object2ObjectAVLTreeMap<>( COMPARATOR );
@Override @Override
public Collection<IAEItemStack> findFuzzy(final IAEItemStack filter, final FuzzyMode fuzzy) { public Collection<IAEItemStack> findFuzzy( final IAEItemStack filter, final FuzzyMode fuzzy )
ItemStack itemStack = filter.getDefinition(); {
ItemStack itemStack = filter.getDefinition();
ItemDamageBound lowerBound = makeLowerBound(itemStack, fuzzy); ItemDamageBound lowerBound = makeLowerBound( itemStack, fuzzy );
ItemDamageBound upperBound = makeUpperBound(itemStack, fuzzy); ItemDamageBound upperBound = makeUpperBound( itemStack, fuzzy );
Preconditions.checkState(lowerBound.itemDamage > upperBound.itemDamage); Preconditions.checkState( lowerBound.itemDamage > upperBound.itemDamage );
return this.records.subMap(lowerBound, upperBound).values(); return this.records.subMap( lowerBound, upperBound ).values();
} }
@SuppressWarnings("unchecked") @SuppressWarnings( "unchecked" )
@Override @Override
Map<AESharedItemStack, IAEItemStack> getRecords() { 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; // 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 { static class ItemDamageBound
final int itemDamage; {
final int itemDamage;
public ItemDamageBound(int itemDamage) { public ItemDamageBound( int itemDamage )
this.itemDamage = itemDamage; {
} this.itemDamage = itemDamage;
} }
}
/** /**
* This comparator creates a strict and total ordering over all {@link AESharedItemStack} of the same item. To * 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 * support selecting ranges of durability, it is defined for type {@link Object} and also accepts
* {@link ItemDamageBound} as an argument to compare against. * {@link ItemDamageBound} as an argument to compare against.
*/ */
static class SharedStackComparator implements Comparator<Object> { static class SharedStackComparator implements Comparator<Object>
@Override {
public int compare(Object a, Object b) { @Override
// Either argument can either be a damage bound or a shared item stack public int compare( Object a, Object b )
// Since we never put damage bounds into the map as keys, only one {
// of the two arguments can possibly be a bound // Either argument can either be a damage bound or a shared item stack
ItemDamageBound boundA = null; // Since we never put damage bounds into the map as keys, only one
AESharedItemStack stackA = null; // of the two arguments can possibly be a bound
int itemDamageA; ItemDamageBound boundA = null;
if (a instanceof ItemDamageBound) { AESharedItemStack stackA = null;
boundA = (ItemDamageBound) a; int itemDamageA;
itemDamageA = boundA.itemDamage; if( a instanceof ItemDamageBound )
} else { {
stackA = (AESharedItemStack) a; boundA = (ItemDamageBound) a;
itemDamageA = stackA.getItemDamage(); itemDamageA = boundA.itemDamage;
} }
ItemDamageBound boundB = null; else
AESharedItemStack stackB = null; {
int itemDamageB; stackA = (AESharedItemStack) a;
if (b instanceof ItemDamageBound) { itemDamageA = stackA.getItemDamage();
boundB = (ItemDamageBound) b; }
itemDamageB = boundB.itemDamage; ItemDamageBound boundB = null;
} else { AESharedItemStack stackB = null;
stackB = (AESharedItemStack) b; int itemDamageB;
itemDamageB = stackB.getItemDamage(); 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 // 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. // only to get a certain damage range out of the map.
if (boundA != null || boundB != null) { if( boundA != null || boundB != null )
return Integer.compare(itemDamageB, itemDamageA); {
} return Integer.compare( itemDamageB, itemDamageA );
}
ItemStack itemStackA = stackA.getDefinition(); ItemStack itemStackA = stackA.getDefinition();
ItemStack itemStackB = stackB.getDefinition(); ItemStack itemStackB = stackB.getDefinition();
Preconditions.checkState(itemStackA.getCount() == 1, "ItemStack#getCount() has to be 1"); Preconditions.checkState( itemStackA.getCount() == 1, "ItemStack#getCount() has to be 1" );
Preconditions.checkArgument(itemStackB.getCount() == 1, "ItemStack#getCount() has to be 1"); Preconditions.checkArgument( itemStackB.getCount() == 1, "ItemStack#getCount() has to be 1" );
if (itemStackA == itemStackB) { if( itemStackA == itemStackB )
return 0; {
} return 0;
}
// Damaged items are sorted before undamaged items // Damaged items are sorted before undamaged items
final int damageValue = Integer.compare(itemDamageB, itemDamageA); final int damageValue = Integer.compare( itemDamageB, itemDamageA );
if (damageValue != 0) { if( damageValue != 0 )
return damageValue; {
} return damageValue;
}
// As a final tie breaker, order by the object identity of the item stack // 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 // While this will order seemingly at random, we only need the order of
// damage values to be predictable, while still having to satisfy the // damage values to be predictable, while still having to satisfy the
// complete order requirements of the sorted map // complete order requirements of the sorted map
return Long.compare(System.identityHashCode(itemStackA), System.identityHashCode(itemStackB)); 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 * Minecraft reverses the damage values. So anything with a damage of 0 is undamaged and increases the more damaged
* the item is. * the item is.
* <p> * <p>
* Further the used subMap follows [MAX_DAMAGE, MIN_DAMAGE), so to include undamaged items, we have to start with a * 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. * 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; 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 * 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. * higher number than the upper bound.
*/ */
static ItemDamageBound makeLowerBound(final ItemStack stack, final FuzzyMode fuzzy) static ItemDamageBound makeLowerBound( final ItemStack stack, final FuzzyMode fuzzy )
{ {
Preconditions.checkState( stack.getItem().isDamageable() , "Item#isDamageable() has to be true" ); Preconditions.checkState( stack.getItem().isDamageable() || stack.getItem() instanceof MetaTool, "Item#isDamageable() has to be true" );
int damage; int damage;
if( fuzzy == FuzzyMode.IGNORE_ALL ) if( fuzzy == FuzzyMode.IGNORE_ALL )
{ {
if( stack.getMaxDamage() == 0 ) int maxDamage;
{ if( stack.getItem() instanceof MetaTool )
damage = stack.getItemDamage(); {
} maxDamage = ( (MetaTool) stack.getItem() ).getMaxItemDamage( stack );
else damage = ( (MetaTool) stack.getItem() ).getItemDamage( stack );
{ }
damage = stack.getMaxDamage(); else
} {
} maxDamage = stack.getMaxDamage();
else damage = stack.getItemDamage();
{ }
final int breakpoint = fuzzy.calculateBreakPoint( stack.getMaxDamage() ); if( maxDamage != 0 )
damage = stack.getItemDamage() <= breakpoint ? breakpoint : stack.getMaxDamage(); {
} 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 * 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. * lower number than the lower bound. It also is exclusive.
*/ */
static ItemDamageBound makeUpperBound(final ItemStack stack, final FuzzyMode fuzzy) { static ItemDamageBound makeUpperBound( final ItemStack stack, final FuzzyMode fuzzy )
Preconditions.checkState(stack.getItem().isDamageable() , "Item#isDamageable() has to be true"); {
Preconditions.checkState( stack.getItem().isDamageable() || stack.getItem() instanceof MetaTool, "Item#isDamageable() has to be true" );
int damage; int damage;
if (fuzzy == FuzzyMode.IGNORE_ALL) { if( fuzzy == FuzzyMode.IGNORE_ALL )
damage = MIN_DAMAGE_VALUE; {
} else { damage = MIN_DAMAGE_VALUE;
final int breakpoint = fuzzy.calculateBreakPoint(stack.getMaxDamage()); }
damage = stack.getItemDamage() <= breakpoint ? MIN_DAMAGE_VALUE : breakpoint; 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 );
} }
} }
+177 -140
View File
@@ -36,180 +36,217 @@ import appeng.api.config.FuzzyMode;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList; import appeng.api.storage.data.IItemList;
public final class ItemList implements IItemList<IAEItemStack> {
private final Reference2ObjectMap<Item, ItemVariantList> records = new Reference2ObjectOpenHashMap<>(); public final class ItemList implements IItemList<IAEItemStack>
/** {
* 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);
@Override private final Reference2ObjectMap<Item, ItemVariantList> records = new Reference2ObjectOpenHashMap<>();
public IAEItemStack findPrecise(final IAEItemStack itemStack) { /**
if (itemStack == null) { * We increment this version field everytime an attempt to mutate this item list (or potentially one of its
return null; * 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()); @Override
return record != null ? record.findPrecise(itemStack) : null; public IAEItemStack findPrecise( final IAEItemStack itemStack )
} {
if( itemStack == null )
{
return null;
}
@Override ItemVariantList record = this.records.get( itemStack.getItem() );
public Collection<IAEItemStack> findFuzzy(final IAEItemStack filter, final FuzzyMode fuzzy) { return record != null ? record.findPrecise( itemStack ) : null;
if (filter == null) { }
return Collections.emptyList();
}
ItemVariantList record = this.records.get(filter.getItem()); @Override
return record != null ? record.findFuzzy(filter, fuzzy) : Collections.emptyList(); public Collection<IAEItemStack> findFuzzy( final IAEItemStack filter, final FuzzyMode fuzzy )
} {
if( filter == null )
{
return Collections.emptyList();
}
@Override ItemVariantList record = this.records.get( filter.getItem() );
public boolean isEmpty() { return record != null ? record.findFuzzy( filter, fuzzy ) : Collections.emptyList();
return !this.iterator().hasNext(); }
}
@Override @Override
public void add(final IAEItemStack itemStack) { public boolean isEmpty()
version.incrementAndGet(); {
return !this.iterator().hasNext();
}
if (itemStack == null) { @Override
return; public void add( final IAEItemStack itemStack )
} {
version.incrementAndGet();
this.getOrCreateRecord(itemStack.getItem()).add(itemStack); if( itemStack == null )
} {
return;
}
@Override this.getOrCreateRecord( itemStack.getItem() ).add( itemStack );
public void addStorage(final IAEItemStack itemStack) { }
version.incrementAndGet();
if (itemStack == null) { @Override
return; public void addStorage( final IAEItemStack itemStack )
} {
version.incrementAndGet();
this.getOrCreateRecord(itemStack.getItem()).addStorage(itemStack); if( itemStack == null )
} {
return;
}
@Override this.getOrCreateRecord( itemStack.getItem() ).addStorage( itemStack );
public void addCrafting(final IAEItemStack itemStack) { }
version.incrementAndGet();
if (itemStack == null) { @Override
return; public void addCrafting( final IAEItemStack itemStack )
} {
version.incrementAndGet();
this.getOrCreateRecord(itemStack.getItem()).addCrafting(itemStack); if( itemStack == null )
} {
return;
}
@Override this.getOrCreateRecord( itemStack.getItem() ).addCrafting( itemStack );
public void addRequestable(final IAEItemStack itemStack) { }
version.incrementAndGet();
if (itemStack == null) { @Override
return; public void addRequestable( final IAEItemStack itemStack )
} {
version.incrementAndGet();
this.getOrCreateRecord(itemStack.getItem()).addRequestable(itemStack); if( itemStack == null )
} {
return;
}
@Override this.getOrCreateRecord( itemStack.getItem() ).addRequestable( itemStack );
public IAEItemStack getFirstItem() { }
for (final IAEItemStack stackType : this) {
return stackType;
}
return null; @Override
} public IAEItemStack getFirstItem()
{
for( final IAEItemStack stackType : this )
{
return stackType;
}
@Override return null;
public int size() { }
int size = 0;
for (ItemVariantList entry : records.values()) {
size += entry.size();
}
return size; @Override
} public int size()
{
int size = 0;
for( ItemVariantList entry : records.values() )
{
size += entry.size();
}
@Override return size;
public Iterator<IAEItemStack> iterator() { }
return new ChainedIterator(this.records.values().iterator(), version);
}
@Override @Override
public void resetStatus() { public Iterator<IAEItemStack> iterator()
for (final IAEItemStack i : this) { {
i.reset(); return new ChainedIterator( this.records.values().iterator(), version );
} }
}
private ItemVariantList getOrCreateRecord(Item item) { @Override
return this.records.computeIfAbsent(item, this::makeRecordMap); public void resetStatus()
} {
for( final IAEItemStack i : this )
{
i.reset();
}
}
private ItemVariantList makeRecordMap(Item item) { private ItemVariantList getOrCreateRecord( Item item )
if (item.isDamageable() ) { {
return new FuzzyItemVariantList(); return this.records.computeIfAbsent( item, this::makeRecordMap );
} else { }
return new NormalItemVariantList();
}
}
/** private ItemVariantList makeRecordMap( Item item )
* Iterates over multiple item lists as if they were one list. {
*/ if( item.isDamageable() || item instanceof MetaTool )
private static class ChainedIterator implements Iterator<IAEItemStack> { {
return new FuzzyItemVariantList();
}
else
{
return new NormalItemVariantList();
}
}
private final AtomicInteger parentVersion; /**
private final int version; * Iterates over multiple item lists as if they were one list.
private final Iterator<ItemVariantList> parent; */
private Iterator<IAEItemStack> next; private static class ChainedIterator implements Iterator<IAEItemStack>
{
public ChainedIterator(Iterator<ItemVariantList> iterator, AtomicInteger parentVersion) { private final AtomicInteger parentVersion;
this.parent = iterator; private final int version;
this.parentVersion = parentVersion; private final Iterator<ItemVariantList> parent;
this.version = parentVersion.get(); private Iterator<IAEItemStack> next;
this.ensureItems();
}
@Override public ChainedIterator( Iterator<ItemVariantList> iterator, AtomicInteger parentVersion )
public boolean hasNext() { {
return next != null && next.hasNext(); this.parent = iterator;
} this.parentVersion = parentVersion;
this.version = parentVersion.get();
this.ensureItems();
}
@Override @Override
public IAEItemStack next() { public boolean hasNext()
if (this.next == null) { {
throw new NoSuchElementException(); return next != null && next.hasNext();
} }
if (this.version != this.parentVersion.get()) {
throw new ConcurrentModificationException();
}
IAEItemStack result = this.next.next(); @Override
this.ensureItems(); public IAEItemStack next()
return result; {
} if( this.next == null )
{
throw new NoSuchElementException();
}
if( this.version != this.parentVersion.get() )
{
throw new ConcurrentModificationException();
}
private void ensureItems() { IAEItemStack result = this.next.next();
if (hasNext()) { this.ensureItems();
return; // Still items left in the current one return result;
} }
// Find the next iterator willing to return some items... private void ensureItems()
while (this.parent.hasNext()) { {
this.next = this.parent.next().iterator(); if( hasNext() )
{
return; // Still items left in the current one
}
if (this.next.hasNext()) { // Find the next iterator willing to return some items...
return; // Found one! while ( this.parent.hasNext() )
} {
} this.next = this.parent.next().iterator();
// No more items if( this.next.hasNext() )
this.next = null; {
} return; // Found one!
} }
}
// No more items
this.next = null;
}
}
} }