Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.jsinco.brewery.api.ingredient;

/**
* Easily hashable ingredient, separate class for clarity
*/
public interface BaseIngredient extends Ingredient {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.jsinco.brewery.api.ingredient;

import java.util.List;

public interface ComplexIngredient extends Ingredient {

/**
* @return A non-empty list with the complex ingredients derivatives
*/
List<BaseIngredient> derivatives();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.stream.Stream;

public record IngredientGroup(String key, Component displayName, List<Ingredient> alternatives) implements Ingredient {
public record IngredientGroup(String key, Component displayName,
List<Ingredient> alternatives) implements ComplexIngredient {
@Override
public @NotNull String getKey() {
return key;
}

@Override
public List<BaseIngredient> derivatives() {
return alternatives
.stream()
.flatMap(alternative -> {
if (alternative instanceof ComplexIngredient complexIngredient) {
return complexIngredient.derivatives().stream();
}
return alternative instanceof BaseIngredient baseIngredient ? Stream.of(baseIngredient) : Stream.empty();
})
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package dev.jsinco.brewery.api.ingredient;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import dev.jsinco.brewery.api.util.BreweryKey;
import dev.jsinco.brewery.api.util.BreweryRegistry;
import dev.jsinco.brewery.api.util.Pair;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* Get an instance of an ingredient from an ItemStack or a string.
Expand All @@ -16,6 +23,9 @@
*/
public interface IngredientManager<I> {

Pattern INGREDIENT_META_DATA_RE = Pattern.compile("\\{(.+)\\}");
Pattern INGREDIENT_META_DATA_ELEMENT_RE = Pattern.compile("([^,{}]+)=([^,{}])");

/**
* Don't use this method on startup, expect unexpected behavior if you do
*
Expand All @@ -31,10 +41,61 @@ public interface IngredientManager<I> {
* </p>
*
* @param ingredientStr A string representing the ingredient
* @return A completable future with an optionally present ingredient, if
* @return A completable future with an optionally present ingredient
*/
CompletableFuture<Optional<Ingredient>> getIngredient(@NotNull String ingredientStr);

/**
* Deserialize the ingredient with meta data. Meta data is within curly brackets
*
* @param serializedIngredient Serialized form of the ingredient
* @return A completable future with an optionally present ingredient
* @throws IllegalArgumentException if the ingredient meta in the string was invalid
*/
default CompletableFuture<Optional<Ingredient>> deserializeIngredient(@NotNull String serializedIngredient) throws IllegalArgumentException {
Matcher matcher = INGREDIENT_META_DATA_RE.matcher(serializedIngredient);
if (!matcher.find()) {
return getIngredient(serializedIngredient);
}
String id = matcher.replaceAll("");
String meta = matcher.group(1);
ImmutableMap.Builder<IngredientMeta<?>, Object> metaBuilder = new ImmutableMap.Builder<>();
for (String extraElement : meta.split(",")) {
Matcher elementMatcher = INGREDIENT_META_DATA_ELEMENT_RE.matcher(extraElement);
Preconditions.checkArgument(elementMatcher.matches(), "Invalid meta ingredient data, invalid pattern: " + extraElement);
String key = elementMatcher.group(1).strip();
String value = elementMatcher.group(2).strip();
IngredientMeta<?> ingredientMeta = BreweryRegistry.INGREDIENT_META.get(BreweryKey.parse(key));
Preconditions.checkArgument(ingredientMeta != null, "Invalid meta ingredient data, unknown key: " + key);
Object deserialized = ingredientMeta.serializer().deserialize(value);
metaBuilder.put(ingredientMeta, deserialized);
}
return getIngredient(id)
.thenApply(ingredientOptional -> ingredientOptional
.map(ingredient -> new IngredientWithMeta(ingredient, metaBuilder.build()))
);
}

/**
* Serialize the ingredient with meta data. Meta data is within curly brackets
*
* @param ingredient Ingredient to serialize
* @return Ingredient in serialized form
* @throws IllegalArgumentException If the ingredient contains invalid meta data
*/
default String serializeIngredient(Ingredient ingredient) throws IllegalArgumentException {
if (!(ingredient instanceof IngredientWithMeta metaIngredient)) {
return ingredient.getKey();
}
Map<IngredientMeta<?>, Object> meta = metaIngredient.meta();
return String.format("%s{%s}", metaIngredient.getKey(),
meta.entrySet()
.stream()
.map(entry -> entry.getKey().key().minimalized() + "=" + entry.getKey().serializer().serializeSafely(entry.getValue()))
.collect(Collectors.joining(","))
);
}

/**
* @param ingredientStr A string with the format [ingredient-name]/[runs]. Allows not specifying runs, where it will default to 1
* @return An ingredient/runs pair
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.jsinco.brewery.api.ingredient;

import dev.jsinco.brewery.api.serialize.Serializer;
import dev.jsinco.brewery.api.util.BreweryKey;
import dev.jsinco.brewery.api.util.BreweryKeyed;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;

public record IngredientMeta<T>(BreweryKey key, Serializer<T> serializer) implements BreweryKeyed {

public static IngredientMeta<Double> SCORE = new IngredientMeta<>(
BreweryKey.parse("score"),
Serializer.compile(String::valueOf, Double::parseDouble, Double.class::isInstance)
);
public static IngredientMeta<Component> DISPLAY_NAME_OVERRIDE = new IngredientMeta<>(
BreweryKey.parse("display_name_override"),
Serializer.compile(MiniMessage.miniMessage()::serialize, MiniMessage.miniMessage()::deserialize, Component.class::isInstance)
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dev.jsinco.brewery.api.ingredient;

import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;

public record IngredientWithMeta(Ingredient ingredient,
Map<IngredientMeta<?>, Object> meta) implements ComplexIngredient {

public IngredientWithMeta {
for (Map.Entry<IngredientMeta<?>, Object> entry : meta.entrySet()) {
Preconditions.checkArgument(entry.getKey().serializer().appliesTo(entry.getValue()), "Invalid meta ingredient data '" + entry.getKey().key().minimalized(), "' for: " + entry.getValue());
}
Preconditions.checkArgument(ingredient instanceof BaseIngredient || ingredient instanceof ComplexIngredient, "Ingredient has to extend complex or base ingredient");
}

@Override
public @NotNull String getKey() {
return ingredient.getKey();
}

@Override
public @NotNull Component displayName() {
Component override = get(IngredientMeta.DISPLAY_NAME_OVERRIDE);
if (override == null) {
return ingredient.displayName();
}
return override;
}

public <T> @Nullable T get(IngredientMeta<T> tKey) {
return (T) meta.get(tKey);
}

@Override
public List<BaseIngredient> derivatives() {
if(ingredient instanceof BaseIngredient baseIngredient) {
return List.of(baseIngredient);
}
if(ingredient instanceof ComplexIngredient complexIngredient) {
return complexIngredient.derivatives();
}
throw new IllegalStateException("Unknown ingredient class, has to be base or complex ingredient");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @param baseIngredient The underlying ingredient for this
* @param score The score of the ingredient
*/
@Deprecated(forRemoval = true)
public record ScoredIngredient(Ingredient baseIngredient, double score) implements Ingredient {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public interface RecipeResult<I> {
I newBrewItem(BrewScore score, Brew brew, Brew.State state);

/**
* @return The recipe item without any extra lore
* @return The recipe item without any meta lore
*/
I newLorelessItem();
}
39 changes: 39 additions & 0 deletions api/src/main/java/dev/jsinco/brewery/api/serialize/Serializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dev.jsinco.brewery.api.serialize;

import com.google.common.base.Preconditions;

import java.util.function.Function;
import java.util.function.Predicate;

public interface Serializer<T> {

T deserialize(String serialized);

String serialize(T deserialized);

default String serializeSafely(Object object){
Preconditions.checkArgument(appliesTo(object), "Invalid deserialization object: " + object);
return serialize((T) object);
}

boolean appliesTo(Object obj);

static <T> Serializer<T> compile(Function<T, String> serialize, Function<String, T> deserialize, Predicate<Object> appliesTo) {
return new Serializer<T>() {
@Override
public T deserialize(String serialized) {
return deserialize.apply(serialized);
}

@Override
public String serialize(T deserialized) {
return serialize.apply(deserialized);
}

@Override
public boolean appliesTo(Object obj) {
return appliesTo.test(obj);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import dev.jsinco.brewery.api.breweries.BarrelType;
import dev.jsinco.brewery.api.breweries.CauldronType;
import dev.jsinco.brewery.api.event.NamedDrunkEvent;
import dev.jsinco.brewery.api.ingredient.IngredientMeta;
import dev.jsinco.brewery.api.structure.StructureMeta;
import dev.jsinco.brewery.api.structure.StructureType;
import org.jetbrains.annotations.Nullable;
Expand All @@ -32,6 +33,7 @@ public class BreweryRegistry<T extends BreweryKeyed> {
public static final BreweryRegistry<BarrelType> BARREL_TYPE = fromEnums(BarrelType.class);
public static final BreweryRegistry<CauldronType> CAULDRON_TYPE = fromEnums(CauldronType.class);
public static final BreweryRegistry<StructureMeta<?>> STRUCTURE_META = (BreweryRegistry<StructureMeta<?>>) fromFields(StructureMeta.class);
public static final BreweryRegistry<IngredientMeta<?>> INGREDIENT_META = (BreweryRegistry<IngredientMeta<?>>) fromFields(IngredientMeta.class);
public static final BreweryRegistry<StructureType> STRUCTURE_TYPE = (BreweryRegistry<StructureType>) fromFields(StructureType.class);
public static final BreweryRegistry<NamedDrunkEvent> DRUNK_EVENT = fromJson("/named_drunk_events.json", NamedDrunkEvent.class);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package dev.jsinco.brewery.bukkit.api.ingredient;

import dev.jsinco.brewery.api.ingredient.BaseIngredient;
import dev.jsinco.brewery.api.ingredient.Ingredient;
import dev.jsinco.brewery.api.util.BreweryKey;
import dev.jsinco.brewery.bukkit.api.integration.ItemIntegration;
import dev.jsinco.brewery.bukkit.ingredient.SimpleIngredient;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;

public class PluginIngredient implements Ingredient {
public class PluginIngredient implements BaseIngredient {
private final ItemIntegration itemIntegration;
private final BreweryKey key;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public void tickInventory() {
}
long timeProcessed = getTimeProcessed();
long processTime = getProcessTime();
// Process has changed one extra tick, to avoid running a sound if the mixture inventory changed
// Process has changed one meta tick, to avoid running a sound if the mixture inventory changed
if (timeProcessed < processTime - 1 || mixture.getInventory().isEmpty()) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import dev.jsinco.brewery.api.effect.DrunkState;
import dev.jsinco.brewery.api.effect.ModifierConsume;
import dev.jsinco.brewery.api.effect.modifier.ModifierDisplay;
import dev.jsinco.brewery.api.ingredient.ComplexIngredient;
import dev.jsinco.brewery.api.ingredient.Ingredient;
import dev.jsinco.brewery.api.ingredient.ScoredIngredient;
import dev.jsinco.brewery.api.util.BreweryKey;
import dev.jsinco.brewery.api.util.Logger;
import dev.jsinco.brewery.api.vector.BreweryLocation;
Expand Down Expand Up @@ -289,8 +289,10 @@ private boolean isIngredient(@Nullable ItemStack itemStack) {
return true;
}
Ingredient ingredient = BukkitIngredientManager.INSTANCE.getIngredient(itemStack);
if (ingredient instanceof ScoredIngredient scoredIngredient) {
ingredient = scoredIngredient.baseIngredient();
if (ingredient instanceof ComplexIngredient complexIngredient) {
return complexIngredient.derivatives()
.stream()
.anyMatch(recipeRegistry::isRegisteredIngredient);
}
return recipeRegistry.isRegisteredIngredient(ingredient);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package dev.jsinco.brewery.bukkit.ingredient;

import com.google.common.collect.ImmutableMap;
import dev.jsinco.brewery.api.ingredient.BaseIngredient;
import dev.jsinco.brewery.api.ingredient.IngredientMeta;
import dev.jsinco.brewery.api.ingredient.Ingredient;
import dev.jsinco.brewery.api.ingredient.ScoredIngredient;
import dev.jsinco.brewery.api.ingredient.IngredientWithMeta;
import dev.jsinco.brewery.api.util.BreweryKey;
import dev.jsinco.brewery.bukkit.brew.BrewAdapter;
import dev.jsinco.brewery.configuration.IngredientsSection;
Expand All @@ -16,7 +19,7 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

public class BreweryIngredient implements Ingredient {
public class BreweryIngredient implements BaseIngredient {
protected final BreweryKey ingredientKey;
private final String displayName;

Expand Down Expand Up @@ -62,22 +65,24 @@ public static Optional<Ingredient> from(ItemStack itemStack) {
String displayName = dataContainer.get(BrewAdapter.BREWERY_DISPLAY_NAME, PersistentDataType.STRING);
BreweryKey breweryKey = BreweryKey.parse(key);
displayName = displayName == null ? breweryKey.key() : displayName;
BaseIngredient baseIngredient = new BreweryIngredient(breweryKey, displayName);
ImmutableMap.Builder<IngredientMeta<?>, Object> extraBuilder = new ImmutableMap.Builder<>();
extraBuilder.put(IngredientMeta.DISPLAY_NAME_OVERRIDE, displayName);
if (score != null) {
return Optional.of(new ScoredIngredient(new BreweryIngredient(breweryKey, displayName), score));
extraBuilder.put(IngredientMeta.SCORE, score);
}
return Optional.of(new BreweryIngredient(breweryKey, displayName));
return Optional.of(new IngredientWithMeta(baseIngredient, extraBuilder.build()));
}

public static Optional<CompletableFuture<Optional<Ingredient>>> from(String id) {
if (!id.startsWith("brewery:") && !id.startsWith("#brewery:")) {
public static Optional<CompletableFuture<Optional<Ingredient>>> from(BreweryKey id) {
if (!id.namespace().equals("brewery") && !id.namespace().equals("#brewery")) {
return Optional.empty();
}
BreweryKey breweryKey = BreweryKey.parse(id);
if (breweryKey.namespace().startsWith("#")) {
if (id.namespace().startsWith("#")) {
return Optional.of(IngredientsSection.ingredients()
.getIngredient(id));
}
return Optional.<Ingredient>of(new BreweryIngredient(breweryKey, breweryKey.key()))
return Optional.<Ingredient>of(new BreweryIngredient(id, id.key()))
.map(Optional::of)
.map(CompletableFuture::completedFuture);
}
Expand Down
Loading