From 1ea524d9e734fdde8883c357caa1f918851c1867 Mon Sep 17 00:00:00 2001 From: IMarvinTPA Date: Thu, 23 May 2024 16:50:26 -0400 Subject: [PATCH 1/4] JEI-549: Add Sort API --- .../mezz/jei/common/config/ClientConfig.java | 4 +- .../common/config/IngredientSortStage.java | 97 ++++++++++++++++++- .../IngredientSortStageSerializer.java | 51 ++++++++++ .../jei/api/runtime/IIngredientFilter.java | 16 +++ .../jei/gui/ingredients/IListElementInfo.java | 4 + .../jei/gui/ingredients/IngredientFilter.java | 12 +++ .../gui/ingredients/IngredientFilterApi.java | 8 ++ .../jei/gui/ingredients/IngredientSorter.java | 18 +++- .../IngredientSorterComparators.java | 70 +++++++++++-- .../jei/gui/ingredients/ListElementInfo.java | 25 +++++ 10 files changed, 291 insertions(+), 14 deletions(-) create mode 100644 Common/src/main/java/mezz/jei/common/config/file/serializers/IngredientSortStageSerializer.java diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index b6730fbf0..9f5b87bbb 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -3,7 +3,7 @@ import com.google.common.base.Preconditions; import mezz.jei.common.config.file.IConfigCategoryBuilder; import mezz.jei.common.config.file.IConfigSchemaBuilder; -import mezz.jei.common.config.file.serializers.EnumSerializer; +import mezz.jei.common.config.file.serializers.IngredientSortStageSerializer; import mezz.jei.common.config.file.serializers.ListSerializer; import mezz.jei.common.platform.Services; import org.jetbrains.annotations.Nullable; @@ -90,7 +90,7 @@ public ClientConfig(IConfigSchemaBuilder schema) { ingredientSorterStages = sorting.addList( "IngredientSortStages", IngredientSortStage.defaultStages, - new ListSerializer<>(new EnumSerializer<>(IngredientSortStage.class)), + new ListSerializer(new IngredientSortStageSerializer()), "Sorting order for the ingredient list" ); } diff --git a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java index 818be063d..03df9688b 100644 --- a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java +++ b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java @@ -1,13 +1,106 @@ package mezz.jei.common.config; +import java.util.ArrayList; import java.util.List; -public enum IngredientSortStage { - MOD_NAME, INGREDIENT_TYPE, ALPHABETICAL, CREATIVE_MENU, TAG, ARMOR, MAX_DURABILITY; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +public class IngredientSortStage { + private static final Logger LOGGER = LogManager.getLogger(); + public final String name; + public final Boolean customStage; + + private IngredientSortStage(String name, Boolean customStage) { + this.name = name.toUpperCase().trim(); + this.customStage = customStage; + var existingStage = getStage(name); + if (existingStage == null) { + LOGGER.info("Adding new Sort Stage: " + name); + allStages.add(this); + } else if (existingStage.customStage) { + LOGGER.info("Replacing Sort Stage: " + name); + //Replace the existing one, maybe the comparator is somehow different. + allStages.remove(existingStage); + allStages.add(this); + } else { + LOGGER.debug("Ignoring Duplicate Sort Stage: " + name); + } + //Don't replace our built-in stages. + } + + private IngredientSortStage(String name) { + this(name, true); + } + + private static List allStages = new ArrayList(10); + + public static final IngredientSortStage MOD_NAME = new IngredientSortStage("MOD_NAME", false); + public static final IngredientSortStage INGREDIENT_TYPE = new IngredientSortStage("INGREDIENT_TYPE", false); + public static final IngredientSortStage ALPHABETICAL = new IngredientSortStage("ALPHABETICAL", false); + public static final IngredientSortStage CREATIVE_MENU = new IngredientSortStage("CREATIVE_MENU", false); + public static final IngredientSortStage TAG = new IngredientSortStage("TAG", false); + public static final IngredientSortStage ARMOR = new IngredientSortStage("ARMOR", false); + public static final IngredientSortStage MAX_DURABILITY = new IngredientSortStage("MAX_DURABILITY", false); public static final List defaultStages = List.of( IngredientSortStage.MOD_NAME, IngredientSortStage.INGREDIENT_TYPE, IngredientSortStage.CREATIVE_MENU ); + + public static List getAllStages() { + return new ArrayList(allStages); + } + + public static IngredientSortStage getOrCreateStage(String name) { + var stage = getStage(name); + if (stage == null) { + stage = new IngredientSortStage(name, true); + } + return stage; + } + + public static IngredientSortStage getStage(String needle) { + needle = needle.toUpperCase().trim(); + LOGGER.debug("All Sort Stage Count: " + allStages.size()); + LOGGER.debug("Searching for Sort Stage: " + needle); + for (IngredientSortStage stage : allStages) { + LOGGER.debug("- Checking Existing Sort Stage: " + stage.name); + if (stage.name.equals(needle)) { + LOGGER.debug("- Matched Existing Sort Stage: " + stage.name); + return stage; + } + } + LOGGER.debug("- Matched No Existing Sort Stage: "); + return null; + } + + public static final List defaultStageNames = List.of( + IngredientSortStage.MOD_NAME.name, + IngredientSortStage.INGREDIENT_TYPE.name, + IngredientSortStage.CREATIVE_MENU.name + ); + + public static List getAllStageNames() { + var names = new ArrayList(allStages.size()); + for(IngredientSortStage stage: allStages) { + names.add(stage.name); + } + return names; + } + + public IngredientSortStage getBestSelf() { + if (!customStage) { + LOGGER.debug(name + " is a standard sort option."); + return this; + } + var otherSelf = getStage(name); + if (otherSelf != null) { + LOGGER.debug("Real Sort Stage exists for " + name); + return otherSelf; + } + return this; + } } diff --git a/Common/src/main/java/mezz/jei/common/config/file/serializers/IngredientSortStageSerializer.java b/Common/src/main/java/mezz/jei/common/config/file/serializers/IngredientSortStageSerializer.java new file mode 100644 index 000000000..00c2ee273 --- /dev/null +++ b/Common/src/main/java/mezz/jei/common/config/file/serializers/IngredientSortStageSerializer.java @@ -0,0 +1,51 @@ +package mezz.jei.common.config.file.serializers; + +import mezz.jei.api.runtime.config.IJeiConfigValueSerializer; +import mezz.jei.common.config.IngredientSortStage; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; + +public class IngredientSortStageSerializer implements IJeiConfigValueSerializer { + + public IngredientSortStageSerializer() { + } + + @Override + public String serialize(IngredientSortStage value) { + return value.name; + } + + @Override + public DeserializeResult deserialize(String string) { + string = string.trim(); + if (string.startsWith("\"") && string.endsWith("\"")) { + string = string.substring(1, string.length() - 1); + } + //Since valid values could be added after we read the config, we can't validate them yet. + var stage = IngredientSortStage.getOrCreateStage(string); + return new DeserializeResult<>(stage); + } + + @Override + public String getValidValuesDescription() { + String names = IngredientSortStage.getAllStageNames().stream() + .collect(Collectors.joining(", ")); + + return "[%s]".formatted(names); + } + + @Override + public boolean isValid(IngredientSortStage value) { + //TODO: Not sure if this only occurs after addins have a chance to register their sorters. + //If so, we just need to return true. + var stage = IngredientSortStage.getStage(value.name); + return stage != null; + } + + @Override + public Optional> getAllValidValues() { + return Optional.of(IngredientSortStage.getAllStages()); + } +} diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java b/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java index 19be97c3d..f89126d7e 100644 --- a/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java +++ b/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java @@ -4,6 +4,7 @@ import mezz.jei.api.ingredients.IIngredientType; import net.minecraft.world.item.ItemStack; +import java.util.Comparator; import java.util.List; /** @@ -45,4 +46,19 @@ default List getFilteredItemStacks() { * to get all the ingredients known to JEI, not just ones currently shown by the filter */ List getFilteredIngredients(IIngredientType ingredientType); + + /** + * Register your own sorting option here. + * The ItemStack comparator needs to be able to handle isEmpty ItemStack inputs (Never Null.) + * (FluidStacks will be silently converted to bucket ItemStacks). + * + * The Object comparator needs to be able to handle ItemStack, FluidStack, and unknown types + * (Mod specific stacks) inputs. Examples of Mod specific ones are Mekanism's GasStack and + * EnderIO's EnergyIngredient + * + * @since JEI ?.?.? + */ + default void addIngredientListItemStackSorter(String name, Comparator comparator) { + } + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java index 0eb89b0e7..5761436a3 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java @@ -9,6 +9,8 @@ import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.common.config.IIngredientFilterConfig; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; + import org.jetbrains.annotations.Unmodifiable; public interface IListElementInfo { @@ -38,4 +40,6 @@ public interface IListElementInfo { int getSortedIndex(); + ItemStack getCheatItemStack(); + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index 858ff9d8e..3c6f8716e 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -19,6 +19,7 @@ import mezz.jei.gui.search.ElementSearchLowMem; import mezz.jei.gui.search.IElementSearch; import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; @@ -365,4 +366,15 @@ private void notifyListenersOfChange() { listener.onSourceListChanged(); } } + + public void addIngredientListItemStackSorter(String name, Comparator comparator) { + IngredientSorterComparators.AddCustomItemStackComparator(name, comparator); + invalidateCache(); + } + + public void addIngredientListElementSorter(String name, Comparator> comparator) { + IngredientSorterComparators.AddCustomListElementComparator(name, comparator); + invalidateCache(); + } + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilterApi.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilterApi.java index cdd936913..20a0670d6 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilterApi.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilterApi.java @@ -3,8 +3,10 @@ import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.runtime.IIngredientFilter; import mezz.jei.gui.filter.IFilterTextSource; +import net.minecraft.world.item.ItemStack; import mezz.jei.common.util.ErrorUtil; +import java.util.Comparator; import java.util.List; public class IngredientFilterApi implements IIngredientFilter { @@ -31,4 +33,10 @@ public void setFilterText(String filterText) { public List getFilteredIngredients(IIngredientType ingredientType) { return ingredientFilter.getFilteredIngredients(ingredientType); } + + @Override + public void addIngredientListItemStackSorter(String name, Comparator comparator) { + ingredientFilter.addIngredientListItemStackSorter(name, comparator); + } + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java index e9b488e0e..f1c9663e0 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java @@ -2,14 +2,17 @@ import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.common.config.IngredientSortStage; +import mezz.jei.core.util.LoggedTimer; import mezz.jei.common.config.IClientConfig; import mezz.jei.gui.config.IngredientTypeSortingConfig; import mezz.jei.gui.config.ModNameSortingConfig; import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; public final class IngredientSorter implements IIngredientSorter { + //private static final Logger LOGGER = LogManager.getLogger(); private static final Comparator> PRE_SORTED = Comparator.comparing(IListElementInfo::getSortedIndex); @@ -18,6 +21,7 @@ public final class IngredientSorter implements IIngredientSorter { private final IngredientTypeSortingConfig ingredientTypeSortingConfig; private boolean isCacheValid; + private String lastStageOrder = ""; public IngredientSorter(IClientConfig clientConfig, ModNameSortingConfig modNameSortingConfig, IngredientTypeSortingConfig ingredientTypeSortingConfig) { this.clientConfig = clientConfig; @@ -28,10 +32,17 @@ public IngredientSorter(IClientConfig clientConfig, ModNameSortingConfig modName @Override public void doPreSort(IngredientFilter ingredientFilter, IIngredientManager ingredientManager) { + LoggedTimer sortTime = new LoggedTimer(); + sortTime.start("Sorting Items"); IngredientSorterComparators comparators = new IngredientSorterComparators(ingredientFilter, ingredientManager, this.modNameSortingConfig, this.ingredientTypeSortingConfig); List ingredientSorterStages = this.clientConfig.getIngredientSorterStages(); + //Remember the stage order so we can tell if it changed later. + lastStageOrder = ingredientSorterStages.stream() + .map(o -> o.name) + .collect(Collectors.joining(", ")); + Comparator> completeComparator = comparators.getComparator(ingredientSorterStages); // Get all of the items sorted with our custom comparator. @@ -43,11 +54,16 @@ public void doPreSort(IngredientFilter ingredientFilter, IIngredientManager ingr element.setSortedIndex(i); } this.isCacheValid = true; + sortTime.stop(); } @Override public Comparator> getComparator(IngredientFilter ingredientFilter, IIngredientManager ingredientManager) { - if (!this.isCacheValid) { + List ingredientSorterStages = this.clientConfig.getIngredientSorterStages(); + String myStageOrderStr = ingredientSorterStages.stream() + .map(o -> o.name) + .collect(Collectors.joining(", ")); + if (!this.isCacheValid || !lastStageOrder.equals(myStageOrderStr)) { doPreSort(ingredientFilter, ingredientManager); } //Now the comparator just uses that index value to order everything. diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java index c9fc3b64d..880300e4d 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java @@ -17,15 +17,22 @@ import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + public class IngredientSorterComparators { + private static final Logger LOGGER = LogManager.getLogger(); private final IngredientFilter ingredientFilter; private final IIngredientManager ingredientManager; private final ModNameSortingConfig modNameSortingConfig; private final IngredientTypeSortingConfig ingredientTypeSortingConfig; + private static HashMap>> customComparators = new HashMap>>(); public IngredientSorterComparators( IngredientFilter ingredientFilter, @@ -47,15 +54,33 @@ public Comparator> getComparator(List i } public Comparator> getComparator(IngredientSortStage ingredientSortStage) { - return switch (ingredientSortStage) { - case ALPHABETICAL -> getAlphabeticalComparator(); - case CREATIVE_MENU -> getCreativeMenuComparator(); - case INGREDIENT_TYPE -> getIngredientTypeComparator(); - case MOD_NAME -> getModNameComparator(); - case TAG -> getTagComparator(); - case ARMOR -> getArmorComparator(); - case MAX_DURABILITY -> getMaxDurabilityComparator(); + //Just return one of the built-in sorts. + switch (ingredientSortStage.name) { + case "ALPHABETICAL": + return getAlphabeticalComparator(); + case "CREATIVE_MENU": + return getCreativeMenuComparator(); + case "INGREDIENT_TYPE": + return getIngredientTypeComparator(); + case "MOD_NAME": + return getModNameComparator(); + case "TAG": + return getTagComparator(); + case "ARMOR": + return getArmorComparator(); + case "MAX_DURABILITY": + return getMaxDurabilityComparator(); }; + + //Find and use a custom sort. + var custom = customComparators.get(ingredientSortStage.name); + if (custom != null) { + return custom; + } + + //Accept and ignore an unknown sort. Mod that added it removed, bad spelling, tried to use it before it was registered, etc. + LOGGER.warn("Sorting option '" + ingredientSortStage.name + "' does not exist, skipping."); + return getNullComparator(); } public Comparator> getDefault() { @@ -64,6 +89,12 @@ public Comparator> getDefault() { .thenComparing(getCreativeMenuComparator()); } + public Comparator> getNullComparator() { + Comparator> nullComparator = + Comparator.comparing(o -> 0); + return nullComparator; + } + private static Comparator> getCreativeMenuComparator() { return Comparator.comparingInt(o -> { IListElement element = o.getElement(); @@ -184,6 +215,27 @@ public static ItemStack getItemStack(IListElementInfo ingredientInfo) { if (ingredient.getIngredient() instanceof ItemStack itemStack) { return itemStack; } - return ItemStack.EMPTY; + return ingredientInfo.getCheatItemStack(); + } + + public static class GenericComparator implements Comparator> { + final private Comparator _itemStackComparator; + public GenericComparator(Comparator comparator) { + this._itemStackComparator = comparator; + } + public int compare(IListElementInfo left, IListElementInfo right) { + return this._itemStackComparator.compare(getItemStack(left), getItemStack(right)); + } + } + + public static IngredientSortStage AddCustomListElementComparator(String comparatorName, Comparator> complexComparator) { + comparatorName = comparatorName.toUpperCase().trim(); + customComparators.put(comparatorName, complexComparator); + return IngredientSortStage.getOrCreateStage(comparatorName); + } + + public static IngredientSortStage AddCustomItemStackComparator(String comparatorName, Comparator itemStackComparator) { + var complexComparator = new GenericComparator(itemStackComparator); + return AddCustomListElementComparator(comparatorName, complexComparator); } } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java index cb93a20d9..f1e8e3918 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java @@ -9,6 +9,7 @@ import mezz.jei.common.util.Translator; import mezz.jei.common.config.IIngredientFilterConfig; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -32,6 +33,7 @@ public class ListElementInfo implements IListElementInfo { private final List modNames; private final ResourceLocation resourceLocation; private int sortedIndex = Integer.MAX_VALUE; + private final ItemStack itemStack; public static Optional> create(IListElement element, IIngredientManager ingredientManager, IModIdHelper modIdHelper) { ITypedIngredient value = element.getTypedIngredient(); @@ -67,6 +69,24 @@ protected ListElementInfo(IListElement element, IIngredientHelper ingredie .toList(); String displayName = IngredientInformationUtil.getDisplayName(ingredient, ingredientHelper); this.displayNameLowercase = Translator.toLowercaseWithLocale(displayName); + ItemStack anItemStack = ItemStack.EMPTY; + try { + anItemStack = ingredientHelper.getCheatItemStack(ingredient); + if (anItemStack == ItemStack.EMPTY) { + String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); + LOGGER.info("Ingredient creates Emtpy ItemStack when cheated. {}", ingredientInfo); + } + if (anItemStack == null) { + String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); + LOGGER.info("Ingredient creates Null ItemStack when cheated. {}", ingredientInfo); + anItemStack = ItemStack.EMPTY; + } + } catch (RuntimeException e) { + anItemStack = ItemStack.EMPTY; + String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); + LOGGER.warn("Ingredient throws error when cheated. {}", ingredientInfo, e); + } + this.itemStack = anItemStack; } @Override @@ -159,4 +179,9 @@ public int getSortedIndex() { return sortedIndex; } + @Override + public ItemStack getCheatItemStack() { + return itemStack; + } + } From 880e39fa08064443ecaaf38c2f6efadcca5fbed6 Mon Sep 17 00:00:00 2001 From: IMarvinTPA Date: Thu, 23 May 2024 22:14:46 -0400 Subject: [PATCH 2/4] Applied spotlessJavaApply. --- .../java/mezz/jei/common/config/IngredientSortStage.java | 6 +++--- .../main/java/mezz/jei/api/runtime/IIngredientFilter.java | 8 ++++---- .../jei/gui/ingredients/IngredientSorterComparators.java | 2 +- .../java/mezz/jei/gui/ingredients/ListElementInfo.java | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java index 03df9688b..d2d746b27 100644 --- a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java +++ b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java @@ -29,7 +29,7 @@ private IngredientSortStage(String name, Boolean customStage) { } //Don't replace our built-in stages. } - + private IngredientSortStage(String name) { this(name, true); } @@ -61,7 +61,7 @@ public static IngredientSortStage getOrCreateStage(String name) { } return stage; } - + public static IngredientSortStage getStage(String needle) { needle = needle.toUpperCase().trim(); LOGGER.debug("All Sort Stage Count: " + allStages.size()); @@ -90,7 +90,7 @@ public static List getAllStageNames() { } return names; } - + public IngredientSortStage getBestSelf() { if (!customStage) { LOGGER.debug(name + " is a standard sort option."); diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java b/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java index f89126d7e..8ecb724dd 100644 --- a/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java +++ b/CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientFilter.java @@ -49,11 +49,11 @@ default List getFilteredItemStacks() { /** * Register your own sorting option here. - * The ItemStack comparator needs to be able to handle isEmpty ItemStack inputs (Never Null.) + * The ItemStack comparator needs to be able to handle isEmpty ItemStack inputs (Never Null.) * (FluidStacks will be silently converted to bucket ItemStacks). - * - * The Object comparator needs to be able to handle ItemStack, FluidStack, and unknown types - * (Mod specific stacks) inputs. Examples of Mod specific ones are Mekanism's GasStack and + * + * The Object comparator needs to be able to handle ItemStack, FluidStack, and unknown types + * (Mod specific stacks) inputs. Examples of Mod specific ones are Mekanism's GasStack and * EnderIO's EnergyIngredient * * @since JEI ?.?.? diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java index 880300e4d..928b0e739 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java @@ -71,7 +71,7 @@ public Comparator> getComparator(IngredientSortStage ingredi case "MAX_DURABILITY": return getMaxDurabilityComparator(); }; - + //Find and use a custom sort. var custom = customComparators.get(ingredientSortStage.name); if (custom != null) { diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java index f1e8e3918..24d95ae27 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java @@ -74,11 +74,11 @@ protected ListElementInfo(IListElement element, IIngredientHelper ingredie anItemStack = ingredientHelper.getCheatItemStack(ingredient); if (anItemStack == ItemStack.EMPTY) { String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); - LOGGER.info("Ingredient creates Emtpy ItemStack when cheated. {}", ingredientInfo); + LOGGER.info("Ingredient creates Emtpy ItemStack when cheated. {}", ingredientInfo); } if (anItemStack == null) { String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); - LOGGER.info("Ingredient creates Null ItemStack when cheated. {}", ingredientInfo); + LOGGER.info("Ingredient creates Null ItemStack when cheated. {}", ingredientInfo); anItemStack = ItemStack.EMPTY; } } catch (RuntimeException e) { From 7fbd55c9dd9ff8b4bddc9c829bff6497a0fa9190 Mon Sep 17 00:00:00 2001 From: IMarvinTPA Date: Fri, 24 May 2024 01:11:35 -0400 Subject: [PATCH 3/4] Tidying up Sort API. --- .../common/config/IngredientSortStage.java | 50 +++++-------------- .../api/ingredients/IIngredientHelper.java | 2 +- .../gui/ingredients/IIngredientSorter.java | 4 ++ .../jei/gui/ingredients/IngredientFilter.java | 11 ++-- .../jei/gui/ingredients/IngredientSorter.java | 16 ++++-- .../IngredientSorterComparators.java | 6 ++- .../jei/gui/ingredients/ListElementInfo.java | 7 ++- .../mezz/jei/gui/startup/JeiGuiStarter.java | 1 + 8 files changed, 45 insertions(+), 52 deletions(-) diff --git a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java index d2d746b27..ade28762b 100644 --- a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java +++ b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java @@ -1,7 +1,9 @@ package mezz.jei.common.config; -import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,12 +20,11 @@ private IngredientSortStage(String name, Boolean customStage) { var existingStage = getStage(name); if (existingStage == null) { LOGGER.info("Adding new Sort Stage: " + name); - allStages.add(this); + allStages.put(name, this); } else if (existingStage.customStage) { LOGGER.info("Replacing Sort Stage: " + name); - //Replace the existing one, maybe the comparator is somehow different. - allStages.remove(existingStage); - allStages.add(this); + //Replace the existing one, maybe the new one is an internal comparator. + allStages.put(name, this); } else { LOGGER.debug("Ignoring Duplicate Sort Stage: " + name); } @@ -34,7 +35,7 @@ private IngredientSortStage(String name) { this(name, true); } - private static List allStages = new ArrayList(10); + private static Map allStages = new HashMap(10); public static final IngredientSortStage MOD_NAME = new IngredientSortStage("MOD_NAME", false); public static final IngredientSortStage INGREDIENT_TYPE = new IngredientSortStage("INGREDIENT_TYPE", false); @@ -50,8 +51,8 @@ private IngredientSortStage(String name) { IngredientSortStage.CREATIVE_MENU ); - public static List getAllStages() { - return new ArrayList(allStages); + public static Collection getAllStages() { + return allStages.values(); } public static IngredientSortStage getOrCreateStage(String name) { @@ -64,17 +65,7 @@ public static IngredientSortStage getOrCreateStage(String name) { public static IngredientSortStage getStage(String needle) { needle = needle.toUpperCase().trim(); - LOGGER.debug("All Sort Stage Count: " + allStages.size()); - LOGGER.debug("Searching for Sort Stage: " + needle); - for (IngredientSortStage stage : allStages) { - LOGGER.debug("- Checking Existing Sort Stage: " + stage.name); - if (stage.name.equals(needle)) { - LOGGER.debug("- Matched Existing Sort Stage: " + stage.name); - return stage; - } - } - LOGGER.debug("- Matched No Existing Sort Stage: "); - return null; + return allStages.get(needle); } public static final List defaultStageNames = List.of( @@ -83,24 +74,7 @@ public static IngredientSortStage getStage(String needle) { IngredientSortStage.CREATIVE_MENU.name ); - public static List getAllStageNames() { - var names = new ArrayList(allStages.size()); - for(IngredientSortStage stage: allStages) { - names.add(stage.name); - } - return names; - } - - public IngredientSortStage getBestSelf() { - if (!customStage) { - LOGGER.debug(name + " is a standard sort option."); - return this; - } - var otherSelf = getStage(name); - if (otherSelf != null) { - LOGGER.debug("Real Sort Stage exists for " + name); - return otherSelf; - } - return this; + public static Collection getAllStageNames() { + return allStages.keySet(); } } diff --git a/CommonApi/src/main/java/mezz/jei/api/ingredients/IIngredientHelper.java b/CommonApi/src/main/java/mezz/jei/api/ingredients/IIngredientHelper.java index 358ab12d1..1d065e801 100644 --- a/CommonApi/src/main/java/mezz/jei/api/ingredients/IIngredientHelper.java +++ b/CommonApi/src/main/java/mezz/jei/api/ingredients/IIngredientHelper.java @@ -74,7 +74,7 @@ default Iterable getColors(V ingredient) { * @return an ItemStack for JEI to give the player, or an empty stack if there is nothing that can be given. */ default ItemStack getCheatItemStack(V ingredient) { - return ItemStack.EMPTY; + return null; } /** diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IIngredientSorter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IIngredientSorter.java index 1cdabadc5..1513867f1 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IIngredientSorter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IIngredientSorter.java @@ -13,4 +13,8 @@ default void doPreSort(IngredientFilter ingredientFilter, IIngredientManager ing default void invalidateCache() { } + + default Boolean hasStageOrderChanged() { + return false; + } } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index 3c6f8716e..232627b34 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -169,10 +169,13 @@ public void onIngredientVisibilityChanged(ITypedIngredient ingredient, bo @Override public List> getIngredientList() { - String filterText = this.filterTextSource.getFilterText(); - filterText = filterText.toLowerCase(); + if (sorter.hasStageOrderChanged()) { + invalidateCache(); + } if (ingredientListCached == null) { - ingredientListCached = getIngredientListUncached(filterText); + String filterText = this.filterTextSource.getFilterText(); + filterText = filterText.toLowerCase(); + ingredientListCached = getIngredientListUncached(filterText); } return ingredientListCached; } @@ -370,11 +373,13 @@ private void notifyListenersOfChange() { public void addIngredientListItemStackSorter(String name, Comparator comparator) { IngredientSorterComparators.AddCustomItemStackComparator(name, comparator); invalidateCache(); + notifyListenersOfChange(); } public void addIngredientListElementSorter(String name, Comparator> comparator) { IngredientSorterComparators.AddCustomListElementComparator(name, comparator); invalidateCache(); + notifyListenersOfChange(); } } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java index f1c9663e0..b8e0d8e59 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java @@ -59,11 +59,8 @@ public void doPreSort(IngredientFilter ingredientFilter, IIngredientManager ingr @Override public Comparator> getComparator(IngredientFilter ingredientFilter, IIngredientManager ingredientManager) { - List ingredientSorterStages = this.clientConfig.getIngredientSorterStages(); - String myStageOrderStr = ingredientSorterStages.stream() - .map(o -> o.name) - .collect(Collectors.joining(", ")); - if (!this.isCacheValid || !lastStageOrder.equals(myStageOrderStr)) { + if (!this.isCacheValid || hasStageOrderChanged()) { + ingredientFilter.invalidateCache(); doPreSort(ingredientFilter, ingredientManager); } //Now the comparator just uses that index value to order everything. @@ -75,4 +72,13 @@ public void invalidateCache() { this.isCacheValid = false; } + @Override + public Boolean hasStageOrderChanged() { + List ingredientSorterStages = this.clientConfig.getIngredientSorterStages(); + String myStageOrderStr = ingredientSorterStages.stream() + .map(o -> o.name) + .collect(Collectors.joining(", ")); + return !lastStageOrder.equals(myStageOrderStr); + } + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java index 928b0e739..cde64e9bc 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java @@ -215,7 +215,11 @@ public static ItemStack getItemStack(IListElementInfo ingredientInfo) { if (ingredient.getIngredient() instanceof ItemStack itemStack) { return itemStack; } - return ingredientInfo.getCheatItemStack(); + ItemStack aStack = ingredientInfo.getCheatItemStack(); + if (aStack == null) { + aStack = ItemStack.EMPTY; + } + return aStack; } public static class GenericComparator implements Comparator> { diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java index 24d95ae27..33374d205 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java @@ -69,20 +69,19 @@ protected ListElementInfo(IListElement element, IIngredientHelper ingredie .toList(); String displayName = IngredientInformationUtil.getDisplayName(ingredient, ingredientHelper); this.displayNameLowercase = Translator.toLowercaseWithLocale(displayName); - ItemStack anItemStack = ItemStack.EMPTY; + ItemStack anItemStack = null; try { anItemStack = ingredientHelper.getCheatItemStack(ingredient); - if (anItemStack == ItemStack.EMPTY) { + if (anItemStack != null && anItemStack.isEmpty()) { String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); LOGGER.info("Ingredient creates Emtpy ItemStack when cheated. {}", ingredientInfo); } if (anItemStack == null) { String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); LOGGER.info("Ingredient creates Null ItemStack when cheated. {}", ingredientInfo); - anItemStack = ItemStack.EMPTY; } } catch (RuntimeException e) { - anItemStack = ItemStack.EMPTY; + anItemStack = null; String ingredientInfo = ingredientHelper.getErrorInfo(value.getIngredient()); LOGGER.warn("Ingredient throws error when cheated. {}", ingredientInfo, e); } diff --git a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java index d0a6f9859..546c7a286 100644 --- a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java +++ b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java @@ -118,6 +118,7 @@ public static JeiEventHandlers start(IRuntimeRegistration registration) { ); ingredientManager.registerIngredientListener(ingredientFilter); ingredientVisibility.registerListener(ingredientFilter::onIngredientVisibilityChanged); + timer.stop(); IIngredientFilter ingredientFilterApi = new IngredientFilterApi(ingredientFilter, filterTextSource); From 93586ebbe04c0744d0acd1f89436fe346863b66a Mon Sep 17 00:00:00 2001 From: IMarvinTPA Date: Fri, 24 May 2024 18:03:39 -0400 Subject: [PATCH 4/4] Add some config manipulation tools to make it easier to modify SortOrder by JEI and others. Other cleanups for sorting. --- .../mezz/jei/common/config/ClientConfig.java | 43 +++++++++++++++++ .../mezz/jei/common/config/IClientConfig.java | 6 +++ .../common/config/IngredientSortStage.java | 6 ++- .../jei/common/config/JeiClientConfigs.java | 3 +- .../common/config/file/ConfigCategory.java | 3 ++ .../jei/common/config/file/ConfigSchema.java | 27 +++++++++++ .../jei/common/config/file/ConfigValue.java | 46 +++++++++++++++++++ .../runtime/config/IJeiConfigCategory.java | 17 +++++++ .../api/runtime/config/IJeiConfigFile.java | 17 +++++++ .../api/runtime/config/IJeiConfigValue.java | 25 ++++++++++ .../mezz/jei/test/lib/TestClientConfig.java | 14 ++++++ .../jei/gui/ingredients/IngredientSorter.java | 12 ++--- .../IngredientSorterComparators.java | 14 +++++- .../mezz/jei/gui/startup/JeiGuiStarter.java | 1 - 14 files changed, 221 insertions(+), 13 deletions(-) diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 9f5b87bbb..81c2cc87d 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -1,7 +1,10 @@ package mezz.jei.common.config; import com.google.common.base.Preconditions; + +import mezz.jei.api.runtime.config.IJeiConfigValue; import mezz.jei.common.config.file.IConfigCategoryBuilder; +import mezz.jei.common.config.file.IConfigSchema; import mezz.jei.common.config.file.IConfigSchemaBuilder; import mezz.jei.common.config.file.serializers.IngredientSortStageSerializer; import mezz.jei.common.config.file.serializers.ListSerializer; @@ -9,11 +12,14 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Optional; import java.util.function.Supplier; +import java.util.stream.Collectors; public final class ClientConfig implements IClientConfig { @Nullable private static IClientConfig instance; + private Optional configSchema = Optional.empty(); private final Supplier centerSearchBarEnabled; private final Supplier lowMemorySlowSearchEnabled; @@ -95,6 +101,10 @@ public ClientConfig(IConfigSchemaBuilder schema) { ); } + public void setSchema(IConfigSchema schema) { + this.configSchema = Optional.ofNullable(schema); + } + /** * Only use this for hacky stuff like the debug plugin */ @@ -158,4 +168,37 @@ public int getMaxRecipeGuiHeight() { public List getIngredientSorterStages() { return ingredientSorterStages.get(); } + + @Override + public void setIngredientSorterStages(List ingredientSortStages) { + if (configSchema.isEmpty()) { + return; + } + @SuppressWarnings("unchecked") + IJeiConfigValue> stages = (IJeiConfigValue>)configSchema.get().getConfigValue("sorting", "IngredientSortStages").orElseGet(null); + if (stages != null) { + stages.set(ingredientSortStages); + } + + } + + @Override + public String getSerializedIngredientSorterStages() { + return ingredientSorterStages.get().stream() + .map(o -> o.name) + .collect(Collectors.joining(", ")); + } + + @Override + public void setIngredientSorterStages(String ingredientSortStages) { + if (configSchema.isEmpty()) { + return; + } + @SuppressWarnings("unchecked") + IJeiConfigValue> stages = (IJeiConfigValue>)configSchema.get().getConfigValue("sorting", "IngredientSortStages").orElseGet(null); + if (stages != null) { + stages.setUsingSerializedValue(ingredientSortStages); + } + + } } diff --git a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java index 73df9c5af..9442fd77d 100644 --- a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java @@ -28,4 +28,10 @@ public interface IClientConfig { int getMaxRecipeGuiHeight(); List getIngredientSorterStages(); + + void setIngredientSorterStages(List ingredientSortStages); + + String getSerializedIngredientSorterStages(); + + void setIngredientSorterStages(String ingredientSortStages); } diff --git a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java index ade28762b..c3b91e45a 100644 --- a/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java +++ b/Common/src/main/java/mezz/jei/common/config/IngredientSortStage.java @@ -19,7 +19,11 @@ private IngredientSortStage(String name, Boolean customStage) { this.customStage = customStage; var existingStage = getStage(name); if (existingStage == null) { - LOGGER.info("Adding new Sort Stage: " + name); + if (customStage) { + LOGGER.info("Adding Custom Sort Stage: " + name); + } else { + LOGGER.info("Adding Built-in Sort Stage: " + name); + } allStages.put(name, this); } else if (existingStage.customStage) { LOGGER.info("Replacing Sort Stage: " + name); diff --git a/Common/src/main/java/mezz/jei/common/config/JeiClientConfigs.java b/Common/src/main/java/mezz/jei/common/config/JeiClientConfigs.java index 790b16786..d430cac16 100644 --- a/Common/src/main/java/mezz/jei/common/config/JeiClientConfigs.java +++ b/Common/src/main/java/mezz/jei/common/config/JeiClientConfigs.java @@ -9,7 +9,7 @@ import java.nio.file.Path; public class JeiClientConfigs implements IJeiClientConfigs { - private final IClientConfig clientConfig; + private final ClientConfig clientConfig; private final IIngredientFilterConfig ingredientFilterConfig; private final IIngredientGridConfig ingredientListConfig; private final IIngredientGridConfig bookmarkListConfig; @@ -25,6 +25,7 @@ public JeiClientConfigs(Path configFile) { bookmarkListConfig = new IngredientGridConfig("BookmarkList", builder, HorizontalAlignment.LEFT); schema = builder.build(); + clientConfig.setSchema(schema); } public void register(FileWatcher fileWatcher, ConfigManager configManager) { diff --git a/Common/src/main/java/mezz/jei/common/config/file/ConfigCategory.java b/Common/src/main/java/mezz/jei/common/config/file/ConfigCategory.java index 7d1896318..f26782bac 100644 --- a/Common/src/main/java/mezz/jei/common/config/file/ConfigCategory.java +++ b/Common/src/main/java/mezz/jei/common/config/file/ConfigCategory.java @@ -30,6 +30,8 @@ public String getName() { return name; } + @Override + @Unmodifiable public Optional> getConfigValue(String configValueName) { ConfigValue configValue = valueMap.get(configValueName); return Optional.ofNullable(configValue); @@ -41,6 +43,7 @@ public Collection> getConfigValues() { return this.valueMap.values(); } + @Override public Set getValueNames() { return this.valueMap.keySet(); } diff --git a/Common/src/main/java/mezz/jei/common/config/file/ConfigSchema.java b/Common/src/main/java/mezz/jei/common/config/file/ConfigSchema.java index a7a3a5e86..c5667351c 100644 --- a/Common/src/main/java/mezz/jei/common/config/file/ConfigSchema.java +++ b/Common/src/main/java/mezz/jei/common/config/file/ConfigSchema.java @@ -1,5 +1,7 @@ package mezz.jei.common.config.file; +import mezz.jei.api.runtime.config.IJeiConfigCategory; +import mezz.jei.api.runtime.config.IJeiConfigValue; import mezz.jei.common.config.ConfigManager; import mezz.jei.common.util.DeduplicatingRunner; import org.apache.logging.log4j.LogManager; @@ -11,6 +13,7 @@ import java.nio.file.Path; import java.time.Duration; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; public class ConfigSchema implements IConfigSchema { @@ -79,6 +82,30 @@ public List getCategories() { return categories; } + @Override + public Optional getCategory(String categoryName) { + ConfigCategory found = null; + for (ConfigCategory category : categories) { + if (category.getName().equals(categoryName)) { + found = category; + break; + } + } + return Optional.ofNullable(found); + } + + @Override + public Optional> getConfigValue(String categoryName, String valueName) { + var cat = this.getCategory(categoryName); + + if (cat.isEmpty()) { + return Optional.ofNullable(null); + } + + return cat.get().getConfigValue(valueName); + + } + @Override public Path getPath() { return path; diff --git a/Common/src/main/java/mezz/jei/common/config/file/ConfigValue.java b/Common/src/main/java/mezz/jei/common/config/file/ConfigValue.java index b29f92c2b..0338caf2e 100644 --- a/Common/src/main/java/mezz/jei/common/config/file/ConfigValue.java +++ b/Common/src/main/java/mezz/jei/common/config/file/ConfigValue.java @@ -46,6 +46,14 @@ public T getDefaultValue() { return defaultValue; } + /* + * Allows retreiving the default value without having to know the real data type of the value. + */ + @Override + public String getSerializedDefaultValue() { + return serializer.serialize(defaultValue); + } + @Override public T getValue() { if (schema != null) { @@ -54,11 +62,25 @@ public T getValue() { return currentValue; } + /* + * Allows retreiving the value without having to know the real data type of the value. + */ + @Override + public String getSerializedValue() { + if (schema != null) { + schema.loadIfNeeded(); + } + return serializer.serialize(currentValue); + } + @Override public IJeiConfigValueSerializer getSerializer() { return serializer; } + /* + * This one is for internal loading. + */ public List setFromSerializedValue(String value) { IJeiConfigValueSerializer.IDeserializeResult deserializeResult = serializer.deserialize(value); deserializeResult.getResult() @@ -66,6 +88,30 @@ public List setFromSerializedValue(String value) { return deserializeResult.getErrors(); } + /* + * Update the value without knowing the exact underlying type. + */ + @Override + public boolean setUsingSerializedValue(String value) { + IJeiConfigValueSerializer.IDeserializeResult deserializeResult = serializer.deserialize(value); + if (!deserializeResult.getErrors().isEmpty()) { + LOGGER.error("Tried to set invalid value : {}\n{}", value, serializer.getValidValuesDescription()); + return false; + } + if (deserializeResult.getResult().isPresent()) { + T realValue = deserializeResult.getResult().get(); + if (!currentValue.equals(realValue)) { + currentValue = realValue; + if (schema != null) { + schema.markDirty(); + } + return true; + } + } + return false; + } + + @Override public boolean set(T value) { if (!serializer.isValid(value)) { diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigCategory.java b/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigCategory.java index a6fc3a3fc..0cae831e6 100644 --- a/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigCategory.java +++ b/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigCategory.java @@ -3,6 +3,8 @@ import org.jetbrains.annotations.Unmodifiable; import java.util.Collection; +import java.util.Optional; +import java.util.Set; /** * Categories organize {@link IJeiConfigValue}s into groups. @@ -25,4 +27,19 @@ public interface IJeiConfigCategory { */ @Unmodifiable Collection> getConfigValues(); + + /** + * Get a specific Config Value from this category. + * + * @since ?.?.? + */ + @Unmodifiable + Optional> getConfigValue(String configValueName); + + /** + * Get a list of Config Value names in this category. + * + * @since ?.?.? + */ + Set getValueNames(); } diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigFile.java b/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigFile.java index 333b2277b..a13436ca3 100644 --- a/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigFile.java +++ b/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigFile.java @@ -4,6 +4,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.Optional; /** * Represents one Config file used by JEI. @@ -34,4 +35,20 @@ public interface IJeiConfigFile { */ @Unmodifiable List getCategories(); + + /** + * Get a specific category from this file. + * + * @since ?.?.? + */ + Optional getCategory(String categoryName); + + + /** + * Get a specific Config Value from a category. + * + * @since ?.?.? + */ + Optional> getConfigValue(String categoryName, String valueName) ; + } diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigValue.java b/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigValue.java index 3f3719077..9dbffb8e8 100644 --- a/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigValue.java +++ b/CommonApi/src/main/java/mezz/jei/api/runtime/config/IJeiConfigValue.java @@ -33,6 +33,14 @@ public interface IJeiConfigValue { */ T getValue(); + /** + * Get the serialized current value. + * This will automatically update and load from the config file if there are changes. + * + * @since ?.?.? + */ + String getSerializedValue() ; + /** * Get the default value. * @@ -40,6 +48,14 @@ public interface IJeiConfigValue { */ T getDefaultValue(); + + /** + * Get the serialized default value. + * + * @since ?.?.? + */ + String getSerializedDefaultValue(); + /** * Set the config value to the given value. * This will automatically mark the config file as dirty so that it will save the new values. @@ -48,6 +64,15 @@ public interface IJeiConfigValue { */ boolean set(T value); + + /** + * Set the config value to the given value using the serialized value. + * This will automatically mark the config file as dirty so that it will save the new values. + * + * @since ?.?.? + */ + boolean setUsingSerializedValue(String value); + /** * Get the helper for serializing values to and from Strings, and validating values. * diff --git a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java index 06e4e1172..404d6edce 100644 --- a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java +++ b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java @@ -67,4 +67,18 @@ public int getMaxRecipeGuiHeight() { public List getIngredientSorterStages() { return List.of(); } + + @Override + public void setIngredientSorterStages(List ingredientSortStages) { + } + + @Override + public String getSerializedIngredientSorterStages() { + return ""; + } + + @Override + public void setIngredientSorterStages(String ingredientSortStages) { + } + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java index b8e0d8e59..026813ec0 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorter.java @@ -9,7 +9,6 @@ import java.util.Comparator; import java.util.List; -import java.util.stream.Collectors; public final class IngredientSorter implements IIngredientSorter { //private static final Logger LOGGER = LogManager.getLogger(); @@ -39,9 +38,7 @@ public void doPreSort(IngredientFilter ingredientFilter, IIngredientManager ingr List ingredientSorterStages = this.clientConfig.getIngredientSorterStages(); //Remember the stage order so we can tell if it changed later. - lastStageOrder = ingredientSorterStages.stream() - .map(o -> o.name) - .collect(Collectors.joining(", ")); + lastStageOrder = this.clientConfig.getSerializedIngredientSorterStages(); Comparator> completeComparator = comparators.getComparator(ingredientSorterStages); @@ -74,11 +71,8 @@ public void invalidateCache() { @Override public Boolean hasStageOrderChanged() { - List ingredientSorterStages = this.clientConfig.getIngredientSorterStages(); - String myStageOrderStr = ingredientSorterStages.stream() - .map(o -> o.name) - .collect(Collectors.joining(", ")); - return !lastStageOrder.equals(myStageOrderStr); + String ingredientSorterStages = this.clientConfig.getSerializedIngredientSorterStages(); + return !lastStageOrder.equals(ingredientSorterStages); } } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java index cde64e9bc..60f0aee8a 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientSorterComparators.java @@ -3,6 +3,7 @@ import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.runtime.IIngredientManager; +//import mezz.jei.common.Internal; import mezz.jei.common.config.IngredientSortStage; import mezz.jei.gui.config.IngredientTypeSortingConfig; import mezz.jei.gui.config.ModNameSortingConfig; @@ -234,8 +235,19 @@ public int compare(IListElementInfo left, IListElementInfo right) { public static IngredientSortStage AddCustomListElementComparator(String comparatorName, Comparator> complexComparator) { comparatorName = comparatorName.toUpperCase().trim(); + var stage = IngredientSortStage.getOrCreateStage(comparatorName); + //Trying to decide if I want to do this automatically, it would keep coming + //back if the user removed it. My current position is to let the addin do it. + // var stage = IngredientSortStage.getStage(comparatorName); + // if (stage == null) { + // var configs = Internal.getJeiClientConfigs(); + // var stages = configs.getClientConfig().getIngredientSorterStages(); + // stage = IngredientSortStage.getOrCreateStage(comparatorName); + // stages.add(stage); + // configs.getClientConfig().setIngredientSorterStages(stages); + // } customComparators.put(comparatorName, complexComparator); - return IngredientSortStage.getOrCreateStage(comparatorName); + return stage; } public static IngredientSortStage AddCustomItemStackComparator(String comparatorName, Comparator itemStackComparator) { diff --git a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java index 546c7a286..d0a6f9859 100644 --- a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java +++ b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java @@ -118,7 +118,6 @@ public static JeiEventHandlers start(IRuntimeRegistration registration) { ); ingredientManager.registerIngredientListener(ingredientFilter); ingredientVisibility.registerListener(ingredientFilter::onIngredientVisibilityChanged); - timer.stop(); IIngredientFilter ingredientFilterApi = new IngredientFilterApi(ingredientFilter, filterTextSource);