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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user