* Fixes #4508: Correctly construct the sparse lists. Also renamed all uncondensed getters to sparse and all condensed to the simple ones. * Apply suggestions from code review Co-authored-by: shartte <shartte@users.noreply.github.com> * Streamlined collapsing the sparse lists * Extracted helper to condense stacks * Sort lists desc by stack size Co-authored-by: shartte <shartte@users.noreply.github.com>
This commit is contained in:
@@ -18,9 +18,6 @@
|
||||
|
||||
package appeng.container.implementations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
@@ -264,21 +261,13 @@ public class PatternTermContainer extends MEMonitorableContainer
|
||||
return new ItemStack[] { out };
|
||||
}
|
||||
} else {
|
||||
final List<ItemStack> list = new ArrayList<>(3);
|
||||
boolean hasValue = false;
|
||||
final ItemStack[] list = new ItemStack[3];
|
||||
|
||||
for (final OptionalFakeSlot outputSlot : this.outputSlots) {
|
||||
final ItemStack out = outputSlot.getStack();
|
||||
|
||||
if (!out.isEmpty() && out.getCount() > 0) {
|
||||
list.add(out);
|
||||
hasValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValue) {
|
||||
return list.toArray(new ItemStack[list.size()]);
|
||||
for (int i = 0; i < this.outputSlots.length; i++) {
|
||||
final ItemStack out = this.outputSlots[i].getStack();
|
||||
list[i] = out;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -85,7 +85,7 @@ public class CraftingTreeNode {
|
||||
}
|
||||
|
||||
boolean notRecursive(final ICraftingPatternDetails details) {
|
||||
IAEItemStack[] o = details.getCondensedOutputs();
|
||||
Collection<IAEItemStack> o = details.getOutputs();
|
||||
|
||||
for (final IAEItemStack i : o) {
|
||||
if (i.equals(this.what)) {
|
||||
@@ -93,7 +93,7 @@ public class CraftingTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
o = details.getCondensedInputs();
|
||||
o = details.getInputs();
|
||||
|
||||
for (final IAEItemStack i : o) {
|
||||
if (i.equals(this.what)) {
|
||||
|
||||
@@ -64,10 +64,10 @@ public class CraftingTreeProcess {
|
||||
final World world = job.getWorld();
|
||||
|
||||
if (details.isCraftable()) {
|
||||
final IAEItemStack[] list = details.getInputs();
|
||||
final IAEItemStack[] list = details.getSparseInputs();
|
||||
|
||||
final CraftingInventory ic = new CraftingInventory(new ContainerNull(), 3, 3);
|
||||
final IAEItemStack[] is = details.getInputs();
|
||||
final IAEItemStack[] is = details.getSparseInputs();
|
||||
for (int x = 0; x < ic.getSizeInventory(); x++) {
|
||||
ic.setInventorySlotContents(x, is[x] == null ? ItemStack.EMPTY : is[x].createItemStack());
|
||||
}
|
||||
@@ -82,11 +82,11 @@ public class CraftingTreeProcess {
|
||||
}
|
||||
}
|
||||
|
||||
for (final IAEItemStack part : details.getCondensedInputs()) {
|
||||
for (final IAEItemStack part : details.getInputs()) {
|
||||
final ItemStack g = part.createItemStack();
|
||||
|
||||
boolean isAnInput = false;
|
||||
for (final IAEItemStack a : details.getCondensedOutputs()) {
|
||||
for (final IAEItemStack a : details.getOutputs()) {
|
||||
if (!g.isEmpty() && a != null && a.equals(g)) {
|
||||
isAnInput = true;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class CraftingTreeProcess {
|
||||
} else {
|
||||
// this is minor different then below, this slot uses the pattern, but kinda
|
||||
// fudges it.
|
||||
for (final IAEItemStack part : details.getCondensedInputs()) {
|
||||
for (final IAEItemStack part : details.getInputs()) {
|
||||
for (int x = 0; x < list.length; x++) {
|
||||
final IAEItemStack comparePart = list[x];
|
||||
if (part != null && part.equals(comparePart)) {
|
||||
@@ -127,11 +127,11 @@ public class CraftingTreeProcess {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (final IAEItemStack part : details.getCondensedInputs()) {
|
||||
for (final IAEItemStack part : details.getInputs()) {
|
||||
final ItemStack g = part.createItemStack();
|
||||
|
||||
boolean isAnInput = false;
|
||||
for (final IAEItemStack a : details.getCondensedOutputs()) {
|
||||
for (final IAEItemStack a : details.getOutputs()) {
|
||||
if (!g.isEmpty() && a != null && a.equals(g)) {
|
||||
isAnInput = true;
|
||||
}
|
||||
@@ -142,7 +142,7 @@ public class CraftingTreeProcess {
|
||||
}
|
||||
}
|
||||
|
||||
for (final IAEItemStack part : details.getCondensedInputs()) {
|
||||
for (final IAEItemStack part : details.getInputs()) {
|
||||
this.nodes.put(new CraftingTreeNode(cc, job, part.copy(), this, -1, depth + 1), part.getStackSize());
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public class CraftingTreeProcess {
|
||||
// assume its possible.
|
||||
|
||||
// add crafting results..
|
||||
for (final IAEItemStack out : this.details.getCondensedOutputs()) {
|
||||
for (final IAEItemStack out : this.details.getOutputs()) {
|
||||
final IAEItemStack o = out.copy();
|
||||
o.setStackSize(o.getStackSize() * i);
|
||||
inv.injectItems(o, Actionable.MODULATE, src);
|
||||
@@ -227,7 +227,7 @@ public class CraftingTreeProcess {
|
||||
}
|
||||
|
||||
IAEItemStack getAmountCrafted(IAEItemStack what2) {
|
||||
for (final IAEItemStack is : this.details.getCondensedOutputs()) {
|
||||
for (final IAEItemStack is : this.details.getOutputs()) {
|
||||
if (is.equals(what2)) {
|
||||
what2 = what2.copy();
|
||||
what2.setStackSize(is.getStackSize());
|
||||
@@ -236,7 +236,7 @@ public class CraftingTreeProcess {
|
||||
}
|
||||
|
||||
// more fuzzy!
|
||||
for (final IAEItemStack is : this.details.getCondensedOutputs()) {
|
||||
for (final IAEItemStack is : this.details.getOutputs()) {
|
||||
if (is.getItem() == what2.getItem()
|
||||
&& (is.getItem().isDamageable() || is.getItemDamage() == what2.getItemDamage())) {
|
||||
what2 = is.copy();
|
||||
|
||||
@@ -19,13 +19,17 @@
|
||||
package appeng.helpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import net.minecraft.inventory.CraftingInventory;
|
||||
import net.minecraft.item.Item;
|
||||
@@ -46,14 +50,21 @@ import appeng.util.Platform;
|
||||
|
||||
public class CraftingPatternDetails implements ICraftingPatternDetails, Comparable<CraftingPatternDetails> {
|
||||
|
||||
private static final int ALL_INPUT_LIMIT = 9;
|
||||
private static final int CRAFTING_OUTPUT_LIMIT = 1;
|
||||
private static final int PROCESSING_OUTPUT_LIMIT = 3;
|
||||
|
||||
private static final Comparator<IAEItemStack> COMPARE_BY_STACKSIZE = (left, right) -> Long
|
||||
.compare(right.getStackSize(), left.getStackSize());
|
||||
|
||||
private final CraftingInventory crafting = new CraftingInventory(new ContainerNull(), 3, 3);
|
||||
private final CraftingInventory testFrame = new CraftingInventory(new ContainerNull(), 3, 3);
|
||||
private final ItemStack correctOutput;
|
||||
private final ICraftingRecipe standardRecipe;
|
||||
private final IAEItemStack[] condensedInputs;
|
||||
private final IAEItemStack[] condensedOutputs;
|
||||
private final IAEItemStack[] inputs;
|
||||
private final IAEItemStack[] outputs;
|
||||
private final List<IAEItemStack> inputs;
|
||||
private final List<IAEItemStack> outputs;
|
||||
private final IAEItemStack[] sparseInputs;
|
||||
private final IAEItemStack[] sparseOutputs;
|
||||
private final boolean isCraftable;
|
||||
private final boolean canSubstitute;
|
||||
private final Set<TestLookup> failCache = new HashSet<>();
|
||||
@@ -79,7 +90,7 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab
|
||||
final List<IAEItemStack> in = new ArrayList<>();
|
||||
final List<IAEItemStack> out = new ArrayList<>();
|
||||
|
||||
for (int x = 0; x < 9; x++) {
|
||||
for (int x = 0; x < ALL_INPUT_LIMIT; x++) {
|
||||
final IAEItemStack ais = ingredients.get(x);
|
||||
final ItemStack gs = ais != null ? ais.createItemStack() : ItemStack.EMPTY;
|
||||
|
||||
@@ -109,70 +120,19 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab
|
||||
this.standardRecipe = null;
|
||||
this.correctOutput = ItemStack.EMPTY;
|
||||
|
||||
for (int x = 0; x < products.size(); x++) {
|
||||
for (int x = 0; x < PROCESSING_OUTPUT_LIMIT; x++) {
|
||||
final IAEItemStack ais = products.get(x);
|
||||
final ItemStack gs = ais.createItemStack();
|
||||
|
||||
if (!gs.isEmpty()) {
|
||||
out.add(ais.copy());
|
||||
}
|
||||
out.add(ais != null ? ais.copy() : null);
|
||||
}
|
||||
}
|
||||
|
||||
this.inputs = in.toArray(new IAEItemStack[0]);
|
||||
this.outputs = out.toArray(new IAEItemStack[0]);
|
||||
final int outputLength = this.isCraftable ? CRAFTING_OUTPUT_LIMIT : PROCESSING_OUTPUT_LIMIT;
|
||||
this.sparseInputs = in.toArray(new IAEItemStack[ALL_INPUT_LIMIT]);
|
||||
this.sparseOutputs = out.toArray(new IAEItemStack[outputLength]);
|
||||
|
||||
final Map<IAEItemStack, IAEItemStack> tmpOutputs = new HashMap<>();
|
||||
|
||||
for (final IAEItemStack io : this.outputs) {
|
||||
if (io == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final IAEItemStack g = tmpOutputs.get(io);
|
||||
|
||||
if (g == null) {
|
||||
tmpOutputs.put(io, io.copy());
|
||||
} else {
|
||||
g.add(io);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<IAEItemStack, IAEItemStack> tmpInputs = new HashMap<>();
|
||||
|
||||
for (final IAEItemStack io : this.inputs) {
|
||||
if (io == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final IAEItemStack g = tmpInputs.get(io);
|
||||
|
||||
if (g == null) {
|
||||
tmpInputs.put(io, io.copy());
|
||||
} else {
|
||||
g.add(io);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmpOutputs.isEmpty() || tmpInputs.isEmpty()) {
|
||||
throw new IllegalStateException("No pattern here!");
|
||||
}
|
||||
|
||||
this.condensedInputs = new IAEItemStack[tmpInputs.size()];
|
||||
int offset = 0;
|
||||
|
||||
for (final IAEItemStack io : tmpInputs.values()) {
|
||||
this.condensedInputs[offset] = io;
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
this.condensedOutputs = new IAEItemStack[tmpOutputs.size()];
|
||||
|
||||
for (final IAEItemStack io : tmpOutputs.values()) {
|
||||
this.condensedOutputs[offset] = io;
|
||||
offset++;
|
||||
}
|
||||
this.inputs = this.condenseStacks(in);
|
||||
this.outputs = this.condenseStacks(out);
|
||||
}
|
||||
|
||||
private void markItemAs(final int slotIndex, final ItemStack i, final TestStatus b) {
|
||||
@@ -213,8 +173,8 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab
|
||||
this.testFrame.setInventorySlotContents(slotIndex, i);
|
||||
|
||||
// If we cannot substitute, the items must match exactly
|
||||
if (!canSubstitute && slotIndex < inputs.length) {
|
||||
if (!inputs[slotIndex].isSameType(i)) {
|
||||
if (!canSubstitute && slotIndex < sparseInputs.length) {
|
||||
if (!sparseInputs[slotIndex].isSameType(i)) {
|
||||
this.markItemAs(slotIndex, i, TestStatus.DECLINE);
|
||||
return false;
|
||||
}
|
||||
@@ -240,25 +200,25 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack[] getInputs() {
|
||||
public IAEItemStack[] getSparseInputs() {
|
||||
return this.sparseInputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IAEItemStack> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack[] getCondensedInputs() {
|
||||
return this.condensedInputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack[] getCondensedOutputs() {
|
||||
return this.condensedOutputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack[] getOutputs() {
|
||||
public List<IAEItemStack> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEItemStack[] getSparseOutputs() {
|
||||
return this.sparseOutputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSubstitute() {
|
||||
return this.canSubstitute;
|
||||
@@ -276,8 +236,8 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab
|
||||
}
|
||||
}
|
||||
|
||||
if (this.outputs != null && this.outputs.length > 0) {
|
||||
return this.outputs[0].createItemStack();
|
||||
if (this.sparseOutputs != null && this.sparseOutputs.length > 0) {
|
||||
return this.sparseOutputs[0].createItemStack();
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
@@ -344,6 +304,29 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all equal entries into a single one while adding their total stack
|
||||
* sizes.
|
||||
*
|
||||
* @throws IllegalStateException if the result would be empty.
|
||||
*
|
||||
* @param collection the collection to condense
|
||||
*
|
||||
* @return a non empty list of condensed stacks.
|
||||
*/
|
||||
private List<IAEItemStack> condenseStacks(Collection<IAEItemStack> collection) {
|
||||
final List<IAEItemStack> merged = collection.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(Function.identity(), IAEItemStack::copy,
|
||||
(left, right) -> left.setStackSize(left.getStackSize() + right.getStackSize())))
|
||||
.values().stream().sorted(COMPARE_BY_STACKSIZE).collect(ImmutableList.toImmutableList());
|
||||
|
||||
if (merged.isEmpty()) {
|
||||
throw new IllegalStateException("No pattern here!");
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private enum TestStatus {
|
||||
ACCEPT, DECLINE, TEST
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package appeng.items.misc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
@@ -155,8 +156,8 @@ public class EncodedPatternItem extends AEBaseItem {
|
||||
final boolean isCrafting = details.isCraftable();
|
||||
final boolean substitute = details.canSubstitute();
|
||||
|
||||
final IAEItemStack[] in = details.getCondensedInputs();
|
||||
final IAEItemStack[] out = details.getCondensedOutputs();
|
||||
final Collection<IAEItemStack> in = details.getInputs();
|
||||
final Collection<IAEItemStack> out = details.getOutputs();
|
||||
|
||||
final ITextComponent label = (isCrafting ? GuiText.Crafts.textComponent() : GuiText.Creates.textComponent())
|
||||
.appendText(": ");
|
||||
@@ -208,7 +209,7 @@ public class EncodedPatternItem extends AEBaseItem {
|
||||
|
||||
final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(item, w);
|
||||
|
||||
out = details != null ? details.getOutputs()[0].createItemStack() : ItemStack.EMPTY;
|
||||
out = details != null ? details.getOutputs().get(0).createItemStack() : ItemStack.EMPTY;
|
||||
|
||||
SIMPLE_CACHE.put(item, out);
|
||||
return out;
|
||||
|
||||
@@ -429,7 +429,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean canCraft(final ICraftingPatternDetails details, final IAEItemStack[] condensedInputs) {
|
||||
private boolean canCraft(final ICraftingPatternDetails details, final Collection<IAEItemStack> condensedInputs) {
|
||||
for (IAEItemStack g : condensedInputs) {
|
||||
|
||||
if (details.isCraftable()) {
|
||||
@@ -557,7 +557,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
|
||||
final ICraftingPatternDetails details = e.getKey();
|
||||
|
||||
if (this.canCraft(details, details.getCondensedInputs())) {
|
||||
if (this.canCraft(details, details.getInputs())) {
|
||||
CraftingInventory ic = null;
|
||||
|
||||
for (final ICraftingMedium m : cc.getMediums(e.getKey())) {
|
||||
@@ -567,7 +567,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
|
||||
if (!m.isBusy()) {
|
||||
if (ic == null) {
|
||||
final IAEItemStack[] input = details.getInputs();
|
||||
final IAEItemStack[] input = details.getSparseInputs();
|
||||
double sum = 0;
|
||||
|
||||
for (final IAEItemStack anInput : input) {
|
||||
@@ -663,7 +663,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
this.somethingChanged = true;
|
||||
this.remainingOperations--;
|
||||
|
||||
for (final IAEItemStack out : details.getCondensedOutputs()) {
|
||||
for (final IAEItemStack out : details.getOutputs()) {
|
||||
this.postChange(out, this.machineSrc);
|
||||
this.waitingFor.add(out.copy());
|
||||
this.postCraftingStatusChange(out.copy());
|
||||
@@ -896,7 +896,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
break;
|
||||
case PENDING:
|
||||
for (final Entry<ICraftingPatternDetails, TaskProgress> t : this.tasks.entrySet()) {
|
||||
for (IAEItemStack ais : t.getKey().getCondensedOutputs()) {
|
||||
for (IAEItemStack ais : t.getKey().getOutputs()) {
|
||||
ais = ais.copy();
|
||||
ais.setStackSize(ais.getStackSize() * t.getValue().value);
|
||||
list.add(ais);
|
||||
@@ -915,7 +915,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
}
|
||||
|
||||
for (final Entry<ICraftingPatternDetails, TaskProgress> t : this.tasks.entrySet()) {
|
||||
for (IAEItemStack ais : t.getKey().getCondensedOutputs()) {
|
||||
for (IAEItemStack ais : t.getKey().getOutputs()) {
|
||||
ais = ais.copy();
|
||||
ais.setStackSize(ais.getStackSize() * t.getValue().value);
|
||||
list.add(ais);
|
||||
@@ -960,7 +960,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
is.setStackSize(0);
|
||||
|
||||
for (final Entry<ICraftingPatternDetails, TaskProgress> t : this.tasks.entrySet()) {
|
||||
for (final IAEItemStack ais : t.getKey().getCondensedOutputs()) {
|
||||
for (final IAEItemStack ais : t.getKey().getOutputs()) {
|
||||
if (ais.equals(is)) {
|
||||
is.setStackSize(is.getStackSize() + ais.getStackSize() * t.getValue().value);
|
||||
}
|
||||
|
||||
@@ -113,13 +113,13 @@ public class PatternTerminalPart extends AbstractTerminalPart {
|
||||
this.setCraftingRecipe(details.isCraftable());
|
||||
this.setSubstitution(details.canSubstitute());
|
||||
|
||||
for (int x = 0; x < this.crafting.getSlots() && x < details.getInputs().length; x++) {
|
||||
final IAEItemStack item = details.getInputs()[x];
|
||||
for (int x = 0; x < this.crafting.getSlots() && x < details.getSparseInputs().length; x++) {
|
||||
final IAEItemStack item = details.getSparseInputs()[x];
|
||||
this.crafting.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack());
|
||||
}
|
||||
|
||||
for (int x = 0; x < this.output.getSlots() && x < details.getOutputs().length; x++) {
|
||||
final IAEItemStack item = details.getOutputs()[x];
|
||||
for (int x = 0; x < this.output.getSlots() && x < details.getSparseOutputs().length; x++) {
|
||||
final IAEItemStack item = details.getSparseOutputs()[x];
|
||||
this.output.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user