Skip to content

Commit

Permalink
Rework ContentMatchIngredient a bit
Browse files Browse the repository at this point in the history
Instead of serializing to the generic "inspiratons:content_match", a serializer is now registered for each type. The generic one is kept in case someone adds a type with no ingredient though, may be helpful for modpacks
  • Loading branch information
KnightMiner committed Aug 21, 2020
1 parent 14f7a63 commit e3286d2
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 126 deletions.
Expand Up @@ -5,9 +5,20 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import io.netty.handler.codec.DecoderException;
import knightminer.inspirations.Inspirations;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronColor;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronContents;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronDye;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronPotion;
import knightminer.inspirations.library.recipe.cauldron.contenttype.MapContentType;
import knightminer.inspirations.library.recipe.cauldron.ingredient.ContentMatchIngredient;
import knightminer.inspirations.library.recipe.cauldron.ingredient.ContentTypeIngredient;
import knightminer.inspirations.library.recipe.cauldron.ingredient.FluidCauldronIngredient;
import knightminer.inspirations.library.recipe.cauldron.ingredient.ICauldronIngredient;
import knightminer.inspirations.library.recipe.cauldron.ingredient.ICauldronIngredientSerializer;
import net.minecraft.item.DyeColor;
import net.minecraft.network.PacketBuffer;
import net.minecraft.potion.Potion;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;

