[1.18.x] Add 3 new ingredient types, and make existing ingredients compatible with datagen (#8517)

This commit is contained in:
KnightMiner authored and GitHub committed 2022-03-24 22:04:32 +00:00
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);
}
}
}