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
@@ -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 );
}
}
+177 -140
View File
@@ -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;
}
}
}