mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 08:17:35 +00:00
[1.18.x] Add 3 new ingredient types, and make existing ingredients compatible with datagen (#8517)
This commit is contained in:
1 parent
c3bd365baa
commit
093acab825
24 files changed
+1130
-38
No files matched your search
@@ -5,11 +5,9 @@
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypes;
|
||||
import net.minecraft.commands.synchronization.ArgumentSerializer;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
@@ -17,17 +15,17 @@ import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.minecraftforge.common.crafting.PartialNBTIngredient;
|
||||
import net.minecraftforge.common.crafting.DifferenceIngredient;
|
||||
import net.minecraftforge.common.crafting.IntersectionIngredient;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import net.minecraftforge.common.data.ForgeFluidTagsProvider;
|
||||
import net.minecraftforge.common.loot.CanToolPerformAction;
|
||||
import net.minecraftforge.common.loot.GlobalLootModifierSerializer;
|
||||
import net.minecraftforge.common.loot.LootTableIdCondition;
|
||||
import net.minecraftforge.common.util.MavenVersionStringHelper;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fluids.FluidAttributes;
|
||||
@@ -53,7 +51,6 @@ import org.apache.logging.log4j.Logger;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.crafting.CompoundIngredient;
|
||||
import net.minecraftforge.common.crafting.ConditionalRecipe;
|
||||
@@ -256,6 +253,9 @@ public class ForgeMod
|
||||
|
||||
CraftingHelper.register(new ResourceLocation("forge", "compound"), CompoundIngredient.Serializer.INSTANCE);
|
||||
CraftingHelper.register(new ResourceLocation("forge", "nbt"), NBTIngredient.Serializer.INSTANCE);
|
||||
CraftingHelper.register(new ResourceLocation("forge", "partial_nbt"), PartialNBTIngredient.Serializer.INSTANCE);
|
||||
CraftingHelper.register(new ResourceLocation("forge", "difference"), DifferenceIngredient.Serializer.INSTANCE);
|
||||
CraftingHelper.register(new ResourceLocation("forge", "intersection"), IntersectionIngredient.Serializer.INSTANCE);
|
||||
CraftingHelper.register(new ResourceLocation("minecraft", "item"), VanillaIngredientSerializer.INSTANCE);
|
||||
|
||||
event.getRegistry().register(new ConditionalRecipe.Serializer<Recipe<?>>().setRegistryName(new ResourceLocation("forge", "conditional")));
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.crafting;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Extension of {@link Ingredient} which makes most methods custom ingredients need to implement abstract, and removes the static constructors
|
||||
* Mods are encouraged to extend this class for their custom ingredients
|
||||
*/
|
||||
public abstract class AbstractIngredient extends Ingredient
|
||||
{
|
||||
/** Empty constructor, for the sake of dynamic ingredients */
|
||||
protected AbstractIngredient()
|
||||
{
|
||||
super(Stream.of());
|
||||
}
|
||||
|
||||
/** Value constructor, for ingredients that have some vanilla representation */
|
||||
protected AbstractIngredient(Stream<? extends Value> values)
|
||||
{
|
||||
super(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isSimple();
|
||||
|
||||
@Override
|
||||
public abstract IIngredientSerializer<? extends Ingredient> getSerializer();
|
||||
|
||||
@Override
|
||||
public abstract JsonElement toJson();
|
||||
|
||||
|
||||
/* Hide vanilla ingredient static constructors to reduce errors with constructing custom ingredients */
|
||||
|
||||
/** @deprecated use {@link Ingredient#fromValues(Stream)} */
|
||||
@Deprecated
|
||||
public static Ingredient fromValues(Stream<? extends Ingredient.Value> values)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.fromValues()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#of()} */
|
||||
@Deprecated
|
||||
public static Ingredient of()
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.of()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#of(ItemLike...)} (Stream)} */
|
||||
@Deprecated
|
||||
public static Ingredient of(ItemLike... items)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.of()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#of(ItemStack...)} (Stream)} */
|
||||
@Deprecated
|
||||
public static Ingredient of(ItemStack... stacks)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.of()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#of(Stream)} (Stream)} */
|
||||
@Deprecated
|
||||
public static Ingredient of(Stream<ItemStack> stacks)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.of()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#of(TagKey)} (Stream)} */
|
||||
@Deprecated
|
||||
public static Ingredient of(TagKey<Item> tag)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.of()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#fromNetwork(FriendlyByteBuf)} */
|
||||
@Deprecated
|
||||
public static Ingredient fromNetwork(FriendlyByteBuf buffer)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.fromNetwork()");
|
||||
}
|
||||
|
||||
/** @deprecated use {@link Ingredient#fromJson(JsonElement)} (Stream)} */
|
||||
@Deprecated
|
||||
public static Ingredient fromJson(@Nullable JsonElement json)
|
||||
{
|
||||
throw new UnsupportedOperationException("Use Ingredient.fromJson()");
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.common.crafting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -14,6 +15,7 @@ import java.util.stream.Stream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -27,7 +29,8 @@ import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
public class CompoundIngredient extends Ingredient
|
||||
/** Ingredient that matches if any of the child ingredients match */
|
||||
public class CompoundIngredient extends AbstractIngredient
|
||||
{
|
||||
private List<Ingredient> children;
|
||||
private ItemStack[] stacks;
|
||||
@@ -36,11 +39,35 @@ public class CompoundIngredient extends Ingredient
|
||||
|
||||
protected CompoundIngredient(List<Ingredient> children)
|
||||
{
|
||||
super(Stream.of());
|
||||
this.children = Collections.unmodifiableList(children);
|
||||
this.isSimple = children.stream().allMatch(Ingredient::isSimple);
|
||||
}
|
||||
|
||||
/** Creates a compound ingredient from the given list of ingredients */
|
||||
public static Ingredient of(Ingredient... children)
|
||||
{
|
||||
// if 0 or 1 ingredient, can save effort
|
||||
if (children.length == 0)
|
||||
throw new IllegalArgumentException("Cannot create a compound ingredient with no children, use Ingredient.of() to create an empty ingredient");
|
||||
if (children.length == 1)
|
||||
return children[0];
|
||||
|
||||
// need to merge vanilla ingredients, as otherwise the JSON produced by this ingredient could be invalid
|
||||
List<Ingredient> vanillaIngredients = new ArrayList<>();
|
||||
List<Ingredient> allIngredients = new ArrayList<>();
|
||||
for (Ingredient child : children) {
|
||||
if (child.getSerializer() == VanillaIngredientSerializer.INSTANCE)
|
||||
vanillaIngredients.add(child);
|
||||
else
|
||||
allIngredients.add(child);
|
||||
}
|
||||
if (!vanillaIngredients.isEmpty())
|
||||
allIngredients.add(merge(vanillaIngredients));
|
||||
if (allIngredients.size() == 1)
|
||||
return allIngredients.get(0);
|
||||
return new CompoundIngredient(allIngredients);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack[] getItems()
|
||||
|
||||
@@ -8,6 +8,7 @@ package net.minecraftforge.common.crafting;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -147,48 +148,52 @@ public class CraftingHelper
|
||||
return getItemStack(json, readNBT, false);
|
||||
}
|
||||
|
||||
public static ItemStack getItemStack(JsonObject json, boolean readNBT, boolean disallowsAirInRecipe)
|
||||
public static Item getItem(String itemName, boolean disallowsAirInRecipe)
|
||||
{
|
||||
String itemName = GsonHelper.getAsString(json, "item");
|
||||
ResourceLocation itemKey = new ResourceLocation(itemName);
|
||||
|
||||
if (!ForgeRegistries.ITEMS.containsKey(itemKey))
|
||||
throw new JsonSyntaxException("Unknown item '" + itemName + "'");
|
||||
|
||||
Item item = ForgeRegistries.ITEMS.getValue(itemKey);
|
||||
|
||||
if (disallowsAirInRecipe && item == Items.AIR)
|
||||
throw new JsonSyntaxException("Invalid item: " + itemName);
|
||||
return Objects.requireNonNull(item);
|
||||
}
|
||||
|
||||
public static CompoundTag getNBT(JsonElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (element.isJsonObject())
|
||||
return TagParser.parseTag(GSON.toJson(element));
|
||||
else
|
||||
return TagParser.parseTag(GsonHelper.convertToString(element, "nbt"));
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new JsonSyntaxException("Invalid NBT Entry: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack getItemStack(JsonObject json, boolean readNBT, boolean disallowsAirInRecipe)
|
||||
{
|
||||
String itemName = GsonHelper.getAsString(json, "item");
|
||||
Item item = getItem(itemName, disallowsAirInRecipe);
|
||||
if (readNBT && json.has("nbt"))
|
||||
{
|
||||
// Lets hope this works? Needs test
|
||||
try
|
||||
CompoundTag nbt = getNBT(json.get("nbt"));
|
||||
CompoundTag tmp = new CompoundTag();
|
||||
if (nbt.contains("ForgeCaps"))
|
||||
{
|
||||
JsonElement element = json.get("nbt");
|
||||
CompoundTag nbt;
|
||||
if(element.isJsonObject())
|
||||
nbt = TagParser.parseTag(GSON.toJson(element));
|
||||
else
|
||||
nbt = TagParser.parseTag(GsonHelper.convertToString(element, "nbt"));
|
||||
|
||||
CompoundTag tmp = new CompoundTag();
|
||||
if (nbt.contains("ForgeCaps"))
|
||||
{
|
||||
tmp.put("ForgeCaps", nbt.get("ForgeCaps"));
|
||||
nbt.remove("ForgeCaps");
|
||||
}
|
||||
|
||||
tmp.put("tag", nbt);
|
||||
tmp.putString("id", itemName);
|
||||
tmp.putInt("Count", GsonHelper.getAsInt(json, "count", 1));
|
||||
|
||||
return ItemStack.of(tmp);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new JsonSyntaxException("Invalid NBT Entry: " + e.toString());
|
||||
tmp.put("ForgeCaps", nbt.get("ForgeCaps"));
|
||||
nbt.remove("ForgeCaps");
|
||||
}
|
||||
|
||||
tmp.put("tag", nbt);
|
||||
tmp.putString("id", itemName);
|
||||
tmp.putInt("Count", GsonHelper.getAsInt(json, "count", 1));
|
||||
|
||||
return ItemStack.of(tmp);
|
||||
}
|
||||
|
||||
return new ItemStack(item, GsonHelper.getAsInt(json, "count", 1));
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.crafting;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntComparators;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Ingredient that matches everything from the first ingredient that is not included in the second ingredient */
|
||||
public class DifferenceIngredient extends AbstractIngredient
|
||||
{
|
||||
private final Ingredient base;
|
||||
private final Ingredient subtracted;
|
||||
private ItemStack[] filteredMatchingStacks;
|
||||
private IntList packedMatchingStacks;
|
||||
|
||||
protected DifferenceIngredient(Ingredient base, Ingredient subtracted)
|
||||
{
|
||||
this.base = base;
|
||||
this.subtracted = subtracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the difference from the two ingredients
|
||||
* @param base Ingredient the item must match
|
||||
* @param subtracted Ingredient the item must not match
|
||||
* @return Ingredient that {@code base} anything in base that is not in {@code subtracted}
|
||||
*/
|
||||
public static DifferenceIngredient of(Ingredient base, Ingredient subtracted)
|
||||
{
|
||||
return new DifferenceIngredient(base, subtracted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable ItemStack stack)
|
||||
{
|
||||
if (stack == null || stack.isEmpty())
|
||||
return false;
|
||||
return base.test(stack) && !subtracted.test(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getItems()
|
||||
{
|
||||
if (this.filteredMatchingStacks == null)
|
||||
this.filteredMatchingStacks = Arrays.stream(base.getItems())
|
||||
.filter(stack -> !subtracted.test(stack))
|
||||
.toArray(ItemStack[]::new);
|
||||
return filteredMatchingStacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return getItems().length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimple()
|
||||
{
|
||||
return base.isSimple() && subtracted.isSimple();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidate()
|
||||
{
|
||||
super.invalidate();
|
||||
this.filteredMatchingStacks = null;
|
||||
this.packedMatchingStacks = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntList getStackingIds()
|
||||
{
|
||||
if (this.packedMatchingStacks == null || checkInvalidation())
|
||||
{
|
||||
markValid();
|
||||
ItemStack[] matchingStacks = getItems();
|
||||
this.packedMatchingStacks = new IntArrayList(matchingStacks.length);
|
||||
for (ItemStack stack : matchingStacks)
|
||||
this.packedMatchingStacks.add(StackedContents.getStackingIndex(stack));
|
||||
|
||||
this.packedMatchingStacks.sort(IntComparators.NATURAL_COMPARATOR);
|
||||
}
|
||||
return packedMatchingStacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("type", CraftingHelper.getID(Serializer.INSTANCE).toString());
|
||||
json.add("base", base.toJson());
|
||||
json.add("subtracted", subtracted.toJson());
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIngredientSerializer<DifferenceIngredient> getSerializer()
|
||||
{
|
||||
return Serializer.INSTANCE;
|
||||
}
|
||||
|
||||
public static class Serializer implements IIngredientSerializer<DifferenceIngredient>
|
||||
{
|
||||
public static final IIngredientSerializer<DifferenceIngredient> INSTANCE = new Serializer();
|
||||
|
||||
@Override
|
||||
public DifferenceIngredient parse(JsonObject json)
|
||||
{
|
||||
Ingredient base = Ingredient.fromJson(json.get("base"));
|
||||
Ingredient without = Ingredient.fromJson(json.get("subtracted"));
|
||||
return new DifferenceIngredient(base, without);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DifferenceIngredient parse(FriendlyByteBuf buffer)
|
||||
{
|
||||
Ingredient base = Ingredient.fromNetwork(buffer);
|
||||
Ingredient without = Ingredient.fromNetwork(buffer);
|
||||
return new DifferenceIngredient(base, without);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buffer, DifferenceIngredient ingredient)
|
||||
{
|
||||
ingredient.base.toNetwork(buffer);
|
||||
ingredient.subtracted.toNetwork(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.crafting;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntComparators;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** Ingredient that matches if all child ingredients match */
|
||||
public class IntersectionIngredient extends AbstractIngredient
|
||||
{
|
||||
private final List<Ingredient> children;
|
||||
private final boolean isSimple;
|
||||
private ItemStack[] intersectedMatchingStacks = null;
|
||||
private IntList packedMatchingStacks = null;
|
||||
protected IntersectionIngredient(List<Ingredient> children)
|
||||
{
|
||||
if (children.size() < 2)
|
||||
throw new IllegalArgumentException("Cannot create an IntersectionIngredient with one or no children");
|
||||
this.children = Collections.unmodifiableList(children);
|
||||
this.isSimple = children.stream().allMatch(Ingredient::isSimple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an intersection ingredient
|
||||
* @param ingredients List of ingredients to match
|
||||
* @return Ingredient that only matches if all the passed ingredients match
|
||||
*/
|
||||
public static Ingredient of(Ingredient... ingredients)
|
||||
{
|
||||
if (ingredients.length == 0)
|
||||
throw new IllegalArgumentException("Cannot create an IntersectionIngredient with no children, use Ingredient.of() to create an empty ingredient");
|
||||
if (ingredients.length == 1)
|
||||
return ingredients[0];
|
||||
|
||||
return new IntersectionIngredient(Arrays.asList(ingredients));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable ItemStack stack)
|
||||
{
|
||||
if (stack == null || stack.isEmpty())
|
||||
return false;
|
||||
|
||||
for (Ingredient ingredient : children)
|
||||
if (!ingredient.test(stack))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getItems()
|
||||
{
|
||||
if (this.intersectedMatchingStacks == null)
|
||||
{
|
||||
this.intersectedMatchingStacks = Arrays
|
||||
.stream(children.get(0).getItems())
|
||||
.filter(stack ->
|
||||
{
|
||||
// the first ingredient is treated as a base, filtered by the second onwards
|
||||
for (int i = 1; i < children.size(); i++)
|
||||
if (!children.get(i).test(stack))
|
||||
return false;
|
||||
return true;
|
||||
})
|
||||
.toArray(ItemStack[]::new);
|
||||
}
|
||||
return intersectedMatchingStacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return getItems().length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimple()
|
||||
{
|
||||
return isSimple;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidate()
|
||||
{
|
||||
super.invalidate();
|
||||
this.intersectedMatchingStacks = null;
|
||||
this.packedMatchingStacks = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntList getStackingIds()
|
||||
{
|
||||
if (this.packedMatchingStacks == null || checkInvalidation())
|
||||
{
|
||||
markValid();
|
||||
ItemStack[] matchingStacks = getItems();
|
||||
this.packedMatchingStacks = new IntArrayList(matchingStacks.length);
|
||||
for(ItemStack stack : matchingStacks)
|
||||
this.packedMatchingStacks.add(StackedContents.getStackingIndex(stack));
|
||||
this.packedMatchingStacks.sort(IntComparators.NATURAL_COMPARATOR);
|
||||
}
|
||||
return packedMatchingStacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("type", CraftingHelper.getID(Serializer.INSTANCE).toString());
|
||||
JsonArray array = new JsonArray();
|
||||
for (Ingredient ingredient : children)
|
||||
array.add(ingredient.toJson());
|
||||
|
||||
json.add("children", array);
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIngredientSerializer<IntersectionIngredient> getSerializer()
|
||||
{
|
||||
return Serializer.INSTANCE;
|
||||
}
|
||||
|
||||
public static class Serializer implements IIngredientSerializer<IntersectionIngredient>
|
||||
{
|
||||
public static final IIngredientSerializer<IntersectionIngredient> INSTANCE = new Serializer();
|
||||
|
||||
@Override
|
||||
public IntersectionIngredient parse(JsonObject json)
|
||||
{
|
||||
JsonArray children = GsonHelper.getAsJsonArray(json, "children");
|
||||
if (children.size() < 2)
|
||||
throw new JsonSyntaxException("Must have at least two children for an intersection ingredient");
|
||||
return new IntersectionIngredient(IntStream.range(0, children.size()).mapToObj(i -> Ingredient.fromJson(children.get(i))).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntersectionIngredient parse(FriendlyByteBuf buffer)
|
||||
{
|
||||
return new IntersectionIngredient(Stream.generate(() -> Ingredient.fromNetwork(buffer)).limit(buffer.readVarInt()).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buffer, IntersectionIngredient intersection)
|
||||
{
|
||||
buffer.writeVarInt(intersection.children.size());
|
||||
for (Ingredient ingredient : intersection.children)
|
||||
ingredient.toNetwork(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
public class NBTIngredient extends Ingredient
|
||||
/** Ingredient that matches the given stack, performing an exact NBT match. Use {@link PartialNBTIngredient} if you need partial match. */
|
||||
public class NBTIngredient extends AbstractIngredient
|
||||
{
|
||||
private final ItemStack stack;
|
||||
protected NBTIngredient(ItemStack stack)
|
||||
@@ -25,6 +26,12 @@ public class NBTIngredient extends Ingredient
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
/** Creates a new ingredient matching the given stack and tag */
|
||||
public static NBTIngredient of(ItemStack stack)
|
||||
{
|
||||
return new NBTIngredient(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable ItemStack input)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.crafting;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import net.minecraft.advancements.critereon.NbtPredicate;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** Ingredient that matches the given items, performing a partial NBT match. Use {@link NBTIngredient} if you want exact match on NBT */
|
||||
public class PartialNBTIngredient extends AbstractIngredient
|
||||
{
|
||||
private final Set<Item> items;
|
||||
private final CompoundTag nbt;
|
||||
private final NbtPredicate predicate;
|
||||
protected PartialNBTIngredient(Set<Item> items, CompoundTag nbt)
|
||||
{
|
||||
super(items.stream().map(item ->
|
||||
{
|
||||
ItemStack stack = new ItemStack(item);
|
||||
// copy NBT to prevent the stack from modifying the original, as capabilities or vanilla item durability will modify the tag
|
||||
stack.setTag(nbt.copy());
|
||||
return new Ingredient.ItemValue(stack);
|
||||
}));
|
||||
if (items.isEmpty())
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot create a PartialNBTIngredient with no items");
|
||||
}
|
||||
this.items = Collections.unmodifiableSet(items);
|
||||
this.nbt = nbt;
|
||||
this.predicate = new NbtPredicate(nbt);
|
||||
}
|
||||
|
||||
/** Creates a new ingredient matching any item from the list, containing the given NBT */
|
||||
public static PartialNBTIngredient of(CompoundTag nbt, ItemLike... items)
|
||||
{
|
||||
return new PartialNBTIngredient(Arrays.stream(items).map(ItemLike::asItem).collect(Collectors.toSet()), nbt);
|
||||
}
|
||||
|
||||
/** Creates a new ingredient matching the given item, containing the given NBT */
|
||||
public static PartialNBTIngredient of(ItemLike item, CompoundTag nbt)
|
||||
{
|
||||
return new PartialNBTIngredient(Set.of(item.asItem()), nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable ItemStack input)
|
||||
{
|
||||
if (input == null)
|
||||
return false;
|
||||
return items.contains(input.getItem()) && predicate.matches(input.getShareTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimple()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIngredientSerializer<? extends Ingredient> getSerializer()
|
||||
{
|
||||
return Serializer.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("type", CraftingHelper.getID(Serializer.INSTANCE).toString());
|
||||
if (items.size() == 1)
|
||||
{
|
||||
json.addProperty("item", items.iterator().next().getRegistryName().toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonArray items = new JsonArray();
|
||||
// ensure the order of items in the set is deterministic when saved to JSON
|
||||
this.items.stream().map(Item::getRegistryName).sorted().forEach(name -> items.add(name.toString()));
|
||||
json.add("items", items);
|
||||
}
|
||||
json.addProperty("nbt", nbt.toString());
|
||||
return json;
|
||||
}
|
||||
|
||||
public static class Serializer implements IIngredientSerializer<PartialNBTIngredient>
|
||||
{
|
||||
public static final Serializer INSTANCE = new Serializer();
|
||||
|
||||
@Override
|
||||
public PartialNBTIngredient parse(JsonObject json)
|
||||
{
|
||||
// parse items
|
||||
Set<Item> items;
|
||||
if (json.has("item"))
|
||||
items = Set.of(CraftingHelper.getItem(GsonHelper.getAsString(json, "item"), true));
|
||||
else if (json.has("items"))
|
||||
{
|
||||
ImmutableSet.Builder<Item> builder = ImmutableSet.builder();
|
||||
JsonArray itemArray = GsonHelper.getAsJsonArray(json, "items");
|
||||
for (int i = 0; i < itemArray.size(); i++)
|
||||
{
|
||||
builder.add(CraftingHelper.getItem(GsonHelper.convertToString(itemArray.get(i), "items[" + i + ']'), true));
|
||||
}
|
||||
items = builder.build();
|
||||
}
|
||||
else
|
||||
throw new JsonSyntaxException("Must set either 'item' or 'items'");
|
||||
|
||||
// parse NBT
|
||||
if (!json.has("nbt"))
|
||||
throw new JsonSyntaxException("Missing nbt, expected to find a String or JsonObject");
|
||||
CompoundTag nbt = CraftingHelper.getNBT(json.get("nbt"));
|
||||
|
||||
return new PartialNBTIngredient(items, nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartialNBTIngredient parse(FriendlyByteBuf buffer)
|
||||
{
|
||||
Set<Item> items = Stream.generate(() -> buffer.readRegistryIdUnsafe(ForgeRegistries.ITEMS)).limit(buffer.readVarInt()).collect(Collectors.toSet());
|
||||
CompoundTag nbt = buffer.readNbt();
|
||||
return new PartialNBTIngredient(items, Objects.requireNonNull(nbt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buffer, PartialNBTIngredient ingredient)
|
||||
{
|
||||
buffer.writeVarInt(ingredient.items.size());
|
||||
for (Item item : ingredient.items)
|
||||
buffer.writeRegistryIdUnsafe(ForgeRegistries.ITEMS, item);
|
||||
buffer.writeNbt(ingredient.nbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user