From 92778560fb4e9b0f37446bb4858b0dcad23bdde6 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 21 Nov 2021 17:21:28 +0800 Subject: Add wildcard matching --- .../shedaniel/rei/api/common/entry/EntryStack.java | 41 +++++++++++++ .../rei/api/common/entry/type/EntryDefinition.java | 70 ++++++++++++++++++++++ .../displays/crafting/DefaultCraftingDisplay.java | 2 +- .../shedaniel/rei/impl/client/view/ViewsImpl.java | 29 +++++---- .../rei/impl/common/entry/AbstractEntryStack.java | 5 ++ .../entry/type/types/BuiltinEntryDefinition.java | 5 ++ .../plugin/client/entry/FluidEntryDefinition.java | 11 +++- .../plugin/client/entry/ItemEntryDefinition.java | 5 ++ 8 files changed, 153 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/me/shedaniel/rei/api/common/entry/EntryStack.java b/api/src/main/java/me/shedaniel/rei/api/common/entry/EntryStack.java index 74529de5c..da37a0432 100644 --- a/api/src/main/java/me/shedaniel/rei/api/common/entry/EntryStack.java +++ b/api/src/main/java/me/shedaniel/rei/api/common/entry/EntryStack.java @@ -52,6 +52,7 @@ import java.util.function.Function; /** * @see me.shedaniel.rei.api.common.util.EntryStacks */ +@ApiStatus.NonExtendable public interface EntryStack extends TextRepresentable, Renderer { static EntryStack empty() { return Internals.getEntryStackProvider().empty(); @@ -122,14 +123,54 @@ public interface EntryStack extends TextRepresentable, Renderer { boolean isEmpty(); + /** + * Returns a copy of this stack. + * The copied stack will retain the same settings applied, with a copied value. + * + * @return a copy for an entry + */ EntryStack copy(); + /** + * Returns a copy of this stack. + * The copied stack will retain the value object, with no settings applied. + * + * @return a copy for an entry + */ default EntryStack rewrap() { return copy(); } + /** + * Returns a copy of this stack. + * The copied stack will have no settings applied. + *

+ * The new value should be functionally equivalent to the original value, + * but should have a normalized state. + *

+ * For example, an {@link net.minecraft.world.item.ItemStack} should have its + * amount removed, but its tags kept. + * + * @return a copy for an entry + */ EntryStack normalize(); + /** + * Returns a copy of this stack. + * The copied stack will have no settings applied. + *

+ * The new value should be the bare minimum to match the original value. + *

+ * For example, an {@link net.minecraft.world.item.ItemStack} should have its + * amount and tags removed. + * + * @return a copy for an entry + * @since 6.2 + */ + default EntryStack wildcard() { + return normalize(); + } + Collection getTagsFor(); @Deprecated diff --git a/api/src/main/java/me/shedaniel/rei/api/common/entry/type/EntryDefinition.java b/api/src/main/java/me/shedaniel/rei/api/common/entry/type/EntryDefinition.java index 81e2a93dd..343a3491c 100644 --- a/api/src/main/java/me/shedaniel/rei/api/common/entry/type/EntryDefinition.java +++ b/api/src/main/java/me/shedaniel/rei/api/common/entry/type/EntryDefinition.java @@ -46,22 +46,92 @@ import java.util.Collection; * @see EntryTypeRegistry */ public interface EntryDefinition { + /** + * Returns the type of the entry. + * + * @return the type of the entry + */ Class getValueType(); + /** + * Returns the type of this definition. The type is also used for comparing the type of two definitions, + * as the definition does not guarantee object and reference equality. + * + * @return the type of this definition + */ EntryType getType(); + /** + * Returns the renderer for this entry, this is used to render the entry, and provide tooltip. + * External plugins can extend this method using {@link me.shedaniel.rei.api.client.entry.renderer.EntryRendererRegistry} + * to provide custom renderers. + * + * @return the renderer for this entry + */ @Environment(EnvType.CLIENT) EntryRenderer getRenderer(); + /** + * Returns the identifier for an entry, used in identifier search argument type, + * and appending the mod name to the tooltip. + * + * @param entry the entry + * @param value the value of the entry + * @return the identifier for an entry + */ @Nullable ResourceLocation getIdentifier(EntryStack entry, T value); + /** + * Returns whether the entry is empty, empty entries are not displayed, + * and are considered invalid. + * Empty entries will be treated equally to {@link EntryStack#empty()}. + * + * @param entry the entry + * @param value the value of the entry + * @return whether the entry is empty + */ boolean isEmpty(EntryStack entry, T value); + /** + * Returns a copy for an entry. + * + * @param entry the entry + * @param value the value of the entry + * @return a copy for an entry + */ T copy(EntryStack entry, T value); + /** + * Returns a normalized copy for an entry. + * The returned stack should be functionally equivalent to the original stack, + * but should have a normalized state. + *

+ * For example, an {@link net.minecraft.world.item.ItemStack} should have its + * amount removed, but its tags kept. + * + * @param entry the entry + * @param value the value of the entry + * @return a normalized copy for an entry + */ T normalize(EntryStack entry, T value); + /** + * Returns a wildcard copy for an entry. + * The returned stack should be the bare minimum to match the original stack. + *

+ * For example, an {@link net.minecraft.world.item.ItemStack} should have its + * amount and tags removed. + * + * @param entry the entry + * @param value the value of the entry + * @return a wildcard copy for an entry + * @since 6.2 + */ + default T wildcard(EntryStack entry, T value) { + return normalize(entry, value); + } + long hash(EntryStack entry, T value, ComparisonContext context); boolean equals(T o1, T o2, ComparisonContext context); diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/crafting/DefaultCraftingDisplay.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/crafting/DefaultCraftingDisplay.java index f37712c05..7138394e2 100644 --- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/crafting/DefaultCraftingDisplay.java +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/crafting/DefaultCraftingDisplay.java @@ -25,7 +25,7 @@ package me.shedaniel.rei.plugin.common.displays.crafting; import dev.architectury.injectables.annotations.ExpectPlatform; import dev.architectury.injectables.annotations.PlatformOnly; -import me.shedaniel.architectury.platform.Platform; +import dev.architectury.platform.Platform; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.display.SimpleGridMenuDisplay; import me.shedaniel.rei.api.common.display.basic.BasicDisplay; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java index a76cce52f..e56c68fc5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java @@ -58,6 +58,7 @@ import org.jetbrains.annotations.ApiStatus; import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; @ApiStatus.Internal public class ViewsImpl implements Views { @@ -69,8 +70,14 @@ public class ViewsImpl implements Views { Stopwatch stopwatch = Stopwatch.createStarted(); Set> categories = builder.getCategories(); - List> recipesFor = builder.getRecipesFor(); - List> usagesFor = builder.getUsagesFor(); + List> recipesForStacks = builder.getRecipesFor(); + List> usagesForStacks = builder.getUsagesFor(); + recipesForStacks = Stream.concat(recipesForStacks.stream(), recipesForStacks.stream().map(EntryStack::wildcard)) + .distinct() + .collect(Collectors.toList()); + usagesForStacks = Stream.concat(usagesForStacks.stream(), usagesForStacks.stream().map(EntryStack::wildcard)) + .distinct() + .collect(Collectors.toList()); DisplayRegistry displayRegistry = DisplayRegistry.getInstance(); Map, List> result = Maps.newLinkedHashMap(); @@ -94,12 +101,12 @@ public class ViewsImpl implements Views { } for (Display display : allRecipesFromCategory) { if (!displayRegistry.isDisplayVisible(display)) continue; - if (!recipesFor.isEmpty()) { + if (!recipesForStacks.isEmpty()) { back: for (List> results : display.getOutputEntries()) { for (EntryStack otherEntry : results) { - for (EntryStack stack : recipesFor) { - if (EntryStacks.equalsFuzzy(otherEntry, stack)) { + for (EntryStack recipesFor : recipesForStacks) { + if (EntryStacks.equalsFuzzy(otherEntry, recipesFor)) { set.add(display); break back; } @@ -107,12 +114,12 @@ public class ViewsImpl implements Views { } } } - if (!usagesFor.isEmpty()) { + if (!usagesForStacks.isEmpty()) { back: for (List> input : display.getInputEntries()) { for (EntryStack otherEntry : input) { - for (EntryStack stack : usagesFor) { - if (EntryStacks.equalsFuzzy(otherEntry, stack)) { + for (EntryStack usagesFor : usagesForStacks) { + if (EntryStacks.equalsFuzzy(otherEntry, usagesFor)) { set.add(display); break back; } @@ -121,8 +128,8 @@ public class ViewsImpl implements Views { } } } - for (EntryStack stack : usagesFor) { - if (isStackWorkStationOfCategory(categoryConfiguration, stack)) { + for (EntryStack usagesFor : usagesForStacks) { + if (isStackWorkStationOfCategory(categoryConfiguration, usagesFor)) { set.addAll(CollectionUtils.filterToSet(allRecipesFromCategory, displayRegistry::isDisplayVisible)); break; } @@ -229,7 +236,7 @@ public class ViewsImpl implements Views { } String message = String.format("Built Recipe View in %s for %d categories, %d recipes for, %d usages for and %d live recipe generators.", - stopwatch.stop(), categories.size(), recipesFor.size(), usagesFor.size(), generatorsCount); + stopwatch.stop(), categories.size(), recipesForStacks.size(), usagesForStacks.size(), generatorsCount); if (ConfigObject.getInstance().doDebugSearchTimeRequired()) { RoughlyEnoughItemsCore.LOGGER.info(message); } else { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/AbstractEntryStack.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/AbstractEntryStack.java index 3e304f41e..1d3486971 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/AbstractEntryStack.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/AbstractEntryStack.java @@ -146,6 +146,11 @@ public abstract class AbstractEntryStack implements EntryStack, Renderer { return wrap(getDefinition().normalize(this, getValue()), false); } + @Override + public EntryStack wildcard() { + return wrap(getDefinition().wildcard(this, getValue()), false); + } + protected EntryStack wrap(A value, boolean copySettings) { TypedEntryStack stack = new TypedEntryStack<>(getDefinition(), value); if (copySettings) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/types/BuiltinEntryDefinition.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/types/BuiltinEntryDefinition.java index 582590f3b..0cfb65533 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/types/BuiltinEntryDefinition.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/types/BuiltinEntryDefinition.java @@ -100,6 +100,11 @@ public class BuiltinEntryDefinition implements EntryDefinition, EntrySeria return value; } + @Override + public T wildcard(EntryStack entry, T value) { + return value; + } + @Override public long hash(EntryStack entry, T value, ComparisonContext context) { return empty ? 0 : Objects.hash(value.getClass().getName(), value); diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/FluidEntryDefinition.java b/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/FluidEntryDefinition.java index 6ddaba0ba..afb0f3613 100644 --- a/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/FluidEntryDefinition.java +++ b/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/FluidEntryDefinition.java @@ -130,9 +130,14 @@ public class FluidEntryDefinition implements EntryDefinition, EntryS public FluidStack normalize(EntryStack entry, FluidStack value) { Fluid fluid = value.getFluid(); if (fluid instanceof FlowingFluid flowingFluid) fluid = flowingFluid.getSource(); - FluidStack copy = FluidStack.create(fluid, value.getAmount(), value.getTag()); - copy.setAmount(FluidStack.bucketAmount()); - return copy; + return FluidStack.create(fluid, FluidStack.bucketAmount(), value.getTag()); + } + + @Override + public FluidStack wildcard(EntryStack entry, FluidStack value) { + Fluid fluid = value.getFluid(); + if (fluid instanceof FlowingFluid) fluid = ((FlowingFluid) fluid).getSource(); + return FluidStack.create(fluid, FluidStack.bucketAmount()); } @Override diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/ItemEntryDefinition.java b/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/ItemEntryDefinition.java index 647d2c7dc..9b34f65d8 100644 --- a/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/ItemEntryDefinition.java +++ b/runtime/src/main/java/me/shedaniel/rei/plugin/client/entry/ItemEntryDefinition.java @@ -128,6 +128,11 @@ public class ItemEntryDefinition implements EntryDefinition, EntrySer return copy; } + @Override + public ItemStack wildcard(EntryStack entry, ItemStack value) { + return new ItemStack(value.getItem(), 1); + } + @Override public long hash(EntryStack entry, ItemStack value, ComparisonContext context) { int code = 1; -- cgit