Refactored crafting patterns (#4458)

* Deduplicate crafting patterns internals

This should save a bit memory for duplicate patterns as well as speed up
loading new patterns on reloading chunks.

* Encode recipe id in pattern
* Use the recipe id directly for looking it up
* Slightly refactored how patterns encode whether they are crafting patterns or not.
* Renamed PatternHelper.
* Added APIs to encode patterns. (#4463)
* Removed ICraftingPatternItem.
* Renamed isCrafting to isCraftable

Co-authored-by: shartte <shartte@users.noreply.github.com>
This commit is contained in:
yueh
2020-07-12 16:21:30 +02:00
committed by GitHub
parent 06735f5c39
commit 9bb23bcac8
19 changed files with 551 additions and 243 deletions
+6
View File
@@ -24,6 +24,7 @@
package appeng.api; package appeng.api;
import appeng.api.client.IClientHelper; import appeng.api.client.IClientHelper;
import appeng.api.crafting.ICraftingHelper;
import appeng.api.definitions.IDefinitions; import appeng.api.definitions.IDefinitions;
import appeng.api.features.IRegistryContainer; import appeng.api.features.IRegistryContainer;
import appeng.api.networking.IGridHelper; import appeng.api.networking.IGridHelper;
@@ -48,6 +49,11 @@ public interface IAppEngApi {
*/ */
IStorageHelper storage(); IStorageHelper storage();
/**
* @return A helper for working with crafting related tasks.
*/
ICraftingHelper crafting();
/** /**
* @return A helper to create {@link IGridNode} and other grid related objects. * @return A helper to create {@link IGridNode} and other grid related objects.
*/ */
@@ -0,0 +1,108 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 TeamAppliedEnergistics
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package appeng.api.crafting;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.world.World;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.storage.data.IAEItemStack;
public interface ICraftingHelper {
/**
* Checks that the given item stack is an encoded pattern.
*/
boolean isEncodedPattern(@Nullable IAEItemStack item);
/**
* Checks that the given item stack is an encoded pattern.
*/
boolean isEncodedPattern(ItemStack item);
/**
* Encodes a processing pattern which represents the ability to convert the
* given inputs into the given outputs using some process external to the ME
* system.
*
* @param stack If null, a new item will be created to hold the encoded pattern.
* Otherwise the given item must already contains an encoded
* pattern that will be overwritten.
* @return A new encoded pattern, or the given stack with the pattern encoded in
* it.
*/
ItemStack encodeProcessingPattern(@Nullable ItemStack stack, ItemStack[] in, ItemStack[] out);
/**
* Encodes a crafting pattern which represents a Vanilla crafting recipe.
*
* @param stack If null, a new item will be created to hold the
* encoded pattern. Otherwise the given item must
* already contains an encoded pattern that will be
* overwritten.
* @param recipe The Vanilla crafting recipe to be encoded.
* @param in The items in the crafting grid, which are used to
* determine what items are supplied from the ME system
* to craft using this pattern.
* @param out What is to be expected as the result of this crafting
* operation by the ME system.
* @param allowSubstitutes Controls whether the ME system will allow the use of
* equivalent items to craft this recipe.
*/
ItemStack encodeCraftingPattern(@Nullable ItemStack stack, ICraftingRecipe recipe, ItemStack[] in, ItemStack out,
boolean allowSubstitutes);
/**
* Same as {@link #decodePattern(ItemStack, World, boolean)} with no auto
* recovery of changed recipe ids.
*/
@Nullable
default ICraftingPatternDetails decodePattern(@Nonnull ItemStack itemStack, @Nonnull World world) {
return decodePattern(itemStack, world, false);
}
/**
* Decodes an encoded crafting pattern and returns the pattern details.
* <p>
* The item backing the {@link ItemStack} needs to be an item returned by the
* encode methods of this class.
*
* @param itemStack pattern
* @param world world used to access the
* {@link net.minecraft.item.crafting.RecipeManager}.
* @param autoRecovery If true, the method will try to recover from changed
* recipe ids by searching the entire recipe manager for a
* recipe matching the inputs. If this is successful, the
* given item stack will be changed to reflect the new
* recipe id.
* @return The pattern details if the pattern could be decoded. Otherwise null.
*/
@Nullable
ICraftingPatternDetails decodePattern(@Nonnull ItemStack itemStack, @Nonnull World world, boolean autoRecovery);
}
@@ -1,46 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 AlgorithmX2
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package appeng.api.implementations;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import appeng.api.networking.crafting.ICraftingPatternDetails;
/**
* Implemented on {@link Item}
*/
public interface ICraftingPatternItem {
/**
* Access Details about a pattern
*
* @param is pattern
* @param w crafting world
*
* @return details of pattern
*/
ICraftingPatternDetails getPatternForItem(ItemStack is, World w);
}
@@ -27,18 +27,19 @@ import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.world.World; import net.minecraft.world.World;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
/** /**
* do not implement provided by {@link ICraftingPatternItem} * Describes a crafting or processing pattern decoded by
* * {@link appeng.api.crafting.ICraftingHelper}.
* caching this INSTANCE will increase performance of validation and checks. * <p>
* Do not cache instances of this class unless you handle recipe reloads on the
* server and client correctly.
*/ */
public interface ICraftingPatternDetails { public interface ICraftingPatternDetails {
/** /**
* @return source item. * @return encodes this crafting pattern into a new item stack.
*/ */
ItemStack getPattern(); ItemStack getPattern();
@@ -52,7 +53,9 @@ public interface ICraftingPatternDetails {
boolean isValidItemForSlot(int slotIndex, ItemStack itemStack, World world); boolean isValidItemForSlot(int slotIndex, ItemStack itemStack, World world);
/** /**
* @return if this pattern is a crafting pattern ( work bench ) * @return if this pattern is for a Vanilla
* {@link net.minecraft.item.crafting.IRecipeType#CRAFTING crafting
* recipe}.
*/ */
boolean isCraftable(); boolean isCraftable();
@@ -23,6 +23,7 @@
package appeng.api.networking.crafting; package appeng.api.networking.crafting;
import appeng.api.crafting.ICraftingHelper;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
/** /**
@@ -33,6 +34,9 @@ public interface ICraftingProviderHelper {
/** /**
* Add new Pattern to AE's crafting cache. * Add new Pattern to AE's crafting cache.
*
* This will only accept instances created by
* {@link ICraftingHelper#decodePattern(net.minecraft.item.ItemStack, net.minecraft.world.World)}
*/ */
void addCraftingOption(ICraftingMedium medium, ICraftingPatternDetails api); void addCraftingOption(ICraftingMedium medium, ICraftingPatternDetails api);
@@ -38,6 +38,7 @@ import appeng.container.slot.AppEngSlot;
import appeng.container.slot.MolecularAssemblerPatternSlot; import appeng.container.slot.MolecularAssemblerPatternSlot;
import appeng.container.slot.OutputSlot; import appeng.container.slot.OutputSlot;
import appeng.container.slot.RestrictedInputSlot; import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.items.misc.EncodedPatternItem; import appeng.items.misc.EncodedPatternItem;
import appeng.tile.crafting.MolecularAssemblerTileEntity; import appeng.tile.crafting.MolecularAssemblerTileEntity;
import appeng.util.Platform; import appeng.util.Platform;
@@ -79,8 +80,7 @@ public class MolecularAssemblerContainer extends UpgradeableContainer implements
if (is.getItem() instanceof EncodedPatternItem) { if (is.getItem() instanceof EncodedPatternItem) {
final World w = this.getTileEntity().getWorld(); final World w = this.getTileEntity().getWorld();
final EncodedPatternItem iep = (EncodedPatternItem) is.getItem(); final ICraftingPatternDetails ph = Api.instance().crafting().decodePattern(is, w);
final ICraftingPatternDetails ph = iep.getPatternForItem(is, w);
if (ph.isCraftable()) { if (ph.isCraftable()) {
return ph.isValidItemForSlot(slotIndex, i, w); return ph.isValidItemForSlot(slotIndex, i, w);
} }
@@ -20,7 +20,6 @@ package appeng.container.implementations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
@@ -32,11 +31,9 @@ import net.minecraft.inventory.container.CraftingResultSlot;
import net.minecraft.inventory.container.IContainerListener; import net.minecraft.inventory.container.IContainerListener;
import net.minecraft.inventory.container.Slot; import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType; import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
@@ -44,6 +41,7 @@ import net.minecraftforge.items.wrapper.PlayerInvWrapper;
import appeng.api.config.Actionable; import appeng.api.config.Actionable;
import appeng.api.config.SecurityPermissions; import appeng.api.config.SecurityPermissions;
import appeng.api.crafting.ICraftingHelper;
import appeng.api.definitions.IDefinitions; import appeng.api.definitions.IDefinitions;
import appeng.api.storage.IMEMonitor; import appeng.api.storage.IMEMonitor;
import appeng.api.storage.ITerminalHost; import appeng.api.storage.ITerminalHost;
@@ -98,8 +96,9 @@ public class PatternTermContainer extends MEMonitorableContainer
private final PatternTermSlot craftSlot; private final PatternTermSlot craftSlot;
private final RestrictedInputSlot patternSlotIN; private final RestrictedInputSlot patternSlotIN;
private final RestrictedInputSlot patternSlotOUT; private final RestrictedInputSlot patternSlotOUT;
private final ICraftingHelper craftingHelper = Api.INSTANCE.crafting();
private IRecipe<CraftingInventory> currentRecipe; private ICraftingRecipe currentRecipe;
@GuiSync(97) @GuiSync(97)
public boolean craftingMode = true; public boolean craftingMode = true;
@GuiSync(96) @GuiSync(96)
@@ -206,17 +205,17 @@ public class PatternTermContainer extends MEMonitorableContainer
final ItemStack[] out = this.getOutputs(); final ItemStack[] out = this.getOutputs();
// if there is no input, this would be silly. // if there is no input, this would be silly.
if (in == null || out == null) { if (in == null || out == null || isCraftingMode() && currentRecipe == null) {
return; return;
} }
// first check the output slots, should either be null, or a pattern // first check the output slots, should either be null, or a pattern
if (!output.isEmpty() && !this.isPattern(output)) { if (!output.isEmpty() && !craftingHelper.isEncodedPattern(output)) {
return; return;
} // if nothing is there we should snag a new pattern. } // if nothing is there we should snag a new pattern.
else if (output.isEmpty()) { else if (output.isEmpty()) {
output = this.patternSlotIN.getStack(); output = this.patternSlotIN.getStack();
if (output.isEmpty() || !this.isPattern(output)) { if (output.isEmpty() || !isPattern(output)) {
return; // no blanks. return; // no blanks.
} }
@@ -226,34 +225,17 @@ public class PatternTermContainer extends MEMonitorableContainer
this.patternSlotIN.putStack(ItemStack.EMPTY); this.patternSlotIN.putStack(ItemStack.EMPTY);
} }
// add a new encoded pattern. // let the crafting helper create a new encoded pattern
Optional<ItemStack> maybePattern = Api.instance().definitions().items().encodedPattern().maybeStack(1); output = null;
if (maybePattern.isPresent()) {
output = maybePattern.get();
this.patternSlotOUT.putStack(output);
}
} }
// encode the slot. if (this.isCraftingMode()) {
final CompoundNBT encodedValue = new CompoundNBT(); output = craftingHelper.encodeCraftingPattern(output, this.currentRecipe, in, out[0], isSubstitute());
} else {
final ListNBT tagIn = new ListNBT(); output = craftingHelper.encodeProcessingPattern(output, in, out);
final ListNBT tagOut = new ListNBT();
for (final ItemStack i : in) {
tagIn.add(this.createItemTag(i));
} }
this.patternSlotOUT.putStack(output);
for (final ItemStack i : out) {
tagOut.add(this.createItemTag(i));
}
encodedValue.put("in", tagIn);
encodedValue.put("out", tagOut);
encodedValue.putBoolean("crafting", this.isCraftingMode());
encodedValue.putBoolean("substitute", this.isSubstitute());
output.setTag(encodedValue);
} }
private ItemStack[] getInputs() { private ItemStack[] getInputs() {
@@ -308,21 +290,7 @@ public class PatternTermContainer extends MEMonitorableContainer
} }
final IDefinitions definitions = Api.instance().definitions(); final IDefinitions definitions = Api.instance().definitions();
return definitions.materials().blankPattern().isSameAs(output);
boolean isPattern = definitions.items().encodedPattern().isSameAs(output);
isPattern |= definitions.materials().blankPattern().isSameAs(output);
return isPattern;
}
private INBT createItemTag(final ItemStack i) {
final CompoundNBT c = new CompoundNBT();
if (!i.isEmpty()) {
i.write(c);
}
return c;
} }
@Override @Override
@@ -33,11 +33,11 @@ import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import appeng.api.crafting.ICraftingHelper;
import appeng.api.definitions.IDefinitions; import appeng.api.definitions.IDefinitions;
import appeng.api.definitions.IItems; import appeng.api.definitions.IItems;
import appeng.api.definitions.IMaterials; import appeng.api.definitions.IMaterials;
import appeng.api.features.INetworkEncodable; import appeng.api.features.INetworkEncodable;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.implementations.items.IBiometricCard; import appeng.api.implementations.items.IBiometricCard;
import appeng.api.implementations.items.ISpatialStorageCell; import appeng.api.implementations.items.ISpatialStorageCell;
import appeng.api.implementations.items.IStorageComponent; import appeng.api.implementations.items.IStorageComponent;
@@ -88,10 +88,7 @@ public class RestrictedInputSlot extends AppEngSlot {
public boolean isValid(final ItemStack is, final World theWorld) { public boolean isValid(final ItemStack is, final World theWorld) {
if (this.which == PlacableItemType.VALID_ENCODED_PATTERN_W_OUTPUT) { if (this.which == PlacableItemType.VALID_ENCODED_PATTERN_W_OUTPUT) {
final ICraftingPatternDetails ap = is.getItem() instanceof ICraftingPatternItem return Api.instance().crafting().decodePattern(is, theWorld) != null;
? ((ICraftingPatternItem) is.getItem()).getPatternForItem(is, theWorld)
: null;
return ap != null;
} }
return true; return true;
} }
@@ -126,39 +123,24 @@ public class RestrictedInputSlot extends AppEngSlot {
final IDefinitions definitions = Api.instance().definitions(); final IDefinitions definitions = Api.instance().definitions();
final IMaterials materials = definitions.materials(); final IMaterials materials = definitions.materials();
final IItems items = definitions.items(); final IItems items = definitions.items();
final ICraftingHelper crafting = Api.instance().crafting();
switch (this.which) { switch (this.which) {
case ENCODED_CRAFTING_PATTERN: case ENCODED_CRAFTING_PATTERN:
if (i.getItem() instanceof ICraftingPatternItem) { final ICraftingPatternDetails de = crafting.decodePattern(i, this.p.player.world);
final ICraftingPatternItem b = (ICraftingPatternItem) i.getItem(); if (de != null) {
final ICraftingPatternDetails de = b.getPatternForItem(i, this.p.player.world); return de.isCraftable();
if (de != null) {
return de.isCraftable();
}
} }
return false; return false;
case VALID_ENCODED_PATTERN_W_OUTPUT: case VALID_ENCODED_PATTERN_W_OUTPUT:
case ENCODED_PATTERN_W_OUTPUT: case ENCODED_PATTERN_W_OUTPUT:
case ENCODED_PATTERN: { case ENCODED_PATTERN:
if (i.getItem() instanceof ICraftingPatternItem) { return crafting.isEncodedPattern(i);
return true;
}
// ICraftingPatternDetails pattern = i.getItem() instanceof ICraftingPatternItem
// ?
// ((ICraftingPatternItem)
// i.getItem()).getPatternForItem( i ) : null;
return false;// pattern != null;
}
case BLANK_PATTERN: case BLANK_PATTERN:
return materials.blankPattern().isSameAs(i); return materials.blankPattern().isSameAs(i);
case PATTERN: case PATTERN:
return materials.blankPattern().isSameAs(i) || crafting.isEncodedPattern(i);
if (i.getItem() instanceof ICraftingPatternItem) {
return true;
}
return materials.blankPattern().isSameAs(i);
case INSCRIBER_PLATE: case INSCRIBER_PLATE:
if (materials.namePress().isSameAs(i)) { if (materials.namePress().isSameAs(i)) {
+9
View File
@@ -21,10 +21,12 @@ package appeng.core;
import appeng.api.AEAddon; import appeng.api.AEAddon;
import appeng.api.IAppEngApi; import appeng.api.IAppEngApi;
import appeng.api.client.IClientHelper; import appeng.api.client.IClientHelper;
import appeng.api.crafting.ICraftingHelper;
import appeng.api.features.IRegistryContainer; import appeng.api.features.IRegistryContainer;
import appeng.api.networking.IGridHelper; import appeng.api.networking.IGridHelper;
import appeng.api.storage.IStorageHelper; import appeng.api.storage.IStorageHelper;
import appeng.core.api.ApiClientHelper; import appeng.core.api.ApiClientHelper;
import appeng.core.api.ApiCrafting;
import appeng.core.api.ApiGrid; import appeng.core.api.ApiGrid;
import appeng.core.api.ApiPart; import appeng.core.api.ApiPart;
import appeng.core.api.ApiStorage; import appeng.core.api.ApiStorage;
@@ -59,6 +61,7 @@ public final class Api implements IAppEngApi {
private final IStorageHelper storageHelper; private final IStorageHelper storageHelper;
private final IGridHelper networkHelper; private final IGridHelper networkHelper;
private final ApiDefinitions definitions; private final ApiDefinitions definitions;
private final ICraftingHelper craftingHelper;
private final IClientHelper client; private final IClientHelper client;
private Api() { private Api() {
@@ -67,6 +70,7 @@ public final class Api implements IAppEngApi {
this.registryContainer = new RegistryContainer(); this.registryContainer = new RegistryContainer();
this.partHelper = new ApiPart(); this.partHelper = new ApiPart();
this.definitions = new ApiDefinitions((PartModels) this.registryContainer.partModels()); this.definitions = new ApiDefinitions((PartModels) this.registryContainer.partModels());
this.craftingHelper = new ApiCrafting(this.definitions);
this.client = new ApiClientHelper(this.definitions); this.client = new ApiClientHelper(this.definitions);
} }
@@ -84,6 +88,11 @@ public final class Api implements IAppEngApi {
return this.storageHelper; return this.storageHelper;
} }
@Override
public ICraftingHelper crafting() {
return this.craftingHelper;
}
@Override @Override
public IGridHelper grid() { public IGridHelper grid() {
return this.networkHelper; return this.networkHelper;
@@ -0,0 +1,167 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, TeamAppliedEnergistics, 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.core.api;
import java.util.List;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.RecipeManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import appeng.api.crafting.ICraftingHelper;
import appeng.api.definitions.IItemDefinition;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.container.ContainerNull;
import appeng.core.AELog;
import appeng.core.Api;
import appeng.core.ApiDefinitions;
import appeng.helpers.CraftingPatternDetails;
import appeng.items.misc.EncodedPatternItem;
public class ApiCrafting implements ICraftingHelper {
private final IItemDefinition encodedPattern;
public ApiCrafting(ApiDefinitions definitions) {
this.encodedPattern = definitions.items().encodedPattern();
}
@Override
public boolean isEncodedPattern(@Nullable IAEItemStack item) {
return item != null && item.getItem() instanceof EncodedPatternItem;
}
@Override
public boolean isEncodedPattern(ItemStack item) {
return !item.isEmpty() && item.getItem() instanceof EncodedPatternItem;
}
@Override
public ItemStack encodeCraftingPattern(@Nullable ItemStack stack, ICraftingRecipe recipe, ItemStack[] in,
ItemStack out, boolean allowSubstitutes) {
if (stack == null) {
stack = encodedPattern.stack(1);
} else {
Preconditions.checkArgument(isEncodedPattern(stack));
}
EncodedPatternItem.encodeCraftingPattern(stack, in, new ItemStack[] { out }, recipe.getId(), allowSubstitutes);
return stack;
}
@Override
public ItemStack encodeProcessingPattern(@Nullable ItemStack stack, ItemStack[] in, ItemStack[] out) {
if (stack == null) {
stack = encodedPattern.stack(1);
} else {
Preconditions.checkArgument(isEncodedPattern(stack));
}
EncodedPatternItem.encodeProcessingPattern(stack, in, out);
return stack;
}
@Override
public ICraftingPatternDetails decodePattern(final ItemStack is, final World world, boolean autoRecovery) {
if (is == null || world == null) {
return null;
}
EncodedPatternItem patternItem = getPatternItem(is);
if (patternItem == null || !patternItem.isEncodedPattern(is)) {
return null;
}
// The recipe ids encoded in a pattern can go stale. This code attempts to find
// the new id
// based on the stored inputs/outputs if that happens.
ResourceLocation recipeId = patternItem.getCraftingRecipeId(is);
if (recipeId != null) {
IRecipe<?> recipe = world.getRecipeManager().getRecipes(IRecipeType.CRAFTING).get(recipeId);
if (!(recipe instanceof ICraftingRecipe)) {
if (!autoRecovery || !attemptRecovery(patternItem, is, world)) {
return null;
}
}
}
// We use the shared itemstack for an identity lookup.
IAEItemStack ais = Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(is);
return new CraftingPatternDetails(ais, world);
}
private boolean attemptRecovery(EncodedPatternItem patternItem, ItemStack itemStack, World world) {
RecipeManager recipeManager = world.getRecipeManager();
List<IAEItemStack> ingredients = patternItem.getIngredients(itemStack);
List<IAEItemStack> products = patternItem.getProducts(itemStack);
if (ingredients.size() < 9 || products.size() < 1) {
return false;
}
ResourceLocation currentRecipeId = patternItem.getCraftingRecipeId(itemStack);
// Fill a crafting inventory with the ingredients to find a suitable recipe
CraftingInventory testInventory = new CraftingInventory(new ContainerNull(), 3, 3);
for (int x = 0; x < 9; x++) {
final IAEItemStack ais = ingredients.get(x);
final ItemStack gs = ais != null ? ais.createItemStack() : ItemStack.EMPTY;
testInventory.setInventorySlotContents(x, gs);
}
ICraftingRecipe potentialRecipe = recipeManager.getRecipe(IRecipeType.CRAFTING, testInventory, world)
.orElse(null);
if (potentialRecipe != null) {
// Check that it matches the expected output
if (products.get(0).isSameType(potentialRecipe.getCraftingResult(testInventory))) {
// Yay we found a match, reencode the pattern
AELog.debug("Re-Encoding pattern from %s -> %s", currentRecipeId, potentialRecipe.getId());
ItemStack[] in = ingredients.stream().map(ais -> ais != null ? ais.createItemStack() : ItemStack.EMPTY)
.toArray(ItemStack[]::new);
ItemStack out = products.get(0).createItemStack();
encodeCraftingPattern(itemStack, potentialRecipe, in, out, patternItem.allowsSubstitution(itemStack));
}
}
AELog.debug("Failed to recover encoded crafting pattern for recipe %s", currentRecipeId);
return false;
}
private static EncodedPatternItem getPatternItem(ItemStack itemStack) {
if (itemStack.getItem() instanceof EncodedPatternItem) {
return (EncodedPatternItem) itemStack.getItem();
}
return null;
}
}
@@ -101,7 +101,7 @@ public class CraftRequestPacket extends BasePacket {
if (futureJob != null) { if (futureJob != null) {
futureJob.cancel(true); futureJob.cancel(true);
} }
AELog.debug(e); AELog.info(e);
} }
} }
} }
@@ -25,13 +25,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.google.common.base.Preconditions;
import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ICraftingRecipe; import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType; import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.ResourceLocation;
import net.minecraft.nbt.ListNBT;
import net.minecraft.world.World; import net.minecraft.world.World;
import appeng.api.networking.crafting.ICraftingPatternDetails; import appeng.api.networking.crafting.ICraftingPatternDetails;
@@ -39,12 +41,11 @@ import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
import appeng.container.ContainerNull; import appeng.container.ContainerNull;
import appeng.core.Api; import appeng.core.Api;
import appeng.items.misc.EncodedPatternItem;
import appeng.util.Platform; import appeng.util.Platform;
import appeng.util.item.AEItemStack;
public class PatternHelper implements ICraftingPatternDetails, Comparable<PatternHelper> { public class CraftingPatternDetails implements ICraftingPatternDetails, Comparable<CraftingPatternDetails> {
private final ItemStack patternItem;
private final CraftingInventory crafting = new CraftingInventory(new ContainerNull(), 3, 3); private final CraftingInventory crafting = new CraftingInventory(new ContainerNull(), 3, 3);
private final CraftingInventory testFrame = new CraftingInventory(new ContainerNull(), 3, 3); private final CraftingInventory testFrame = new CraftingInventory(new ContainerNull(), 3, 3);
private final ItemStack correctOutput; private final ItemStack correctOutput;
@@ -53,79 +54,73 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
private final IAEItemStack[] condensedOutputs; private final IAEItemStack[] condensedOutputs;
private final IAEItemStack[] inputs; private final IAEItemStack[] inputs;
private final IAEItemStack[] outputs; private final IAEItemStack[] outputs;
private final boolean isCrafting; private final boolean isCraftable;
private final boolean canSubstitute; private final boolean canSubstitute;
private final Set<TestLookup> failCache = new HashSet<>(); private final Set<TestLookup> failCache = new HashSet<>();
private final Set<TestLookup> passCache = new HashSet<>(); private final Set<TestLookup> passCache = new HashSet<>();
private final IAEItemStack pattern; private final IAEItemStack pattern;
private int priority = 0; private int priority = 0;
public PatternHelper(final ItemStack is, final World w) { public CraftingPatternDetails(final IAEItemStack is, final World w) {
final CompoundNBT encodedValue = is.getTag(); Preconditions.checkArgument(is.getItem() instanceof EncodedPatternItem,
"itemStack is not a ICraftingPatternItem");
if (encodedValue == null) { final EncodedPatternItem templateItem = (EncodedPatternItem) is.getItem();
throw new IllegalArgumentException("No pattern here!"); final ItemStack itemStack = is.createItemStack();
}
final ListNBT inTag = encodedValue.getList("in", 10); final List<IAEItemStack> ingredients = templateItem.getIngredients(itemStack);
final ListNBT outTag = encodedValue.getList("out", 10); final List<IAEItemStack> products = templateItem.getProducts(itemStack);
this.isCrafting = encodedValue.getBoolean("crafting"); final ResourceLocation recipeId = templateItem.getCraftingRecipeId(itemStack);
this.canSubstitute = this.isCrafting && encodedValue.getBoolean("substitute"); this.pattern = is.copy();
this.patternItem = is; this.isCraftable = recipeId != null;
this.pattern = AEItemStack.fromItemStack(is); this.canSubstitute = templateItem.allowsSubstitution(itemStack);
final List<IAEItemStack> in = new ArrayList<>(); final List<IAEItemStack> in = new ArrayList<>();
final List<IAEItemStack> out = new ArrayList<>(); final List<IAEItemStack> out = new ArrayList<>();
for (int x = 0; x < inTag.size(); x++) { for (int x = 0; x < 9; x++) {
CompoundNBT ingredient = inTag.getCompound(x); final IAEItemStack ais = ingredients.get(x);
final ItemStack gs = ItemStack.read(ingredient); final ItemStack gs = ais != null ? ais.createItemStack() : ItemStack.EMPTY;
if (!ingredient.isEmpty() && gs.isEmpty()) {
throw new IllegalArgumentException("No pattern here!");
}
this.crafting.setInventorySlotContents(x, gs); this.crafting.setInventorySlotContents(x, gs);
if (!gs.isEmpty() && (!this.isCrafting || !gs.hasTag())) { if (!gs.isEmpty() && (!this.isCraftable) || !gs.hasTag()) {
this.markItemAs(x, gs, TestStatus.ACCEPT); this.markItemAs(x, gs, TestStatus.ACCEPT);
} }
in.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(gs)); in.add(ais != null ? ais.copy() : null);
this.testFrame.setInventorySlotContents(x, gs); this.testFrame.setInventorySlotContents(x, gs);
} }
if (this.isCrafting) { if (this.isCraftable) {
this.standardRecipe = w.getRecipeManager().getRecipe(IRecipeType.CRAFTING, this.crafting, w).orElse(null); IRecipe<?> recipe = w.getRecipeManager().getRecipes(IRecipeType.CRAFTING).get(recipeId);
if (this.standardRecipe != null) { if (recipe == null || recipe.getType() != IRecipeType.CRAFTING) {
this.correctOutput = this.standardRecipe.getCraftingResult(this.crafting); throw new IllegalStateException("recipe id is not a crafting recipe");
out.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class)
.createStack(this.correctOutput));
} else {
throw new IllegalStateException("No pattern here!");
} }
this.standardRecipe = (ICraftingRecipe) recipe;
this.correctOutput = this.standardRecipe.getCraftingResult(this.crafting);
out.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class)
.createStack(this.correctOutput));
} else { } else {
this.standardRecipe = null; this.standardRecipe = null;
this.correctOutput = ItemStack.EMPTY; this.correctOutput = ItemStack.EMPTY;
for (int x = 0; x < outTag.size(); x++) { for (int x = 0; x < 3; x++) {
CompoundNBT resultItemTag = outTag.getCompound(x); final IAEItemStack ais = products.get(x);
final ItemStack gs = ItemStack.read(resultItemTag); final ItemStack gs = ais.createItemStack();
if (!resultItemTag.isEmpty() && gs.isEmpty()) {
throw new IllegalArgumentException("No pattern here!");
}
if (!gs.isEmpty()) { if (!gs.isEmpty()) {
out.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(gs)); out.add(ais.copy());
} }
} }
} }
this.outputs = out.toArray(new IAEItemStack[0]);
this.inputs = in.toArray(new IAEItemStack[0]); this.inputs = in.toArray(new IAEItemStack[0]);
this.outputs = out.toArray(new IAEItemStack[0]);
final Map<IAEItemStack, IAEItemStack> tmpOutputs = new HashMap<>(); final Map<IAEItemStack, IAEItemStack> tmpOutputs = new HashMap<>();
@@ -190,12 +185,12 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
@Override @Override
public ItemStack getPattern() { public ItemStack getPattern() {
return this.patternItem; return this.pattern.createItemStack();
} }
@Override @Override
public synchronized boolean isValidItemForSlot(final int slotIndex, final ItemStack i, final World w) { public synchronized boolean isValidItemForSlot(final int slotIndex, final ItemStack i, final World w) {
if (!this.isCrafting) { if (!this.isCraftable) {
throw new IllegalStateException("Only crafting recipes supported."); throw new IllegalStateException("Only crafting recipes supported.");
} }
@@ -241,7 +236,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
@Override @Override
public boolean isCraftable() { public boolean isCraftable() {
return this.isCrafting; return this.isCraftable;
} }
@Override @Override
@@ -271,7 +266,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
@Override @Override
public ItemStack getOutput(final CraftingInventory craftingInv, final World w) { public ItemStack getOutput(final CraftingInventory craftingInv, final World w) {
if (!this.isCrafting) { if (!this.isCraftable) {
throw new IllegalStateException("Only crafting recipes supported."); throw new IllegalStateException("Only crafting recipes supported.");
} }
@@ -323,7 +318,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
} }
@Override @Override
public int compareTo(final PatternHelper o) { public int compareTo(final CraftingPatternDetails o) {
return Integer.compare(o.priority, this.priority); return Integer.compare(o.priority, this.priority);
} }
@@ -341,7 +336,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
return false; return false;
} }
final PatternHelper other = (PatternHelper) obj; final CraftingPatternDetails other = (CraftingPatternDetails) obj;
if (this.pattern != null && other.pattern != null) { if (this.pattern != null && other.pattern != null) {
return this.pattern.equals(other.pattern); return this.pattern.equals(other.pattern);
@@ -57,7 +57,6 @@ import appeng.api.config.Actionable;
import appeng.api.config.Settings; import appeng.api.config.Settings;
import appeng.api.config.Upgrades; import appeng.api.config.Upgrades;
import appeng.api.config.YesNo; import appeng.api.config.YesNo;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.implementations.IUpgradeableHost; import appeng.api.implementations.IUpgradeableHost;
import appeng.api.implementations.tiles.ICraftingMachine; import appeng.api.implementations.tiles.ICraftingMachine;
import appeng.api.networking.GridFlags; import appeng.api.networking.GridFlags;
@@ -407,21 +406,15 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
} }
private void addToCraftingList(final ItemStack is) { private void addToCraftingList(final ItemStack is) {
if (is.isEmpty()) { final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(is,
return; this.iHost.getTileEntity().getWorld());
}
if (is.getItem() instanceof ICraftingPatternItem) { if (details != null) {
final ICraftingPatternItem cpi = (ICraftingPatternItem) is.getItem(); if (this.craftingList == null) {
final ICraftingPatternDetails details = cpi.getPatternForItem(is, this.iHost.getTileEntity().getWorld()); this.craftingList = new ArrayList<>();
if (details != null) {
if (this.craftingList == null) {
this.craftingList = new ArrayList<>();
}
this.craftingList.add(details);
} }
this.craftingList.add(details);
} }
} }
@@ -18,37 +18,50 @@
package appeng.items.misc; package appeng.items.misc;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import com.google.common.base.Preconditions;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext; import net.minecraft.item.ItemUseContext;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType; import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.networking.crafting.ICraftingPatternDetails; import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
import appeng.core.Api; import appeng.core.Api;
import appeng.core.AppEng; import appeng.core.AppEng;
import appeng.core.localization.GuiText; import appeng.core.localization.GuiText;
import appeng.helpers.InvalidPatternHelper; import appeng.helpers.InvalidPatternHelper;
import appeng.helpers.PatternHelper;
import appeng.items.AEBaseItem; import appeng.items.AEBaseItem;
import appeng.util.Platform; import appeng.util.Platform;
public class EncodedPatternItem extends AEBaseItem implements ICraftingPatternItem { public class EncodedPatternItem extends AEBaseItem {
public static final String NBT_INGREDIENTS = "in";
public static final String NBT_PRODUCTS = "out";
public static final String NBT_SUBSITUTE = "substitute";
public static final String NBT_RECIPE_ID = "recipe";
// rather simple client side caching. // rather simple client side caching.
private static final Map<ItemStack, ItemStack> SIMPLE_CACHE = new WeakHashMap<>(); private static final Map<ItemStack, ItemStack> SIMPLE_CACHE = new WeakHashMap<>();
@@ -95,7 +108,7 @@ public class EncodedPatternItem extends AEBaseItem implements ICraftingPatternIt
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public void addInformation(final ItemStack stack, final World world, final List<ITextComponent> lines, public void addInformation(final ItemStack stack, final World world, final List<ITextComponent> lines,
final ITooltipFlag advancedTooltips) { final ITooltipFlag advancedTooltips) {
final ICraftingPatternDetails details = this.getPatternForItem(stack, world); final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(stack, world);
if (details == null) { if (details == null) {
if (!stack.hasTag()) { if (!stack.hasTag()) {
@@ -181,15 +194,6 @@ public class EncodedPatternItem extends AEBaseItem implements ICraftingPatternIt
} }
} }
@Override
public ICraftingPatternDetails getPatternForItem(final ItemStack is, final World w) {
try {
return new PatternHelper(is, w);
} catch (final Throwable t) {
return null;
}
}
public ItemStack getOutput(final ItemStack item) { public ItemStack getOutput(final ItemStack item) {
ItemStack out = SIMPLE_CACHE.get(item); ItemStack out = SIMPLE_CACHE.get(item);
@@ -202,11 +206,127 @@ public class EncodedPatternItem extends AEBaseItem implements ICraftingPatternIt
return ItemStack.EMPTY; return ItemStack.EMPTY;
} }
final ICraftingPatternDetails details = this.getPatternForItem(item, w); final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(item, w);
out = details != null ? details.getOutputs()[0].createItemStack() : ItemStack.EMPTY; out = details != null ? details.getOutputs()[0].createItemStack() : ItemStack.EMPTY;
SIMPLE_CACHE.put(item, out); SIMPLE_CACHE.put(item, out);
return out; return out;
} }
public boolean isEncodedPattern(ItemStack itemStack) {
return itemStack != null && !itemStack.isEmpty() && itemStack.getItem() == this && itemStack.getTag() != null
&& itemStack.getTag().contains(NBT_INGREDIENTS, Constants.NBT.TAG_LIST)
&& itemStack.getTag().contains(NBT_PRODUCTS, Constants.NBT.TAG_LIST);
}
public ResourceLocation getCraftingRecipeId(ItemStack itemStack) {
Preconditions.checkArgument(itemStack.getItem() == this, "Given item stack %s is not an encoded pattern.",
itemStack);
final CompoundNBT tag = itemStack.getTag();
Preconditions.checkArgument(tag != null, "itemStack missing a NBT tag");
return new ResourceLocation(tag.getString(NBT_RECIPE_ID));
}
public List<IAEItemStack> getIngredients(ItemStack itemStack) {
Preconditions.checkArgument(itemStack.getItem() == this, "Given item stack %s is not an encoded pattern.",
itemStack);
final CompoundNBT tag = itemStack.getTag();
Preconditions.checkArgument(tag != null, "itemStack missing a NBT tag");
final ListNBT inTag = tag.getList(NBT_INGREDIENTS, 10);
Preconditions.checkArgument(inTag.size() < 10, "Cannot use more than 9 ingredients");
final List<IAEItemStack> in = new ArrayList<>(inTag.size());
for (int x = 0; x < inTag.size(); x++) {
CompoundNBT ingredient = inTag.getCompound(x);
final ItemStack gs = ItemStack.read(ingredient);
Preconditions.checkArgument(!(!ingredient.isEmpty() && gs.isEmpty()), "invalid itemStack in slot", x);
in.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(gs));
}
return in;
}
public List<IAEItemStack> getProducts(ItemStack itemStack) {
Preconditions.checkArgument(itemStack.getItem() == this, "Given item stack %s is not an encoded pattern.",
itemStack);
final CompoundNBT tag = itemStack.getTag();
Preconditions.checkArgument(tag != null, "itemStack missing a NBT tag");
final ListNBT outTag = tag.getList(NBT_PRODUCTS, 10);
Preconditions.checkArgument(outTag.size() < 4, "Cannot use more than 3 ingredients");
final List<IAEItemStack> out = new ArrayList<>(outTag.size());
for (int x = 0; x < outTag.size(); x++) {
CompoundNBT ingredient = outTag.getCompound(x);
final ItemStack gs = ItemStack.read(ingredient);
Preconditions.checkArgument(!(!ingredient.isEmpty() && gs.isEmpty()), "invalid itemStack in slot", x);
out.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(gs));
}
return out;
}
public boolean allowsSubstitution(ItemStack itemStack) {
final CompoundNBT tag = itemStack.getTag();
Preconditions.checkArgument(tag != null, "itemStack missing a NBT tag");
return getCraftingRecipeId(itemStack) == null && tag.getBoolean(NBT_SUBSITUTE);
}
/**
* Use the public API instead {@link appeng.core.api.ApiCrafting}
*/
public static void encodeCraftingPattern(ItemStack stack, ItemStack[] in, ItemStack[] out,
ResourceLocation recipeId, boolean allowSubstitutes) {
CompoundNBT encodedValue = encodeInputsAndOutputs(in, out);
encodedValue.putString(EncodedPatternItem.NBT_RECIPE_ID, recipeId.toString());
encodedValue.putBoolean(EncodedPatternItem.NBT_SUBSITUTE, allowSubstitutes);
stack.setTag(encodedValue);
}
/**
* Use the public API instead {@link appeng.core.api.ApiCrafting}
*/
public static void encodeProcessingPattern(ItemStack stack, ItemStack[] in, ItemStack[] out) {
stack.setTag(encodeInputsAndOutputs(in, out));
}
private static CompoundNBT encodeInputsAndOutputs(ItemStack[] in, ItemStack[] out) {
final CompoundNBT encodedValue = new CompoundNBT();
final ListNBT tagIn = new ListNBT();
final ListNBT tagOut = new ListNBT();
for (final ItemStack i : in) {
tagIn.add(createItemTag(i));
}
for (final ItemStack i : out) {
tagOut.add(createItemTag(i));
}
encodedValue.put(EncodedPatternItem.NBT_INGREDIENTS, tagIn);
encodedValue.put(EncodedPatternItem.NBT_PRODUCTS, tagOut);
return encodedValue;
}
private static INBT createItemTag(final ItemStack i) {
final CompoundNBT c = new CompoundNBT();
if (!i.isEmpty()) {
i.write(c);
}
return c;
}
} }
+4
View File
@@ -35,6 +35,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -80,6 +81,7 @@ import appeng.crafting.CraftingJob;
import appeng.crafting.CraftingLink; import appeng.crafting.CraftingLink;
import appeng.crafting.CraftingLinkNexus; import appeng.crafting.CraftingLinkNexus;
import appeng.crafting.CraftingWatcher; import appeng.crafting.CraftingWatcher;
import appeng.helpers.CraftingPatternDetails;
import appeng.me.cluster.implementations.CraftingCPUCluster; import appeng.me.cluster.implementations.CraftingCPUCluster;
import appeng.me.helpers.BaseActionSource; import appeng.me.helpers.BaseActionSource;
import appeng.me.helpers.GenericInterestManager; import appeng.me.helpers.GenericInterestManager;
@@ -303,6 +305,8 @@ public class CraftingGridCache
@Override @Override
public void addCraftingOption(final ICraftingMedium medium, final ICraftingPatternDetails api) { public void addCraftingOption(final ICraftingMedium medium, final ICraftingPatternDetails api) {
Preconditions.checkArgument(api.getClass() == CraftingPatternDetails.class,
"Only supports internal ICraftingPatternDetails for now");
List<ICraftingMedium> details = this.craftingMethods.get(api); List<ICraftingMedium> details = this.craftingMethods.get(api);
if (details == null) { if (details == null) {
details = new ArrayList<>(); details = new ArrayList<>();
@@ -40,7 +40,7 @@ import net.minecraftforge.fml.hooks.BasicEventHooks;
import appeng.api.config.Actionable; import appeng.api.config.Actionable;
import appeng.api.config.FuzzyMode; import appeng.api.config.FuzzyMode;
import appeng.api.config.PowerMultiplier; import appeng.api.config.PowerMultiplier;
import appeng.api.implementations.ICraftingPatternItem; import appeng.api.crafting.ICraftingHelper;
import appeng.api.networking.IGrid; import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost; import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode; import appeng.api.networking.IGridNode;
@@ -1056,9 +1056,9 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU {
for (int x = 0; x < list.size(); x++) { for (int x = 0; x < list.size(); x++) {
final CompoundNBT item = list.getCompound(x); final CompoundNBT item = list.getCompound(x);
final IAEItemStack pattern = AEItemStack.fromNBT(item); final IAEItemStack pattern = AEItemStack.fromNBT(item);
if (pattern != null && pattern.getItem() instanceof ICraftingPatternItem) { ICraftingHelper craftingHelper = Api.instance().crafting();
final ICraftingPatternItem cpi = (ICraftingPatternItem) pattern.getItem(); if (craftingHelper.isEncodedPattern(pattern)) {
final ICraftingPatternDetails details = cpi.getPatternForItem(pattern.createItemStack(), final ICraftingPatternDetails details = craftingHelper.decodePattern(pattern.createItemStack(),
this.getWorld()); this.getWorld());
if (details != null) { if (details != null) {
final TaskProgress tp = new TaskProgress(); final TaskProgress tp = new TaskProgress();
@@ -28,12 +28,12 @@ import net.minecraft.util.ResourceLocation;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import appeng.api.config.SecurityPermissions; import appeng.api.config.SecurityPermissions;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.networking.crafting.ICraftingPatternDetails; import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.parts.IPartModel; import appeng.api.parts.IPartModel;
import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEItemStack;
import appeng.container.implementations.MEMonitorableContainer; import appeng.container.implementations.MEMonitorableContainer;
import appeng.container.implementations.PatternTermContainer; import appeng.container.implementations.PatternTermContainer;
import appeng.core.Api;
import appeng.core.AppEng; import appeng.core.AppEng;
import appeng.helpers.Reflected; import appeng.helpers.Reflected;
import appeng.items.parts.PartModels; import appeng.items.parts.PartModels;
@@ -107,23 +107,20 @@ public class PatternTerminalPart extends AbstractTerminalPart {
final ItemStack removedStack, final ItemStack newStack) { final ItemStack removedStack, final ItemStack newStack) {
if (inv == this.pattern && slot == 1) { if (inv == this.pattern && slot == 1) {
final ItemStack is = this.pattern.getStackInSlot(1); final ItemStack is = this.pattern.getStackInSlot(1);
if (!is.isEmpty() && is.getItem() instanceof ICraftingPatternItem) { final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(is,
final ICraftingPatternItem pattern = (ICraftingPatternItem) is.getItem(); this.getHost().getTile().getWorld());
final ICraftingPatternDetails details = pattern.getPatternForItem(is, if (details != null) {
this.getHost().getTile().getWorld()); this.setCraftingRecipe(details.isCraftable());
if (details != null) { this.setSubstitution(details.canSubstitute());
this.setCraftingRecipe(details.isCraftable());
this.setSubstitution(details.canSubstitute());
for (int x = 0; x < this.crafting.getSlots() && x < details.getInputs().length; x++) { for (int x = 0; x < this.crafting.getSlots() && x < details.getInputs().length; x++) {
final IAEItemStack item = details.getInputs()[x]; final IAEItemStack item = details.getInputs()[x];
this.crafting.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack()); this.crafting.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack());
} }
for (int x = 0; x < this.output.getSlots() && x < details.getOutputs().length; x++) { for (int x = 0; x < this.output.getSlots() && x < details.getOutputs().length; x++) {
final IAEItemStack item = details.getOutputs()[x]; final IAEItemStack item = details.getOutputs()[x];
this.output.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack()); this.output.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack());
}
} }
} }
} else if (inv == this.crafting) { } else if (inv == this.crafting) {
@@ -226,8 +226,7 @@ public class MolecularAssemblerTileEntity extends AENetworkInvTileEntity
if (!myPat.isEmpty() && myPat.getItem() instanceof EncodedPatternItem) { if (!myPat.isEmpty() && myPat.getItem() instanceof EncodedPatternItem) {
final World w = this.getWorld(); final World w = this.getWorld();
final EncodedPatternItem iep = (EncodedPatternItem) myPat.getItem(); final ICraftingPatternDetails ph = Api.instance().crafting().decodePattern(myPat, w);
final ICraftingPatternDetails ph = iep.getPatternForItem(myPat, w);
if (ph != null && ph.isCraftable()) { if (ph != null && ph.isCraftable()) {
this.forcePlan = true; this.forcePlan = true;
this.myPlan = ph; this.myPlan = ph;
@@ -253,8 +252,7 @@ public class MolecularAssemblerTileEntity extends AENetworkInvTileEntity
if (!is.isEmpty() && is.getItem() instanceof EncodedPatternItem) { if (!is.isEmpty() && is.getItem() instanceof EncodedPatternItem) {
if (!ItemStack.areItemsEqual(is, this.myPattern)) { if (!ItemStack.areItemsEqual(is, this.myPattern)) {
final World w = this.getWorld(); final World w = this.getWorld();
final EncodedPatternItem iep = (EncodedPatternItem) is.getItem(); final ICraftingPatternDetails ph = Api.instance().crafting().decodePattern(is, w);
final ICraftingPatternDetails ph = iep.getPatternForItem(is, w);
if (ph != null && ph.isCraftable()) { if (ph != null && ph.isCraftable()) {
this.progress = 0; this.progress = 0;
+13 -13
View File
@@ -10,12 +10,14 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.World; import net.minecraft.world.World;
/** /**
* To be implemented on blocks that "hide" another block inside, so connected textures can still be accomplished. * To be implemented on blocks that "hide" another block inside, so connected
* textures can still be accomplished.
*/ */
public interface IFacade { public interface IFacade {
/** /**
* @deprecated Use {@link #getFacade(IBlockReader, BlockPos, Direction, BlockPos)} * @deprecated Use
* {@link #getFacade(IBlockReader, BlockPos, Direction, BlockPos)}
*/ */
@Nonnull @Nonnull
@Deprecated @Deprecated
@@ -24,20 +26,18 @@ public interface IFacade {
/** /**
* Gets the blockstate this facade appears as. * Gets the blockstate this facade appears as.
* *
* @param world * @param world {@link World}
* {@link World} * @param pos The Blocks position
* @param pos * @param side The side being rendered, NOT the side being connected from.
* The Blocks position * <p>
* @param side * This value can be null if no side is specified. Please
* The side being rendered, NOT the side being connected from. * handle this appropriately.
* <p> * @param connection The position of the block being connected to.
* This value can be null if no side is specified. Please handle this appropriately.
* @param connection
* The position of the block being connected to.
* @return The blockstate which your block appears as. * @return The blockstate which your block appears as.
*/ */
@Nonnull @Nonnull
default BlockState getFacade(@Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nullable Direction side, @Nonnull BlockPos connection) { default BlockState getFacade(@Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nullable Direction side,
@Nonnull BlockPos connection) {
return getFacade(world, pos, side); return getFacade(world, pos, side);
} }