Expand All @@ -20,7 +31,19 @@ public class CauldronIngredients {

/* Public constants */

/** Generic content match serializer */
public static final ContentMatchIngredient.Serializer<?,?> MATCH = register("match_content", ContentMatchIngredient.Serializer.GENERIC);
/** Generic content match serializer */
public static final ContentTypeIngredient.Serializer TYPE = register("content_type", new ContentTypeIngredient.Serializer());

/** Fluid content match serializer */
public static final FluidCauldronIngredient.Serializer FLUID = register("fluid", new FluidCauldronIngredient.Serializer());
/** Color content match serializer */
public static final ContentMatchIngredient.Serializer<ICauldronColor,Integer> COLOR = registerMatch(CauldronContentTypes.COLOR);
/** Dye content match serializer */
public static final ContentMatchIngredient.Serializer<ICauldronDye,DyeColor> DYE = registerMatch(CauldronContentTypes.DYE);
/** Fluid content match serializer */
public static final ContentMatchIngredient.Serializer<ICauldronPotion,Potion> POTION = registerMatch(CauldronContentTypes.POTION);

/**
* Registers a new content type
Expand All @@ -34,6 +57,31 @@ public static void register(ResourceLocation name, ICauldronIngredientSerializer
INGREDIENTS.put(name, type);
}

/**
* Helper to register static types
* @param name Inspirations namespace name
* @param type Type to register
* @param <T> Output type
* @return Registered type
*/
private static <T extends ICauldronIngredientSerializer<?>> T register(String name, T type) {
register(Inspirations.getResource(name), type);
return type;
}

/**
* Registers a generic content match type for the given type
* @param mapType Map type instance
* @param <C> Content type
* @param <T> Map value type
* @return Registered serializer
*/
public static <C extends ICauldronContents, T> ContentMatchIngredient.Serializer<C,T> registerMatch(MapContentType<C,T> mapType) {
ContentMatchIngredient.Serializer<C,T> serializer = new ContentMatchIngredient.Serializer<>(mapType);
register(CauldronContentTypes.getName(mapType), serializer);
return serializer;
}

/**
* Gets the type for the given cauldron contents
* @param contents Contents object
Expand Down
Expand Up @@ -10,15 +10,16 @@
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronContents;
import knightminer.inspirations.library.recipe.cauldron.contenttype.CauldronContentType;
import knightminer.inspirations.library.recipe.cauldron.contenttype.MapContentType;
import knightminer.inspirations.recipes.InspirationsRecipes;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import slimeknights.mantle.util.JsonHelper;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

Expand All @@ -28,10 +29,12 @@
* @param <T> Value type
*/
public abstract class ContentMatchIngredient<C extends ICauldronContents,T> implements ICauldronIngredient {
private final ICauldronIngredientSerializer<?> serializer;
protected final MapContentType<C,T> type;

@SuppressWarnings("WeakerAccess")
protected ContentMatchIngredient(MapContentType<C,T> type) {
protected ContentMatchIngredient(ICauldronIngredientSerializer<?> serializer, MapContentType<C,T> type) {
this.serializer = serializer;
this.type = type;
}

Expand All @@ -44,7 +47,7 @@ protected ContentMatchIngredient(MapContentType<C,T> type) {
* @return Ingredient
*/
public static <C extends ICauldronContents,T> ContentMatchIngredient<C,T> of(MapContentType<C,T> type, T value) {
return new Single<>(type, value);
return new Single<>(Serializer.GENERIC, type, value);
}

/**
Expand All @@ -56,7 +59,31 @@ public static <C extends ICauldronContents,T> ContentMatchIngredient<C,T> of(Map
* @return Ingredient
*/
public static <C extends ICauldronContents,T> ContentMatchIngredient<C,T> of(MapContentType<C,T> type, Collection<T> values) {
return new Multi<>(type, ImmutableSet.copyOf(values));
return new Multi<>(Serializer.GENERIC, type, ImmutableSet.copyOf(values));
}

/**
* Creates an instance from the given serializer and value
* @param serializer Serializer to use
* @param value Value
* @param <C> Content type
* @param <T> Value type
* @return Ingredient
*/
public static <C extends ICauldronContents,T> ContentMatchIngredient<C,T> of(Serializer<C,T> serializer, T value) {
return new Single<>(serializer, Objects.requireNonNull(serializer.type), value);
}

/**
* Creates an instance from the given serializer and values
* @param serializer Serializer to use
* @param values Values
* @param <C> Content type
* @param <T> Value type
* @return Ingredient
*/
public static <C extends ICauldronContents,T> ContentMatchIngredient<C,T> of(Serializer<C,T> serializer, Collection<T> values) {
return new Multi<>(serializer, Objects.requireNonNull(serializer.type), ImmutableSet.copyOf(values));
}

/**
Expand Down Expand Up @@ -88,14 +115,14 @@ public boolean test(ICauldronContents contents) {

@Override
public ICauldronIngredientSerializer<?> getSerializer() {
return InspirationsRecipes.contentMatchIngredient;
return serializer;
}

/** Matches a single value */
private static class Single<C extends ICauldronContents, T> extends ContentMatchIngredient<C,T> {
private final T value;
private Single(MapContentType<C,T> type, T value) {
super(type);
private Single(ICauldronIngredientSerializer<?> serializer, MapContentType<C,T> type, T value) {
super(serializer, type);
this.value = value;
}

Expand All @@ -118,8 +145,8 @@ protected void write(PacketBuffer buffer) {
/** Matches from a set */
private static class Multi<C extends ICauldronContents, T> extends ContentMatchIngredient<C,T> {
private final Set<T> values;
private Multi(MapContentType<C,T> type, Set<T> values) {
super(type);
private Multi(ICauldronIngredientSerializer<?> serializer, MapContentType<C,T> type, Set<T> values) {
super(serializer, type);
this.values = values;
}

Expand All @@ -146,7 +173,29 @@ protected void write(PacketBuffer buffer) {
}
}

public static class Serializer implements ICauldronIngredientSerializer<ContentMatchIngredient> {
public static class Serializer<C extends ICauldronContents,T> implements ICauldronIngredientSerializer<ContentMatchIngredient<C,T>> {
/**
* Generic recipe serializer, requires both JSON and packets to include the contents type
*/
public static final Serializer<?,?> GENERIC = new Serializer<>();

@Nullable
private final MapContentType<C,T> type;

/**
* Creates a new serializer using the given type
* @param type Serializer type
*/
public Serializer(MapContentType<C,T> type) {
this.type = type;
}

/**
* Creates a new generic serializer
*/
private Serializer() {
this.type = null;
}

/**
* Helper to get a single value from a string
Expand All @@ -164,6 +213,29 @@ private static <C extends ICauldronContents, T> ContentMatchIngredient<C,T> getS
return of(type, value);
}

/**
* Gets a type from the given name and for the given exception function
* @param name Type name
* @param exception Exception function
* @return Type instance
* @throws RuntimeException if the type is missing or the wrong class type
*/
private static <C extends ICauldronContents, T> MapContentType<C,T> getType(ResourceLocation name, Function<String,RuntimeException> exception) {
CauldronContentType<?> type = CauldronContentTypes.get(name);
// must exist
if (type == null) {
throw exception.apply("Unknown cauldron content type '" + name + "'");
}
// must match type
if (!(type instanceof MapContentType)) {
throw exception.apply("Cauldron content type '" + name + "' does not support content match");
}
// only used by generic type, so type has ?,? generics
//noinspection unchecked
return (MapContentType<C,T>) type;
}


/**
* Helper to get a list of values from a string
* @param type Content type
Expand All @@ -190,15 +262,15 @@ private static <C extends ICauldronContents, T> ContentMatchIngredient<C,T> getL
}

@Override
public ContentMatchIngredient read(JsonObject json) {
ResourceLocation typeName = new ResourceLocation(JSONUtils.getString(json, "match"));
CauldronContentType<?> baseType = CauldronContentTypes.get(typeName);
if (!(baseType instanceof MapContentType)) {
throw new JsonSyntaxException("Cauldron content type '" + typeName + "' does not support content match");
public ContentMatchIngredient<C,T> read(JsonObject json) {
// use the instance type if present
MapContentType<C,T> type = this.type;
// if generic, find a type using the match key
if (type == null) {
type = getType(new ResourceLocation(JSONUtils.getString(json, "match")), JsonSyntaxException::new);
}

// can be string or array
MapContentType<?,?> type = (MapContentType<?,?>) baseType;
// actual element can be a string or array
JsonElement element = JsonHelper.getElement(json, type.getKey());

// single name
Expand All @@ -217,21 +289,23 @@ public ContentMatchIngredient read(JsonObject json) {
}

@Override
public void write(ContentMatchIngredient ingredient, JsonObject json) {
json.addProperty("match", CauldronContentTypes.getName(ingredient.type).toString());
public void write(ContentMatchIngredient<C,T> ingredient, JsonObject json) {
if (this.type == null) {
json.addProperty("match", CauldronContentTypes.getName(ingredient.type).toString());
}
ingredient.write(json);
}

@Override
public ContentMatchIngredient read(PacketBuffer buffer) {
ResourceLocation typeName = buffer.readResourceLocation();
CauldronContentType<?> baseType = CauldronContentTypes.get(typeName);
if (!(baseType instanceof MapContentType)) {
throw new DecoderException("Cauldron content type '" + typeName + "' does not support content match");
public ContentMatchIngredient<C,T> read(PacketBuffer buffer) {
// use the instance type if present
MapContentType<C,T> type = this.type;
// if generic, find a type using the match key
if (type == null) {
type = getType(buffer.readResourceLocation(), DecoderException::new);
}

// read the number told
MapContentType<?,?> type = (MapContentType<?,?>) baseType;
// read all values from the buffer
int size = buffer.readVarInt();
List<String> names = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Expand All @@ -243,8 +317,11 @@ public ContentMatchIngredient read(PacketBuffer buffer) {
}

@Override
public void write(ContentMatchIngredient ingredient, PacketBuffer buffer) {
buffer.writeResourceLocation(CauldronContentTypes.getName(ingredient.type));
public void write(ContentMatchIngredient<C,T> ingredient, PacketBuffer buffer) {
// only write the type to the buffer if this is the generic type
if (this.type == null) {
buffer.writeResourceLocation(CauldronContentTypes.getName(ingredient.type));
}
ingredient.write(buffer);
}
}
Expand Down
Expand Up @@ -4,9 +4,9 @@
import com.google.gson.JsonSyntaxException;
import io.netty.handler.codec.DecoderException;
import knightminer.inspirations.library.recipe.cauldron.CauldronContentTypes;
import knightminer.inspirations.library.recipe.cauldron.CauldronIngredients;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronContents;
import knightminer.inspirations.library.recipe.cauldron.contenttype.CauldronContentType;
import knightminer.inspirations.recipes.InspirationsRecipes;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
Expand Down Expand Up @@ -41,7 +41,7 @@ public boolean test(ICauldronContents contents) {

@Override
public ICauldronIngredientSerializer<?> getSerializer() {
return InspirationsRecipes.contentTypeIngredient;
return CauldronIngredients.TYPE;
}

public static class Serializer implements ICauldronIngredientSerializer<ContentTypeIngredient> {
Expand Down

0 comments on commit e3286d2

Please sign in to comment.