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;
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.
*/
@@ -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.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}.
* <p>
* 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();
@@ -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);
@@ -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);
}
@@ -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<CraftingInventory> 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<ItemStack> 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
@@ -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)) {
+9
View File
@@ -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;
@@ -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) {
futureJob.cancel(true);
}
AELog.debug(e);
AELog.info(e);
}
}
}
@@ -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<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 testFrame = new CraftingInventory(new ContainerNull(), 3, 3);
private final ItemStack correctOutput;
@@ -53,79 +54,73 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
private final IAEItemStack[] condensedOutputs;
private final IAEItemStack[] inputs;
private final IAEItemStack[] outputs;
private final boolean isCrafting;
private final boolean isCraftable;
private final boolean canSubstitute;
private final Set<TestLookup> failCache = new HashSet<>();
private final Set<TestLookup> 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<IAEItemStack> ingredients = templateItem.getIngredients(itemStack);
final List<IAEItemStack> 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<IAEItemStack> in = new ArrayList<>();
final List<IAEItemStack> 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<IAEItemStack, IAEItemStack> tmpOutputs = new HashMap<>();
@@ -190,12 +185,12 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
@Override
public ItemStack getPattern() {
return this.patternItem;
return this.pattern.createItemStack();
}
@Override
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.");
}
@@ -241,7 +236,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
@Override
public boolean isCraftable() {
return this.isCrafting;
return this.isCraftable;
}
@Override
@@ -271,7 +266,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
@Override
public ItemStack getOutput(final CraftingInventory craftingInv, final World w) {
if (!this.isCrafting) {
if (!this.isCraftable) {
throw new IllegalStateException("Only crafting recipes supported.");
}
@@ -323,7 +318,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
}
@Override
public int compareTo(final PatternHelper o) {
public int compareTo(final CraftingPatternDetails o) {
return Integer.compare(o.priority, this.priority);
}
@@ -341,7 +336,7 @@ public class PatternHelper implements ICraftingPatternDetails, Comparable<Patter
return false;
}
final PatternHelper other = (PatternHelper) obj;
final CraftingPatternDetails other = (CraftingPatternDetails) obj;
if (this.pattern != null && other.pattern != null) {
return this.pattern.equals(other.pattern);
@@ -57,7 +57,6 @@ import appeng.api.config.Actionable;
import appeng.api.config.Settings;
import appeng.api.config.Upgrades;
import appeng.api.config.YesNo;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.implementations.IUpgradeableHost;
import appeng.api.implementations.tiles.ICraftingMachine;
import appeng.api.networking.GridFlags;
@@ -407,21 +406,15 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
}
private void addToCraftingList(final ItemStack is) {
if (is.isEmpty()) {
return;
}
final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(is,
this.iHost.getTileEntity().getWorld());
if (is.getItem() instanceof ICraftingPatternItem) {
final ICraftingPatternItem cpi = (ICraftingPatternItem) is.getItem();
final ICraftingPatternDetails details = cpi.getPatternForItem(is, this.iHost.getTileEntity().getWorld());
if (details != null) {
if (this.craftingList == null) {
this.craftingList = new ArrayList<>();
}
this.craftingList.add(details);
if (details != null) {
if (this.craftingList == null) {
this.craftingList = new ArrayList<>();
}
this.craftingList.add(details);
}
}
@@ -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<ItemStack, ItemStack> 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<ITextComponent> 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<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.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<ICraftingMedium> details = this.craftingMethods.get(api);
if (details == null) {
details = new ArrayList<>();
@@ -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();
@@ -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) {
@@ -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;
+13 -13
View File
@@ -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.
* <p>
* 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.
* <p>
* 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);
}