Applied-Energistics-2-tiler.../src/main/java/appeng/helpers/PatternHelper.java
LordMZTE f67fb6a129
Some checks failed
continuous-integration/drone/push Build is failing
chore: format code
2022-12-02 17:40:47 +01:00

387 lines
12 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.helpers;
import java.util.*;
import appeng.api.AEApi;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.storage.data.IAEItemStack;
import appeng.container.ContainerNull;
import appeng.util.ItemSorters;
import appeng.util.Platform;
import appeng.util.item.AEItemStack;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
public class PatternHelper implements ICraftingPatternDetails, Comparable<PatternHelper> {
private final ItemStack patternItem;
private final InventoryCrafting crafting
= new InventoryCrafting(new ContainerNull(), 3, 3);
private final InventoryCrafting testFrame
= new InventoryCrafting(new ContainerNull(), 3, 3);
private final ItemStack correctOutput;
private final IRecipe standardRecipe;
private final IAEItemStack[] condensedInputs;
private final IAEItemStack[] condensedOutputs;
private final IAEItemStack[] inputs;
private final IAEItemStack[] outputs;
private final boolean isCrafting;
private final boolean canSubstitute;
private final Set<TestLookup> failCache = new HashSet<TestLookup>();
private final Set<TestLookup> passCache = new HashSet<TestLookup>();
private final IAEItemStack pattern;
private int priority = 0;
public PatternHelper(final ItemStack is, final World w) {
final NBTTagCompound encodedValue = is.getTagCompound();
if (encodedValue == null) {
throw new IllegalArgumentException("No pattern here!");
}
final NBTTagList inTag = encodedValue.getTagList("in", 10);
final NBTTagList outTag = encodedValue.getTagList("out", 10);
this.isCrafting = encodedValue.getBoolean("crafting");
this.canSubstitute = encodedValue.getBoolean("substitute");
this.patternItem = is;
this.pattern = AEItemStack.create(is);
final List<IAEItemStack> in = new ArrayList<IAEItemStack>();
final List<IAEItemStack> out = new ArrayList<IAEItemStack>();
for (int x = 0; x < inTag.tagCount(); x++) {
final ItemStack gs
= ItemStack.loadItemStackFromNBT(inTag.getCompoundTagAt(x));
this.crafting.setInventorySlotContents(x, gs);
if (gs != null && (!this.isCrafting || !gs.hasTagCompound())) {
this.markItemAs(x, gs, TestStatus.ACCEPT);
}
in.add(AEApi.instance().storage().createItemStack(gs));
this.testFrame.setInventorySlotContents(x, gs);
}
if (this.isCrafting) {
this.standardRecipe = Platform.findMatchingRecipe(this.crafting, w);
if (this.standardRecipe != null) {
this.correctOutput = this.standardRecipe.getCraftingResult(this.crafting);
out.add(AEApi.instance().storage().createItemStack(this.correctOutput));
} else {
throw new IllegalStateException("No pattern here!");
}
} else {
this.standardRecipe = null;
this.correctOutput = null;
for (int x = 0; x < outTag.tagCount(); x++) {
final ItemStack gs
= ItemStack.loadItemStackFromNBT(outTag.getCompoundTagAt(x));
if (gs != null) {
out.add(AEApi.instance().storage().createItemStack(gs));
}
}
}
this.outputs = out.toArray(new IAEItemStack[out.size()]);
this.inputs = in.toArray(new IAEItemStack[in.size()]);
final Map<IAEItemStack, IAEItemStack> tmpOutputs
= new HashMap<IAEItemStack, IAEItemStack>();
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<IAEItemStack, IAEItemStack>();
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++;
}
}
private void markItemAs(final int slotIndex, final ItemStack i, final TestStatus b) {
if (b == TestStatus.TEST || i.hasTagCompound()) {
return;
}
(b == TestStatus.ACCEPT ? this.passCache : this.failCache)
.add(new TestLookup(slotIndex, i));
}
@Override
public ItemStack getPattern() {
return this.patternItem;
}
@Override
public synchronized boolean
isValidItemForSlot(final int slotIndex, final ItemStack i, final World w) {
if (!this.isCrafting) {
throw new IllegalStateException("Only crafting recipes supported.");
}
final TestStatus result = this.getStatus(slotIndex, i);
switch (result) {
case ACCEPT:
return true;
case DECLINE:
return false;
case TEST:
default:
break;
}
for (int x = 0; x < this.crafting.getSizeInventory(); x++) {
this.testFrame.setInventorySlotContents(x, this.crafting.getStackInSlot(x));
}
this.testFrame.setInventorySlotContents(slotIndex, i);
if (this.standardRecipe.matches(this.testFrame, w)) {
final ItemStack testOutput
= this.standardRecipe.getCraftingResult(this.testFrame);
if (Platform.isSameItemPrecise(this.correctOutput, testOutput)) {
this.testFrame.setInventorySlotContents(
slotIndex, this.crafting.getStackInSlot(slotIndex)
);
this.markItemAs(slotIndex, i, TestStatus.ACCEPT);
return true;
}
} else {
final ItemStack testOutput
= CraftingManager.getInstance().findMatchingRecipe(this.testFrame, w);
if (Platform.isSameItemPrecise(this.correctOutput, testOutput)) {
this.testFrame.setInventorySlotContents(
slotIndex, this.crafting.getStackInSlot(slotIndex)
);
this.markItemAs(slotIndex, i, TestStatus.ACCEPT);
return true;
}
}
this.markItemAs(slotIndex, i, TestStatus.DECLINE);
return false;
}
@Override
public boolean isCraftable() {
return this.isCrafting;
}
@Override
public IAEItemStack[] getInputs() {
return this.inputs;
}
@Override
public IAEItemStack[] getCondensedInputs() {
return this.condensedInputs;
}
@Override
public IAEItemStack[] getCondensedOutputs() {
return this.condensedOutputs;
}
@Override
public IAEItemStack[] getOutputs() {
return this.outputs;
}
@Override
public boolean canSubstitute() {
return this.canSubstitute;
}
@Override
public ItemStack getOutput(final InventoryCrafting craftingInv, final World w) {
if (!this.isCrafting) {
throw new IllegalStateException("Only crafting recipes supported.");
}
for (int x = 0; x < craftingInv.getSizeInventory(); x++) {
if (!this.isValidItemForSlot(x, craftingInv.getStackInSlot(x), w)) {
return null;
}
}
if (this.outputs != null && this.outputs.length > 0) {
return this.outputs[0].getItemStack();
}
return null;
}
private TestStatus getStatus(final int slotIndex, final ItemStack i) {
if (this.crafting.getStackInSlot(slotIndex) == null) {
return i == null ? TestStatus.ACCEPT : TestStatus.DECLINE;
}
if (i == null) {
return TestStatus.DECLINE;
}
if (i.hasTagCompound()) {
return TestStatus.TEST;
}
if (this.passCache.contains(new TestLookup(slotIndex, i))) {
return TestStatus.ACCEPT;
}
if (this.failCache.contains(new TestLookup(slotIndex, i))) {
return TestStatus.DECLINE;
}
return TestStatus.TEST;
}
@Override
public int getPriority() {
return this.priority;
}
@Override
public void setPriority(final int priority) {
this.priority = priority;
}
@Override
public int compareTo(final PatternHelper o) {
return ItemSorters.compareInt(o.priority, this.priority);
}
@Override
public int hashCode() {
return this.pattern.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final PatternHelper other = (PatternHelper) obj;
if (this.pattern != null && other.pattern != null) {
return this.pattern.equals(other.pattern);
}
return false;
}
private enum TestStatus { ACCEPT, DECLINE, TEST }
private static final class TestLookup {
private final int slot;
private final int ref;
private final int hash;
public TestLookup(final int slot, final ItemStack i) {
this(slot, i.getItem(), i.getItemDamage());
}
public TestLookup(final int slot, final Item item, final int dmg) {
this.slot = slot;
this.ref = (dmg << Platform.DEF_OFFSET) | (Item.getIdFromItem(item) & 0xffff);
final int offset = 3 * slot;
this.hash = (this.ref << offset) | (this.ref >> (offset + 32));
}
@Override
public int hashCode() {
return this.hash;
}
@Override
public boolean equals(final Object obj) {
final boolean equality;
if (obj instanceof TestLookup) {
final TestLookup b = (TestLookup) obj;
equality = b.slot == this.slot && b.ref == this.ref;
} else {
equality = false;
}
return equality;
}
}
}