From 36470f69adb34dd277ee1dcc0ddb739e1225d9fc Mon Sep 17 00:00:00 2001 From: Brennan Ward Date: Mon, 25 Mar 2019 15:31:54 -0400 Subject: [PATCH] Add class to wrap IItemHandler as IInventory for easier recipe interaction. (#5522) --- .../common/extensions/ForgeRecipeManager.java | 34 +++- .../common/extensions/IForgeRecipe.java | 4 +- .../items/wrapper/RecipeWrapper.java | 157 ++++++++++++++++++ 3 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/minecraftforge/items/wrapper/RecipeWrapper.java diff --git a/src/main/java/net/minecraftforge/common/extensions/ForgeRecipeManager.java b/src/main/java/net/minecraftforge/common/extensions/ForgeRecipeManager.java index b4b474a2f7..959eace45a 100644 --- a/src/main/java/net/minecraftforge/common/extensions/ForgeRecipeManager.java +++ b/src/main/java/net/minecraftforge/common/extensions/ForgeRecipeManager.java @@ -28,6 +28,7 @@ import javax.annotation.Nullable; import com.google.common.collect.Maps; import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; import net.minecraft.resources.IResourceManager; @@ -36,24 +37,42 @@ import net.minecraft.util.NonNullList; import net.minecraft.world.World; import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.common.crafting.RecipeType; +import net.minecraftforge.items.wrapper.RecipeWrapper; @SuppressWarnings("deprecation") public abstract class ForgeRecipeManager implements IResourceManagerReloadListener { + /** + * Master list of sorted recipes. + */ protected final Map, List> sortedRecipes = Maps.newHashMap(); @Override - public void onResourceManagerReload(IResourceManager resourceManager) { + public void onResourceManagerReload(IResourceManager resourceManager) + { this.sortedRecipes.clear(); CraftingHelper.reloadConstants(resourceManager); } + /** + * Returns all the recipes that match this type. + * @param type A recipe type. + * @return All recipes that are of the passed type. This list may be empty. + */ @SuppressWarnings("unchecked") public List getRecipes(RecipeType type) { return (List) this.sortedRecipes.computeIfAbsent(type, t -> new ArrayList<>()); } + /** + * Automatically returns a result from the first recipe that matches this inv and type. + * Modders should probably not use this, and instead use {@link #getRecipe(IInventory, World, RecipeType)} since you get the recipe object itself. + * @param input An inventory. + * @param world The world. + * @param type The type of recipe to match against. + * @return The output of the first matching recipe, or {@link ItemStack#EMPTY} if no recipes match. + */ public ItemStack getResult(IInventory input, World world, RecipeType type) { for(IRecipe irecipe : getRecipes(type)) @@ -61,7 +80,16 @@ public abstract class ForgeRecipeManager implements IResourceManagerReloadListen return ItemStack.EMPTY; } - @Nullable + /** + * Used to find a matching recipe for the given inv, world, and type. + * Since this requires an IInventory, modders can use {@link RecipeWrapper} to avoid direct implementation. + * For crafting recipes, it is advised to still use {@link InventoryCrafting}. The dynamic vanilla recipes require they be crafted in one. + * @param input An inventory. + * @param world The world. + * @param type The type of recipe to match against. + * @return The first matching recipe. + */ + @Nullable public T getRecipe(IInventory input, World world, RecipeType type) { for(T irecipe : getRecipes(type)) @@ -71,7 +99,9 @@ public abstract class ForgeRecipeManager implements IResourceManagerReloadListen /** * Modders should not use this if possible. In the context that this is used, you should already have the recipe. Do not run extra lookups using this method. + * Use {@link IRecipe#getRemainingItems(IInventory)}. */ + @Deprecated public NonNullList getRemainingItems(IInventory input, World world, RecipeType type) { for(IRecipe irecipe : getRecipes(type)) diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeRecipe.java b/src/main/java/net/minecraftforge/common/extensions/IForgeRecipe.java index b36aed279e..7bcdbdf67e 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeRecipe.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeRecipe.java @@ -19,8 +19,6 @@ package net.minecraftforge.common.extensions; -import java.util.Collection; - import net.minecraft.item.crafting.IRecipe; import net.minecraftforge.common.crafting.RecipeType; import net.minecraftforge.common.crafting.VanillaRecipeTypes; @@ -28,7 +26,7 @@ import net.minecraftforge.common.crafting.VanillaRecipeTypes; public interface IForgeRecipe { /** - * Used when sorting this recipe into it's category during load, and in the default type matcher below. + * Used when sorting this recipe into it's category during load. This type determines what class the recipe must subclass. * @return The type of this recipe. */ default RecipeType getType() diff --git a/src/main/java/net/minecraftforge/items/wrapper/RecipeWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/RecipeWrapper.java new file mode 100644 index 0000000000..64f60ea06f --- /dev/null +++ b/src/main/java/net/minecraftforge/items/wrapper/RecipeWrapper.java @@ -0,0 +1,157 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2019. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.items.wrapper; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.text.ITextComponent; +import net.minecraftforge.items.IItemHandlerModifiable; + +public class RecipeWrapper implements IInventory { + + protected final IItemHandlerModifiable inv; + protected final int width; + protected final int height; + + public RecipeWrapper(IItemHandlerModifiable inv, int width, int height) + { + this.inv = inv; + this.width = width; + this.height = height; + } + + /** + * Returns the size of this inventory. Must be equivalent to {@link #getHeight()} * {@link #getWidth()}. + */ + @Override + public int getSizeInventory() + { + return getHeight() * getWidth(); + } + + /** + * Returns the stack in this slot. This stack should be a modifiable reference, not a copy of a stack in your inventory. + */ + @Override + public ItemStack getStackInSlot(int slot) + { + return inv.getStackInSlot(slot); + } + + /** + * Attempts to remove n items from the specified slot. Returns the split stack that was removed. Modifies the inventory. + */ + @Override + public ItemStack decrStackSize(int slot, int count) + { + ItemStack stack = inv.getStackInSlot(slot); + return stack.isEmpty() ? ItemStack.EMPTY : stack.split(count); + } + + /** + * Sets the contents of this slot to the provided stack. + */ + @Override + public void setInventorySlotContents(int slot, ItemStack stack) + { + inv.setStackInSlot(slot, stack); + } + + /** + * Returns the height of this inventory. + */ + @Override + public int getHeight() + { + return height; + } + + /** + * Returns the width of this inventory. + */ + @Override + public int getWidth() + { + return width; + } + + /** + * Removes the stack contained in this slot from the underlying handler, and returns it. + */ + @Override + public ItemStack removeStackFromSlot(int index) + { + ItemStack s = getStackInSlot(index); + if(s.isEmpty()) return ItemStack.EMPTY; + setInventorySlotContents(index, ItemStack.EMPTY); + return s; + } + + @Override + public boolean isEmpty() + { + for(int i = 0; i < inv.getSlots(); i++) + { + if(!inv.getStackInSlot(i).isEmpty()) return false; + } + return true; + } + + @Override + public boolean isItemValidForSlot(int slot, ItemStack stack) + { + return inv.isItemValid(slot, stack); + } + + @Override + public void clear() + { + for(int i = 0; i < inv.getSlots(); i++) + { + inv.setStackInSlot(i, ItemStack.EMPTY); + } + } + + //The following methods are never used by vanilla in crafting. They are defunct as mods need not override them. + @Override + public int getInventoryStackLimit() { return 0; } + @Override + public void markDirty() {} + @Override + public boolean isUsableByPlayer(EntityPlayer player) { return false; } + @Override + public void openInventory(EntityPlayer player) {} + @Override + public void closeInventory(EntityPlayer player) {} + @Override + public int getField(int id) { return 0; } + @Override + public void setField(int id, int value) {} + @Override + public int getFieldCount() { return 0; } + @Override + public ITextComponent getName() { return null; } + @Override + public boolean hasCustomName() { return false; } + @Override + public ITextComponent getCustomName() { return null; } + +}