From 9bb23bcac87f6b9e4a568f021ffb39a0b684df5d Mon Sep 17 00:00:00 2001 From: yueh Date: Sun, 12 Jul 2020 16:21:30 +0200 Subject: [PATCH] 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 --- src/api/java/appeng/api/IAppEngApi.java | 6 + .../appeng/api/crafting/ICraftingHelper.java | 108 +++++++++++ .../implementations/ICraftingPatternItem.java | 46 ----- .../crafting/ICraftingPatternDetails.java | 15 +- .../crafting/ICraftingProviderHelper.java | 4 + .../MolecularAssemblerContainer.java | 4 +- .../implementations/PatternTermContainer.java | 62 ++----- .../container/slot/RestrictedInputSlot.java | 36 +--- src/main/java/appeng/core/Api.java | 9 + .../java/appeng/core/api/ApiCrafting.java | 167 ++++++++++++++++++ .../core/sync/packets/CraftRequestPacket.java | 2 +- ...elper.java => CraftingPatternDetails.java} | 93 +++++----- .../java/appeng/helpers/DualityInterface.java | 21 +-- .../appeng/items/misc/EncodedPatternItem.java | 148 ++++++++++++++-- .../appeng/me/cache/CraftingGridCache.java | 4 + .../implementations/CraftingCPUCluster.java | 8 +- .../parts/reporting/PatternTerminalPart.java | 29 ++- .../MolecularAssemblerTileEntity.java | 6 +- .../java/team/chisel/ctm/api/IFacade.java | 26 +-- 19 files changed, 551 insertions(+), 243 deletions(-) create mode 100644 src/api/java/appeng/api/crafting/ICraftingHelper.java delete mode 100644 src/api/java/appeng/api/implementations/ICraftingPatternItem.java create mode 100644 src/main/java/appeng/core/api/ApiCrafting.java rename src/main/java/appeng/helpers/{PatternHelper.java => CraftingPatternDetails.java} (79%) diff --git a/src/api/java/appeng/api/IAppEngApi.java b/src/api/java/appeng/api/IAppEngApi.java index 59b06c871..6a3391f8c 100644 --- a/src/api/java/appeng/api/IAppEngApi.java +++ b/src/api/java/appeng/api/IAppEngApi.java @@ -24,6 +24,7 @@ package appeng.api; import appeng.api.client.IClientHelper; +import appeng.api.crafting.ICraftingHelper; import appeng.api.definitions.IDefinitions; import appeng.api.features.IRegistryContainer; import appeng.api.networking.IGridHelper; @@ -48,6 +49,11 @@ public interface IAppEngApi { */ 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. */ diff --git a/src/api/java/appeng/api/crafting/ICraftingHelper.java b/src/api/java/appeng/api/crafting/ICraftingHelper.java new file mode 100644 index 000000000..909f26353 --- /dev/null +++ b/src/api/java/appeng/api/crafting/ICraftingHelper.java @@ -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. + *

+ * 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); + +} diff --git a/src/api/java/appeng/api/implementations/ICraftingPatternItem.java b/src/api/java/appeng/api/implementations/ICraftingPatternItem.java deleted file mode 100644 index 5b93bd7f6..000000000 --- a/src/api/java/appeng/api/implementations/ICraftingPatternItem.java +++ /dev/null @@ -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); -} diff --git a/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java b/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java index 795c5830b..f2ac98821 100644 --- a/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java +++ b/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java @@ -27,18 +27,19 @@ import net.minecraft.inventory.CraftingInventory; import net.minecraft.item.ItemStack; import net.minecraft.world.World; -import appeng.api.implementations.ICraftingPatternItem; import appeng.api.storage.data.IAEItemStack; /** - * do not implement provided by {@link ICraftingPatternItem} - * - * caching this INSTANCE will increase performance of validation and checks. + * Describes a crafting or processing pattern decoded by + * {@link appeng.api.crafting.ICraftingHelper}. + *

+ * Do not cache instances of this class unless you handle recipe reloads on the + * server and client correctly. */ public interface ICraftingPatternDetails { /** - * @return source item. + * @return encodes this crafting pattern into a new item stack. */ ItemStack getPattern(); @@ -52,7 +53,9 @@ public interface ICraftingPatternDetails { 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(); diff --git a/src/api/java/appeng/api/networking/crafting/ICraftingProviderHelper.java b/src/api/java/appeng/api/networking/crafting/ICraftingProviderHelper.java index 00efcc1ca..1c7e6c4cc 100644 --- a/src/api/java/appeng/api/networking/crafting/ICraftingProviderHelper.java +++ b/src/api/java/appeng/api/networking/crafting/ICraftingProviderHelper.java @@ -23,6 +23,7 @@ package appeng.api.networking.crafting; +import appeng.api.crafting.ICraftingHelper; import appeng.api.storage.data.IAEItemStack; /** @@ -33,6 +34,9 @@ public interface ICraftingProviderHelper { /** * 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); diff --git a/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java b/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java index 341602fbe..542a32cfd 100644 --- a/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java +++ b/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java @@ -38,6 +38,7 @@ import appeng.container.slot.AppEngSlot; import appeng.container.slot.MolecularAssemblerPatternSlot; import appeng.container.slot.OutputSlot; import appeng.container.slot.RestrictedInputSlot; +import appeng.core.Api; import appeng.items.misc.EncodedPatternItem; import appeng.tile.crafting.MolecularAssemblerTileEntity; import appeng.util.Platform; @@ -79,8 +80,7 @@ public class MolecularAssemblerContainer extends UpgradeableContainer implements if (is.getItem() instanceof EncodedPatternItem) { final World w = this.getTileEntity().getWorld(); - final EncodedPatternItem iep = (EncodedPatternItem) is.getItem(); - final ICraftingPatternDetails ph = iep.getPatternForItem(is, w); + final ICraftingPatternDetails ph = Api.instance().crafting().decodePattern(is, w); if (ph.isCraftable()) { return ph.isValidItemForSlot(slotIndex, i, w); } diff --git a/src/main/java/appeng/container/implementations/PatternTermContainer.java b/src/main/java/appeng/container/implementations/PatternTermContainer.java index 7ca82cc5a..22fd63e5b 100644 --- a/src/main/java/appeng/container/implementations/PatternTermContainer.java +++ b/src/main/java/appeng/container/implementations/PatternTermContainer.java @@ -20,7 +20,6 @@ package appeng.container.implementations; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import net.minecraft.entity.player.PlayerEntity; 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.Slot; 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.nbt.CompoundNBT; -import net.minecraft.nbt.INBT; -import net.minecraft.nbt.ListNBT; import net.minecraft.network.PacketBuffer; import net.minecraft.world.World; import net.minecraftforge.items.IItemHandler; @@ -44,6 +41,7 @@ import net.minecraftforge.items.wrapper.PlayerInvWrapper; import appeng.api.config.Actionable; import appeng.api.config.SecurityPermissions; +import appeng.api.crafting.ICraftingHelper; import appeng.api.definitions.IDefinitions; import appeng.api.storage.IMEMonitor; import appeng.api.storage.ITerminalHost; @@ -98,8 +96,9 @@ public class PatternTermContainer extends MEMonitorableContainer private final PatternTermSlot craftSlot; private final RestrictedInputSlot patternSlotIN; private final RestrictedInputSlot patternSlotOUT; + private final ICraftingHelper craftingHelper = Api.INSTANCE.crafting(); - private IRecipe currentRecipe; + private ICraftingRecipe currentRecipe; @GuiSync(97) public boolean craftingMode = true; @GuiSync(96) @@ -206,17 +205,17 @@ public class PatternTermContainer extends MEMonitorableContainer final ItemStack[] out = this.getOutputs(); // if there is no input, this would be silly. - if (in == null || out == null) { + if (in == null || out == null || isCraftingMode() && currentRecipe == null) { return; } // 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; } // if nothing is there we should snag a new pattern. else if (output.isEmpty()) { output = this.patternSlotIN.getStack(); - if (output.isEmpty() || !this.isPattern(output)) { + if (output.isEmpty() || !isPattern(output)) { return; // no blanks. } @@ -226,34 +225,17 @@ public class PatternTermContainer extends MEMonitorableContainer this.patternSlotIN.putStack(ItemStack.EMPTY); } - // add a new encoded pattern. - Optional maybePattern = Api.instance().definitions().items().encodedPattern().maybeStack(1); - if (maybePattern.isPresent()) { - output = maybePattern.get(); - this.patternSlotOUT.putStack(output); - } + // let the crafting helper create a new encoded pattern + output = null; } - // encode the slot. - final CompoundNBT encodedValue = new CompoundNBT(); - - final ListNBT tagIn = new ListNBT(); - final ListNBT tagOut = new ListNBT(); - - for (final ItemStack i : in) { - tagIn.add(this.createItemTag(i)); + if (this.isCraftingMode()) { + output = craftingHelper.encodeCraftingPattern(output, this.currentRecipe, in, out[0], isSubstitute()); + } else { + output = craftingHelper.encodeProcessingPattern(output, in, out); } + 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() { @@ -308,21 +290,7 @@ public class PatternTermContainer extends MEMonitorableContainer } final IDefinitions definitions = Api.instance().definitions(); - - 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; + return definitions.materials().blankPattern().isSameAs(output); } @Override diff --git a/src/main/java/appeng/container/slot/RestrictedInputSlot.java b/src/main/java/appeng/container/slot/RestrictedInputSlot.java index 152c9eb92..d6913fc45 100644 --- a/src/main/java/appeng/container/slot/RestrictedInputSlot.java +++ b/src/main/java/appeng/container/slot/RestrictedInputSlot.java @@ -33,11 +33,11 @@ import net.minecraft.world.World; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.items.IItemHandler; +import appeng.api.crafting.ICraftingHelper; import appeng.api.definitions.IDefinitions; import appeng.api.definitions.IItems; import appeng.api.definitions.IMaterials; import appeng.api.features.INetworkEncodable; -import appeng.api.implementations.ICraftingPatternItem; import appeng.api.implementations.items.IBiometricCard; import appeng.api.implementations.items.ISpatialStorageCell; import appeng.api.implementations.items.IStorageComponent; @@ -88,10 +88,7 @@ public class RestrictedInputSlot extends AppEngSlot { public boolean isValid(final ItemStack is, final World theWorld) { if (this.which == PlacableItemType.VALID_ENCODED_PATTERN_W_OUTPUT) { - final ICraftingPatternDetails ap = is.getItem() instanceof ICraftingPatternItem - ? ((ICraftingPatternItem) is.getItem()).getPatternForItem(is, theWorld) - : null; - return ap != null; + return Api.instance().crafting().decodePattern(is, theWorld) != null; } return true; } @@ -126,39 +123,24 @@ public class RestrictedInputSlot extends AppEngSlot { final IDefinitions definitions = Api.instance().definitions(); final IMaterials materials = definitions.materials(); final IItems items = definitions.items(); + final ICraftingHelper crafting = Api.instance().crafting(); switch (this.which) { case ENCODED_CRAFTING_PATTERN: - if (i.getItem() instanceof ICraftingPatternItem) { - final ICraftingPatternItem b = (ICraftingPatternItem) i.getItem(); - final ICraftingPatternDetails de = b.getPatternForItem(i, this.p.player.world); - if (de != null) { - return de.isCraftable(); - } + final ICraftingPatternDetails de = crafting.decodePattern(i, this.p.player.world); + if (de != null) { + return de.isCraftable(); } return false; case VALID_ENCODED_PATTERN_W_OUTPUT: case ENCODED_PATTERN_W_OUTPUT: - case ENCODED_PATTERN: { - if (i.getItem() instanceof ICraftingPatternItem) { - return true; - } - // ICraftingPatternDetails pattern = i.getItem() instanceof ICraftingPatternItem - // ? - // ((ICraftingPatternItem) - // i.getItem()).getPatternForItem( i ) : null; - return false;// pattern != null; - } + case ENCODED_PATTERN: + return crafting.isEncodedPattern(i); case BLANK_PATTERN: return materials.blankPattern().isSameAs(i); case PATTERN: - - if (i.getItem() instanceof ICraftingPatternItem) { - return true; - } - - return materials.blankPattern().isSameAs(i); + return materials.blankPattern().isSameAs(i) || crafting.isEncodedPattern(i); case INSCRIBER_PLATE: if (materials.namePress().isSameAs(i)) { diff --git a/src/main/java/appeng/core/Api.java b/src/main/java/appeng/core/Api.java index b61134ab0..f052afd07 100644 --- a/src/main/java/appeng/core/Api.java +++ b/src/main/java/appeng/core/Api.java @@ -21,10 +21,12 @@ package appeng.core; import appeng.api.AEAddon; import appeng.api.IAppEngApi; import appeng.api.client.IClientHelper; +import appeng.api.crafting.ICraftingHelper; import appeng.api.features.IRegistryContainer; import appeng.api.networking.IGridHelper; import appeng.api.storage.IStorageHelper; import appeng.core.api.ApiClientHelper; +import appeng.core.api.ApiCrafting; import appeng.core.api.ApiGrid; import appeng.core.api.ApiPart; import appeng.core.api.ApiStorage; @@ -59,6 +61,7 @@ public final class Api implements IAppEngApi { private final IStorageHelper storageHelper; private final IGridHelper networkHelper; private final ApiDefinitions definitions; + private final ICraftingHelper craftingHelper; private final IClientHelper client; private Api() { @@ -67,6 +70,7 @@ public final class Api implements IAppEngApi { this.registryContainer = new RegistryContainer(); this.partHelper = new ApiPart(); this.definitions = new ApiDefinitions((PartModels) this.registryContainer.partModels()); + this.craftingHelper = new ApiCrafting(this.definitions); this.client = new ApiClientHelper(this.definitions); } @@ -84,6 +88,11 @@ public final class Api implements IAppEngApi { return this.storageHelper; } + @Override + public ICraftingHelper crafting() { + return this.craftingHelper; + } + @Override public IGridHelper grid() { return this.networkHelper; diff --git a/src/main/java/appeng/core/api/ApiCrafting.java b/src/main/java/appeng/core/api/ApiCrafting.java new file mode 100644 index 000000000..e4f710bc2 --- /dev/null +++ b/src/main/java/appeng/core/api/ApiCrafting.java @@ -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 . + */ + +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 ingredients = patternItem.getIngredients(itemStack); + List 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; + } + +} diff --git a/src/main/java/appeng/core/sync/packets/CraftRequestPacket.java b/src/main/java/appeng/core/sync/packets/CraftRequestPacket.java index 56bf5bce3..713fdadec 100644 --- a/src/main/java/appeng/core/sync/packets/CraftRequestPacket.java +++ b/src/main/java/appeng/core/sync/packets/CraftRequestPacket.java @@ -101,7 +101,7 @@ public class CraftRequestPacket extends BasePacket { if (futureJob != null) { futureJob.cancel(true); } - AELog.debug(e); + AELog.info(e); } } } diff --git a/src/main/java/appeng/helpers/PatternHelper.java b/src/main/java/appeng/helpers/CraftingPatternDetails.java similarity index 79% rename from src/main/java/appeng/helpers/PatternHelper.java rename to src/main/java/appeng/helpers/CraftingPatternDetails.java index 0664ec83e..b92dcad17 100644 --- a/src/main/java/appeng/helpers/PatternHelper.java +++ b/src/main/java/appeng/helpers/CraftingPatternDetails.java @@ -25,13 +25,15 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.google.common.base.Preconditions; + import net.minecraft.inventory.CraftingInventory; import net.minecraft.item.Item; 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.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; +import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import appeng.api.networking.crafting.ICraftingPatternDetails; @@ -39,12 +41,11 @@ import appeng.api.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.container.ContainerNull; import appeng.core.Api; +import appeng.items.misc.EncodedPatternItem; import appeng.util.Platform; -import appeng.util.item.AEItemStack; -public class PatternHelper implements ICraftingPatternDetails, Comparable { +public class CraftingPatternDetails implements ICraftingPatternDetails, Comparable { - private final ItemStack patternItem; private final CraftingInventory crafting = new CraftingInventory(new ContainerNull(), 3, 3); private final CraftingInventory testFrame = new CraftingInventory(new ContainerNull(), 3, 3); private final ItemStack correctOutput; @@ -53,79 +54,73 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable failCache = new HashSet<>(); private final Set passCache = new HashSet<>(); private final IAEItemStack pattern; private int priority = 0; - public PatternHelper(final ItemStack is, final World w) { - final CompoundNBT encodedValue = is.getTag(); + public CraftingPatternDetails(final IAEItemStack is, final World w) { + Preconditions.checkArgument(is.getItem() instanceof EncodedPatternItem, + "itemStack is not a ICraftingPatternItem"); - if (encodedValue == null) { - throw new IllegalArgumentException("No pattern here!"); - } + final EncodedPatternItem templateItem = (EncodedPatternItem) is.getItem(); + final ItemStack itemStack = is.createItemStack(); - final ListNBT inTag = encodedValue.getList("in", 10); - final ListNBT outTag = encodedValue.getList("out", 10); - this.isCrafting = encodedValue.getBoolean("crafting"); + final List ingredients = templateItem.getIngredients(itemStack); + final List products = templateItem.getProducts(itemStack); + final ResourceLocation recipeId = templateItem.getCraftingRecipeId(itemStack); - this.canSubstitute = this.isCrafting && encodedValue.getBoolean("substitute"); - this.patternItem = is; - this.pattern = AEItemStack.fromItemStack(is); + this.pattern = is.copy(); + this.isCraftable = recipeId != null; + this.canSubstitute = templateItem.allowsSubstitution(itemStack); final List in = new ArrayList<>(); final List out = new ArrayList<>(); - for (int x = 0; x < inTag.size(); x++) { - CompoundNBT ingredient = inTag.getCompound(x); - final ItemStack gs = ItemStack.read(ingredient); - - if (!ingredient.isEmpty() && gs.isEmpty()) { - throw new IllegalArgumentException("No pattern here!"); - } + for (int x = 0; x < 9; x++) { + final IAEItemStack ais = ingredients.get(x); + final ItemStack gs = ais != null ? ais.createItemStack() : ItemStack.EMPTY; 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); } - in.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(gs)); + in.add(ais != null ? ais.copy() : null); this.testFrame.setInventorySlotContents(x, gs); } - if (this.isCrafting) { - this.standardRecipe = w.getRecipeManager().getRecipe(IRecipeType.CRAFTING, this.crafting, w).orElse(null); + if (this.isCraftable) { + IRecipe recipe = w.getRecipeManager().getRecipes(IRecipeType.CRAFTING).get(recipeId); - if (this.standardRecipe != null) { - this.correctOutput = this.standardRecipe.getCraftingResult(this.crafting); - out.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class) - .createStack(this.correctOutput)); - } else { - throw new IllegalStateException("No pattern here!"); + if (recipe == null || recipe.getType() != IRecipeType.CRAFTING) { + throw new IllegalStateException("recipe id is not a crafting recipe"); } + + this.standardRecipe = (ICraftingRecipe) recipe; + this.correctOutput = this.standardRecipe.getCraftingResult(this.crafting); + + out.add(Api.instance().storage().getStorageChannel(IItemStorageChannel.class) + .createStack(this.correctOutput)); } else { this.standardRecipe = null; this.correctOutput = ItemStack.EMPTY; - for (int x = 0; x < outTag.size(); x++) { - CompoundNBT resultItemTag = outTag.getCompound(x); - final ItemStack gs = ItemStack.read(resultItemTag); - - if (!resultItemTag.isEmpty() && gs.isEmpty()) { - throw new IllegalArgumentException("No pattern here!"); - } + for (int x = 0; x < 3; x++) { + final IAEItemStack ais = products.get(x); + final ItemStack gs = ais.createItemStack(); 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.outputs = out.toArray(new IAEItemStack[0]); final Map tmpOutputs = new HashMap<>(); @@ -190,12 +185,12 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable(); - } - - this.craftingList.add(details); + if (details != null) { + if (this.craftingList == null) { + this.craftingList = new ArrayList<>(); } + + this.craftingList.add(details); } } diff --git a/src/main/java/appeng/items/misc/EncodedPatternItem.java b/src/main/java/appeng/items/misc/EncodedPatternItem.java index 41dd4b3df..9009a35ff 100644 --- a/src/main/java/appeng/items/misc/EncodedPatternItem.java +++ b/src/main/java/appeng/items/misc/EncodedPatternItem.java @@ -18,37 +18,50 @@ package appeng.items.misc; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; +import com.google.common.base.Preconditions; + import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; 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.ActionResultType; import net.minecraft.util.Hand; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; 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.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.core.Api; import appeng.core.AppEng; import appeng.core.localization.GuiText; import appeng.helpers.InvalidPatternHelper; -import appeng.helpers.PatternHelper; import appeng.items.AEBaseItem; 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. private static final Map SIMPLE_CACHE = new WeakHashMap<>(); @@ -95,7 +108,7 @@ public class EncodedPatternItem extends AEBaseItem implements ICraftingPatternIt @OnlyIn(Dist.CLIENT) public void addInformation(final ItemStack stack, final World world, final List lines, final ITooltipFlag advancedTooltips) { - final ICraftingPatternDetails details = this.getPatternForItem(stack, world); + final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(stack, world); if (details == null) { 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) { ItemStack out = SIMPLE_CACHE.get(item); @@ -202,11 +206,127 @@ public class EncodedPatternItem extends AEBaseItem implements ICraftingPatternIt 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; SIMPLE_CACHE.put(item, 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 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 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 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 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; + } + } diff --git a/src/main/java/appeng/me/cache/CraftingGridCache.java b/src/main/java/appeng/me/cache/CraftingGridCache.java index 5452970ac..26cac7e84 100644 --- a/src/main/java/appeng/me/cache/CraftingGridCache.java +++ b/src/main/java/appeng/me/cache/CraftingGridCache.java @@ -35,6 +35,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; +import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; @@ -80,6 +81,7 @@ import appeng.crafting.CraftingJob; import appeng.crafting.CraftingLink; import appeng.crafting.CraftingLinkNexus; import appeng.crafting.CraftingWatcher; +import appeng.helpers.CraftingPatternDetails; import appeng.me.cluster.implementations.CraftingCPUCluster; import appeng.me.helpers.BaseActionSource; import appeng.me.helpers.GenericInterestManager; @@ -303,6 +305,8 @@ public class CraftingGridCache @Override public void addCraftingOption(final ICraftingMedium medium, final ICraftingPatternDetails api) { + Preconditions.checkArgument(api.getClass() == CraftingPatternDetails.class, + "Only supports internal ICraftingPatternDetails for now"); List details = this.craftingMethods.get(api); if (details == null) { details = new ArrayList<>(); diff --git a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java index 69fa1561c..5fb4428d9 100644 --- a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java +++ b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java @@ -40,7 +40,7 @@ import net.minecraftforge.fml.hooks.BasicEventHooks; import appeng.api.config.Actionable; import appeng.api.config.FuzzyMode; import appeng.api.config.PowerMultiplier; -import appeng.api.implementations.ICraftingPatternItem; +import appeng.api.crafting.ICraftingHelper; import appeng.api.networking.IGrid; import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; @@ -1056,9 +1056,9 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { for (int x = 0; x < list.size(); x++) { final CompoundNBT item = list.getCompound(x); final IAEItemStack pattern = AEItemStack.fromNBT(item); - if (pattern != null && pattern.getItem() instanceof ICraftingPatternItem) { - final ICraftingPatternItem cpi = (ICraftingPatternItem) pattern.getItem(); - final ICraftingPatternDetails details = cpi.getPatternForItem(pattern.createItemStack(), + ICraftingHelper craftingHelper = Api.instance().crafting(); + if (craftingHelper.isEncodedPattern(pattern)) { + final ICraftingPatternDetails details = craftingHelper.decodePattern(pattern.createItemStack(), this.getWorld()); if (details != null) { final TaskProgress tp = new TaskProgress(); diff --git a/src/main/java/appeng/parts/reporting/PatternTerminalPart.java b/src/main/java/appeng/parts/reporting/PatternTerminalPart.java index 89ab7a99d..4576b158d 100644 --- a/src/main/java/appeng/parts/reporting/PatternTerminalPart.java +++ b/src/main/java/appeng/parts/reporting/PatternTerminalPart.java @@ -28,12 +28,12 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandler; import appeng.api.config.SecurityPermissions; -import appeng.api.implementations.ICraftingPatternItem; import appeng.api.networking.crafting.ICraftingPatternDetails; import appeng.api.parts.IPartModel; import appeng.api.storage.data.IAEItemStack; import appeng.container.implementations.MEMonitorableContainer; import appeng.container.implementations.PatternTermContainer; +import appeng.core.Api; import appeng.core.AppEng; import appeng.helpers.Reflected; import appeng.items.parts.PartModels; @@ -107,23 +107,20 @@ public class PatternTerminalPart extends AbstractTerminalPart { final ItemStack removedStack, final ItemStack newStack) { if (inv == this.pattern && slot == 1) { final ItemStack is = this.pattern.getStackInSlot(1); - if (!is.isEmpty() && is.getItem() instanceof ICraftingPatternItem) { - final ICraftingPatternItem pattern = (ICraftingPatternItem) is.getItem(); - final ICraftingPatternDetails details = pattern.getPatternForItem(is, - this.getHost().getTile().getWorld()); - if (details != null) { - this.setCraftingRecipe(details.isCraftable()); - this.setSubstitution(details.canSubstitute()); + final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(is, + this.getHost().getTile().getWorld()); + if (details != null) { + 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]; - this.crafting.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack()); - } + for (int x = 0; x < this.crafting.getSlots() && x < details.getInputs().length; x++) { + final IAEItemStack item = details.getInputs()[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]; - this.output.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]; + this.output.setStackInSlot(x, item == null ? ItemStack.EMPTY : item.createItemStack()); } } } else if (inv == this.crafting) { diff --git a/src/main/java/appeng/tile/crafting/MolecularAssemblerTileEntity.java b/src/main/java/appeng/tile/crafting/MolecularAssemblerTileEntity.java index 96bfd73f8..be9211259 100644 --- a/src/main/java/appeng/tile/crafting/MolecularAssemblerTileEntity.java +++ b/src/main/java/appeng/tile/crafting/MolecularAssemblerTileEntity.java @@ -226,8 +226,7 @@ public class MolecularAssemblerTileEntity extends AENetworkInvTileEntity if (!myPat.isEmpty() && myPat.getItem() instanceof EncodedPatternItem) { final World w = this.getWorld(); - final EncodedPatternItem iep = (EncodedPatternItem) myPat.getItem(); - final ICraftingPatternDetails ph = iep.getPatternForItem(myPat, w); + final ICraftingPatternDetails ph = Api.instance().crafting().decodePattern(myPat, w); if (ph != null && ph.isCraftable()) { this.forcePlan = true; this.myPlan = ph; @@ -253,8 +252,7 @@ public class MolecularAssemblerTileEntity extends AENetworkInvTileEntity if (!is.isEmpty() && is.getItem() instanceof EncodedPatternItem) { if (!ItemStack.areItemsEqual(is, this.myPattern)) { final World w = this.getWorld(); - final EncodedPatternItem iep = (EncodedPatternItem) is.getItem(); - final ICraftingPatternDetails ph = iep.getPatternForItem(is, w); + final ICraftingPatternDetails ph = Api.instance().crafting().decodePattern(is, w); if (ph != null && ph.isCraftable()) { this.progress = 0; diff --git a/src/main/java/team/chisel/ctm/api/IFacade.java b/src/main/java/team/chisel/ctm/api/IFacade.java index 710a3a250..9d17bd055 100644 --- a/src/main/java/team/chisel/ctm/api/IFacade.java +++ b/src/main/java/team/chisel/ctm/api/IFacade.java @@ -10,12 +10,14 @@ import net.minecraft.world.IBlockReader; 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 { /** - * @deprecated Use {@link #getFacade(IBlockReader, BlockPos, Direction, BlockPos)} + * @deprecated Use + * {@link #getFacade(IBlockReader, BlockPos, Direction, BlockPos)} */ @Nonnull @Deprecated @@ -24,20 +26,18 @@ public interface IFacade { /** * Gets the blockstate this facade appears as. * - * @param world - * {@link World} - * @param pos - * The Blocks position - * @param side - * The side being rendered, NOT the side being connected from. - *

- * This value can be null if no side is specified. Please handle this appropriately. - * @param connection - * The position of the block being connected to. + * @param world {@link World} + * @param pos The Blocks position + * @param side The side being rendered, NOT the side being connected from. + *

+ * 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. */ @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); }