Bläddra i källkod

3.4.1

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 5 år sedan
förälder
incheckning
18e4629a0f

+ 1 - 1
gradle.properties

@@ -3,7 +3,7 @@ minecraft_version=20w06a
 yarn_version=20w06a+build.3
 fabricloader_version=0.7.8+build.184
 cloth_events_version=1.2.0
-cloth_config_version=2.9.3
+cloth_config_version=2.10
 modmenu_version=1.8.5+build.23
 fabric_api=0.4.30+build.294-1.16
 autoconfig1u=1.2.4

+ 5 - 5
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java

@@ -147,6 +147,11 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         }
     }
     
+    @ApiStatus.Internal
+    public static boolean isDebugModeEnabled() {
+        return System.getProperty("rei.test", "false").equals("true");
+    }
+    
     @Override
     public void onInitializeClient() {
         configManager = new ConfigManagerImpl();
@@ -219,11 +224,6 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         loadTestPlugins();
     }
     
-    @ApiStatus.Internal
-    public static boolean isDebugModeEnabled() {
-        return System.getProperty("rei.test", "false").equals("true");
-    }
-    
     private void loadTestPlugins() {
         if (isDebugModeEnabled()) {
             registerPlugin(new REITestPlugin());

+ 2 - 0
src/main/java/me/shedaniel/rei/api/EntryRegistry.java

@@ -28,6 +28,8 @@ public interface EntryRegistry {
      */
     List<EntryStack> getStacksList();
     
+    List<EntryStack> getPreFilteredList();
+    
     List<ItemStack> appendStacksForItem(Item item);
     
     /**

+ 21 - 0
src/main/java/me/shedaniel/rei/api/EntryStack.java

@@ -135,6 +135,27 @@ public interface EntryStack {
         return hashCode();
     }
     
+    /**
+     * {@link #hashCode()} for {@link #equalsIgnoreAmount(EntryStack)}
+     */
+    default int hashIgnoreAmount() {
+        return hashCode();
+    }
+    
+    /**
+     * {@link #hashCode()} for {@link #equalsIgnoreTags(EntryStack)}
+     */
+    default int hashIgnoreTags() {
+        return hashCode();
+    }
+    
+    /**
+     * {@link #hashCode()} for {@link #equalsIgnoreTagsAndAmount(EntryStack)}
+     */
+    default int hashIgnoreAmountAndTags() {
+        return hashCode();
+    }
+    
     int getZ();
     
     void setZ(int z);

+ 5 - 5
src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java

@@ -122,7 +122,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         }
         ScreenHelper.getSearchField().getBounds().setBounds(getSearchFieldArea());
         this.widgets.add(ScreenHelper.getSearchField());
-        ScreenHelper.getSearchField().setChangedListener(ENTRY_LIST_WIDGET::updateSearch);
+        ScreenHelper.getSearchField().setChangedListener(s -> ENTRY_LIST_WIDGET.updateSearch(s, false));
         if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) {
             widgets.add(leftButton = new ButtonWidget(new Rectangle(bounds.x, bounds.y + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + 5, 16, 16), new TranslatableText("text.rei.left_arrow")) {
                 @Override
@@ -279,7 +279,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
                 @Override
                 public void onPressed() {
                     ConfigManager.getInstance().toggleCraftableOnly();
-                    ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText());
+                    ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText(), true);
                 }
                 
                 @Override
@@ -388,9 +388,9 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
     @Override
     public void render(int mouseX, int mouseY, float delta) {
         List<ItemStack> currentStacks = ClientHelper.getInstance().getInventoryItemsTypes();
-        if (shouldReInit)
+        if (shouldReInit) {
             init();
-        else {
+        } else {
             for (DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
                 if (handler != null && handler.shouldRecalculateArea(!ConfigObject.getInstance().isLeftHandSidePanel(), bounds)) {
                     init();
@@ -400,7 +400,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         }
         if (ConfigManager.getInstance().isCraftableOnlyEnabled() && ((currentStacks.size() != ScreenHelper.inventoryStacks.size()) || !hasSameListContent(new LinkedList<>(ScreenHelper.inventoryStacks), currentStacks))) {
             ScreenHelper.inventoryStacks = currentStacks;
-            ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText());
+            ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText(), true);
         }
         if (OverlaySearchField.isSearching) {
             setBlitOffset(200);

+ 1 - 1
src/main/java/me/shedaniel/rei/gui/OverlaySearchField.java

@@ -27,9 +27,9 @@ public class OverlaySearchField extends TextFieldWidget {
     public static boolean isSearching = false;
     public long keybindFocusTime = -1;
     public int keybindFocusKey = -1;
+    public boolean isMain = true;
     protected Pair<Long, Point> lastClickedDetails = null;
     private List<String> history = Lists.newArrayListWithCapacity(100);
-    public boolean isMain = true;
     
     public OverlaySearchField(int x, int y, int width, int height) {
         super(x, y, width, height);

+ 14 - 14
src/main/java/me/shedaniel/rei/gui/RecipeDisplayExporter.java

@@ -43,6 +43,20 @@ public final class RecipeDisplayExporter extends Widget {
         INSTANCE.exportRecipe(rectangle, widgets);
     }
     
+    private static File getExportFilename(File directory) {
+        String string = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date());
+        int i = 1;
+        
+        while (true) {
+            File file = new File(directory, "REI_" + string + (i == 1 ? "" : "_" + i) + ".png");
+            if (!file.exists()) {
+                return file;
+            }
+            
+            ++i;
+        }
+    }
+    
     @SuppressWarnings("deprecation")
     private void exportRecipe(Rectangle rectangle, List<Widget> widgets) {
         Framebuffer framebuffer = new Framebuffer(rectangle.width * 8, rectangle.height * 8, true, MinecraftClient.IS_SYSTEM_MAC);
@@ -176,20 +190,6 @@ public final class RecipeDisplayExporter extends Widget {
         });
     }
     
-    private static File getExportFilename(File directory) {
-        String string = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date());
-        int i = 1;
-        
-        while (true) {
-            File file = new File(directory, "REI_" + string + (i == 1 ? "" : "_" + i) + ".png");
-            if (!file.exists()) {
-                return file;
-            }
-            
-            ++i;
-        }
-    }
-    
     @Override
     public void render(int mouseX, int mouseY, float delta) {
     

+ 17 - 17
src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java

@@ -6,6 +6,7 @@
 package me.shedaniel.rei.gui.config.entry;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.mojang.blaze3d.systems.RenderSystem;
 import me.shedaniel.clothconfig2.ClothConfigInitializer;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
@@ -38,26 +39,23 @@ import org.jetbrains.annotations.Nullable;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Consumer;
 
 import static me.shedaniel.rei.gui.widget.EntryListWidget.entrySize;
 
 @ApiStatus.Internal
 public class FilteringEntry extends AbstractConfigListEntry<List<EntryStack>> {
-    private Consumer<List<EntryStack>> saveConsumer;
-    private List<EntryStack> defaultValue;
-    private List<EntryStack> configFiltered;
-    
-    private QueuedTooltip tooltip = null;
-    
-    @SuppressWarnings("rawtypes") private ClothConfigScreen.ListWidget lastList = null;
-    
     protected List<EntryStack> selected = Lists.newArrayList();
-    
     protected double target;
     protected double scroll;
     protected long start;
     protected long duration;
+    private Consumer<List<EntryStack>> saveConsumer;
+    private List<EntryStack> defaultValue;
+    private List<EntryStack> configFiltered;
+    private QueuedTooltip tooltip = null;
+    @SuppressWarnings("rawtypes") private ClothConfigScreen.ListWidget lastList = null;
     private List<EntryStack> entryStacks = null;
     private Rectangle innerBounds;
     private List<EntryListEntry> entries = Collections.emptyList();
@@ -125,6 +123,11 @@ public class FilteringEntry extends AbstractConfigListEntry<List<EntryStack>> {
         this.searchField.isMain = false;
     }
     
+    private static Rectangle updateInnerBounds(Rectangle bounds) {
+        int width = Math.max(MathHelper.floor((bounds.width - 2 - 6) / (float) entrySize()), 1);
+        return new Rectangle((int) (bounds.getCenterX() - width * entrySize() / 2f), bounds.y + 5, width * entrySize(), bounds.height);
+    }
+    
     @SuppressWarnings("rawtypes")
     public Rectangle getBounds() {
         ClothConfigScreen.ListWidget listWidget = getParent();
@@ -295,13 +298,14 @@ public class FilteringEntry extends AbstractConfigListEntry<List<EntryStack>> {
     
     public void updateSearch(String searchTerm) {
         lastSearchArguments = SearchArgument.processSearchTerm(searchTerm);
-        List<EntryStack> list = Lists.newArrayList();
+        Set<EntryStack> list = Sets.newLinkedHashSet();
         for (EntryStack stack : EntryRegistry.getInstance().getStacksList()) {
             if (canLastSearchTermsBeAppliedTo(stack)) {
-                list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.CHECK_TAGS, EntryStack.Settings.TRUE));
+                list.add(stack.copy().setting(EntryStack.Settings.CHECK_AMOUNT, EntryStack.Settings.FALSE).setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.CHECK_TAGS, EntryStack.Settings.TRUE));
             }
         }
-        entryStacks = list;
+        
+        entryStacks = Lists.newArrayList(list);
         updateEntriesPosition();
     }
     
@@ -416,11 +420,6 @@ public class FilteringEntry extends AbstractConfigListEntry<List<EntryStack>> {
             updateEntriesPosition();
     }
     
-    private static Rectangle updateInnerBounds(Rectangle bounds) {
-        int width = Math.max(MathHelper.floor((bounds.width - 2 - 6) / (float) entrySize()), 1);
-        return new Rectangle((int) (bounds.getCenterX() - width * entrySize() / 2f), bounds.y + 5, width * entrySize(), bounds.height);
-    }
-    
     protected final int getMaxScrollPosition() {
         return MathHelper.ceil(entryStacks.size() / (innerBounds.width / (float) entrySize())) * entrySize() + 28;
     }
@@ -529,6 +528,7 @@ public class FilteringEntry extends AbstractConfigListEntry<List<EntryStack>> {
             if (tooltip != null) {
                 FilteringEntry.this.tooltip = tooltip;
             }
+//            System.out.println(getCurrentEntry().getItemStack().toTag(new CompoundTag()).toString());
         }
     }
 }

+ 1 - 1
src/main/java/me/shedaniel/rei/gui/widget/CraftableToggleButtonWidget.java

@@ -21,8 +21,8 @@ import java.util.Optional;
 public abstract class CraftableToggleButtonWidget extends LateRenderedButton {
     
     public static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
-    private ItemRenderer itemRenderer;
     private static final ItemStack ICON = new ItemStack(Blocks.CRAFTING_TABLE);
+    private ItemRenderer itemRenderer;
     
     public CraftableToggleButtonWidget(Rectangle rectangle) {
         super(rectangle, NarratorManager.EMPTY);

+ 17 - 7
src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java

@@ -73,6 +73,7 @@ public class EntryListWidget extends WidgetWithBounds {
     private List<Widget> renders = Collections.emptyList();
     private List<Widget> widgets = Collections.emptyList();
     private List<SearchArgument.SearchArguments> lastSearchArguments = Collections.emptyList();
+    private String lastSearchTerm = null;
     private boolean draggingScrollBar = false;
     
     public static int entrySize() {
@@ -539,9 +540,9 @@ public class EntryListWidget extends WidgetWithBounds {
         if (favoritesListWidget != null)
             favoritesListWidget.updateFavoritesBounds(boundsHandler, searchTerm);
         if (searchTerm != null)
-            updateSearch(searchTerm);
+            updateSearch(searchTerm, true);
         else if (allStacks == null || favorites == null || (favoritesListWidget != null && favoritesListWidget.favorites == null))
-            updateSearch("");
+            updateSearch("", true);
         else
             updateEntriesPosition();
     }
@@ -620,19 +621,24 @@ public class EntryListWidget extends WidgetWithBounds {
     }
     
     public void updateSearch(String searchTerm) {
-        lastSearchArguments = SearchArgument.processSearchTerm(searchTerm);
-        {
+        updateSearch(searchTerm, true);
+    }
+    
+    public void updateSearch(String searchTerm, boolean ignoreLastSearch) {
+        long started = System.nanoTime();
+        if (ignoreLastSearch || this.lastSearchTerm == null || !this.lastSearchTerm.equals(searchTerm)) {
+            this.lastSearchTerm = searchTerm;
+            this.lastSearchArguments = SearchArgument.processSearchTerm(searchTerm);
             List<EntryStack> list = Lists.newArrayList();
             boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled() && !ScreenHelper.inventoryStacks.isEmpty();
             List<EntryStack> workingItems = checkCraftable ? RecipeHelper.getInstance().findCraftableEntriesByItems(CollectionUtils.map(ScreenHelper.inventoryStacks, EntryStack::create)) : null;
-            List<EntryStack> stacks = EntryRegistry.getInstance().getStacksList();
+            List<EntryStack> stacks = EntryRegistry.getInstance().getPreFilteredList();
             if (stacks instanceof CopyOnWriteArrayList) {
                 for (EntryStack stack : stacks) {
                     if (canLastSearchTermsBeAppliedTo(stack)) {
                         if (workingItems != null && CollectionUtils.findFirstOrNullEquals(workingItems, stack) == null)
                             continue;
-                        if (CollectionUtils.findFirstOrNullEquals(ConfigObject.getInstance().getFilteredStacks(), stack) == null)
-                            list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_ENCHANTMENT_GLINT, RENDER_ENCHANTMENT_GLINT));
+                        list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_ENCHANTMENT_GLINT, RENDER_ENCHANTMENT_GLINT));
                     }
                 }
             }
@@ -670,6 +676,10 @@ public class EntryListWidget extends WidgetWithBounds {
         FavoritesListWidget favoritesListWidget = ContainerScreenOverlay.getFavoritesListWidget();
         if (favoritesListWidget != null)
             favoritesListWidget.updateSearch(this, searchTerm);
+        long ended = System.nanoTime();
+        long time = ended - started;
+        if (RoughlyEnoughItemsCore.isDebugModeEnabled())
+            RoughlyEnoughItemsCore.LOGGER.info("[REI] Search Used: %.2fms", time * 1e-6);
         updateEntriesPosition();
     }
     

+ 13 - 0
src/main/java/me/shedaniel/rei/impl/AbstractEntryStack.java

@@ -67,6 +67,19 @@ public abstract class AbstractEntryStack extends DrawableHelper implements Entry
         return equals(stack, !checkTags, !checkAmount);
     }
     
+    @Override
+    public int hashCode() {
+        boolean checkTags = get(Settings.CHECK_TAGS).get();
+        boolean checkAmount = get(Settings.CHECK_AMOUNT).get();
+        if (!checkAmount && !checkTags)
+            return hashIgnoreAmountAndTags();
+        if (!checkAmount)
+            return hashIgnoreAmount();
+        if (!checkTags)
+            return hashIgnoreTags();
+        return hashOfAll();
+    }
+    
     @Override
     public int getZ() {
         return getBlitOffset();

+ 1 - 1
src/main/java/me/shedaniel/rei/impl/ClientHelperImpl.java

@@ -48,6 +48,7 @@ import java.util.UUID;
 @ApiStatus.Internal
 public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     
+    public static ClientHelperImpl instance;
     @ApiStatus.Internal public final Lazy<Boolean> isYog = new Lazy<>(() -> {
         try {
             if (MinecraftClient.getInstance().getSession().getProfile().getId().equals(UUID.fromString("f9546389-9415-4358-9c29-2c26b25bff5b")))
@@ -66,7 +67,6 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
         }
         return false;
     });
-    public static ClientHelperImpl instance;
     private final Map<String, String> modNameCache = Maps.newHashMap();
     
     @Override

+ 43 - 47
src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java

@@ -9,7 +9,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonElement;
-import io.github.prospector.modmenu.ModMenu;
 import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
 import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry;
 import me.sargunvohra.mcmods.autoconfig1u.gui.ConfigScreenProvider;
@@ -20,16 +19,14 @@ import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.JsonObje
 import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.JsonPrimitive;
 import me.sargunvohra.mcmods.autoconfig1u.util.Utils;
 import me.shedaniel.cloth.hooks.ScreenHooks;
+import me.shedaniel.clothconfig2.ClothConfigInitializer;
 import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
 import me.shedaniel.clothconfig2.api.Modifier;
 import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.clothconfig2.gui.entries.KeyCodeEntry;
 import me.shedaniel.clothconfig2.gui.entries.TooltipListEntry;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
-import me.shedaniel.rei.api.ConfigManager;
-import me.shedaniel.rei.api.ConfigObject;
-import me.shedaniel.rei.api.EntryStack;
-import me.shedaniel.rei.api.RecipeHelper;
+import me.shedaniel.rei.api.*;
 import me.shedaniel.rei.gui.ConfigReloadingScreen;
 import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.gui.config.RecipeScreenType;
@@ -37,7 +34,6 @@ import me.shedaniel.rei.gui.config.entry.FilteringEntry;
 import me.shedaniel.rei.gui.config.entry.NoFilteringEntry;
 import me.shedaniel.rei.gui.config.entry.RecipeScreenTypeEntry;
 import me.shedaniel.rei.gui.credits.CreditsScreen;
-import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.screen.Screen;
@@ -121,8 +117,12 @@ public class ConfigManagerImpl implements ConfigManager {
     public void saveConfig() {
         if (getConfig().getFavorites() != null)
             getConfig().getFavorites().removeIf(EntryStack::isEmpty);
-        if (getConfig().getFilteredStacks() != null)
+        if (getConfig().getFilteredStacks() != null) {
             getConfig().getFilteredStacks().removeIf(EntryStack::isEmpty);
+            for (EntryStack stack : getConfig().getFilteredStacks()) {
+                stack.setting(EntryStack.Settings.CHECK_AMOUNT, EntryStack.Settings.FALSE).setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.CHECK_TAGS, EntryStack.Settings.TRUE);
+            }
+        }
         ((me.sargunvohra.mcmods.autoconfig1u.ConfigManager<ConfigObjectImpl>) AutoConfig.getConfigHolder(ConfigObjectImpl.class)).save();
     }
     
@@ -149,46 +149,41 @@ public class ConfigManagerImpl implements ConfigManager {
             provider.setOptionFunction((baseI13n, field) -> field.isAnnotationPresent(ConfigObjectImpl.DontApplyFieldName.class) ? baseI13n : String.format("%s.%s", baseI13n, field.getName()));
             provider.setCategoryFunction((baseI13n, categoryName) -> String.format("%s.%s", baseI13n, categoryName));
             provider.setBuildFunction(builder -> {
-                if (FabricLoader.getInstance().isModLoaded("modmenu")) {
-                    builder.getOrCreateCategory("config.roughlyenoughitems.!general").addEntry(new TooltipListEntry<Object>(I18n.translate("config.roughlyenoughitems.smooth_scrolling"), null) {
-                        final int width = 220;
-                        private final AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, this.getFieldName()) {
-                            public void onPress() {
-                                Screen screen = ModMenu.getConfigScreen("cloth-config2", parent);
-                                if (screen != null) {
-                                    MinecraftClient.getInstance().openScreen(screen);
-                                } else
-                                    ModMenu.openConfigScreen("cloth-config2");
-                            }
-                        };
-                        private final List<Element> children = ImmutableList.of(this.buttonWidget);
-                        
-                        public Object getValue() {
-                            return null;
-                        }
-                        
-                        public Optional<Object> getDefaultValue() {
-                            return Optional.empty();
-                        }
-                        
-                        public void save() {
-                        }
-                        
-                        public List<? extends Element> children() {
-                            return this.children;
+                builder.getOrCreateCategory("config.roughlyenoughitems.!general").addEntry(new TooltipListEntry<Object>(I18n.translate("config.roughlyenoughitems.smooth_scrolling"), null) {
+                    int width = 220;
+                    private AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, this.getFieldName()) {
+                        public void onPress() {
+                            Screen screen = ClothConfigInitializer.getConfigBuilder().build();
+                            MinecraftClient.getInstance().openScreen(screen);
                         }
-                        
-                        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
-                            super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
-                            Window window = MinecraftClient.getInstance().getWindow();
-                            this.buttonWidget.active = this.isEditable();
-                            this.buttonWidget.y = y;
-                            this.buttonWidget.x = x + entryWidth / 2 - this.width / 2;
-                            this.buttonWidget.setWidth(this.width);
-                            this.buttonWidget.render(mouseX, mouseY, delta);
-                        }
-                    });
-                }
+                    };
+                    private List<Element> children = ImmutableList.of(this.buttonWidget);
+                    
+                    public Object getValue() {
+                        return null;
+                    }
+                    
+                    public Optional<Object> getDefaultValue() {
+                        return Optional.empty();
+                    }
+                    
+                    public void save() {
+                    }
+                    
+                    public List<? extends Element> children() {
+                        return this.children;
+                    }
+                    
+                    public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
+                        super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
+                        Window window = MinecraftClient.getInstance().getWindow();
+                        this.buttonWidget.active = this.isEditable();
+                        this.buttonWidget.y = y;
+                        this.buttonWidget.x = x + entryWidth / 2 - this.width / 2;
+                        this.buttonWidget.setWidth(this.width);
+                        this.buttonWidget.render(mouseX, mouseY, delta);
+                    }
+                });
                 return builder.setAfterInitConsumer(screen -> {
                     if (MinecraftClient.getInstance().getNetworkHandler() != null && MinecraftClient.getInstance().getNetworkHandler().getRecipeManager() != null) {
                         ((ScreenHooks) screen).cloth_addButton(new net.minecraft.client.gui.widget.ButtonWidget(4, 4, 100, 20, I18n.translate("text.rei.reload_config"), buttonWidget -> {
@@ -208,8 +203,9 @@ public class ConfigManagerImpl implements ConfigManager {
                     }));
                 }).setSavingRunnable(() -> {
                     saveConfig();
+                    ((EntryRegistryImpl) EntryRegistry.getInstance()).refilter();
                     if (ScreenHelper.getSearchField() != null)
-                        ContainerScreenOverlay.getEntryListWidget().updateSearch(ScreenHelper.getSearchField().getText());
+                        ContainerScreenOverlay.getEntryListWidget().updateSearch(ScreenHelper.getSearchField().getText(), true);
                 }).build();
             });
             return provider.get();

+ 24 - 24
src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java

@@ -31,30 +31,6 @@ import java.util.List;
 @Config(name = "roughlyenoughitems/config")
 public class ConfigObjectImpl implements ConfigObject, ConfigData {
     
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.FIELD})
-    @interface DontApplyFieldName {}
-    
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.FIELD})
-    @interface UseEnumSelectorInstead {}
-    
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.FIELD})
-    @interface UseSpecialRecipeTypeScreen {}
-    
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.FIELD})
-    @interface UseFilteringScreen {}
-    
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.FIELD})
-    @interface UsePercentage {
-        double min();
-        
-        double max();
-    }
-    
     @ConfigEntry.Category("!general") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName public General general = new General();
     @ConfigEntry.Category("appearance") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName private Appearance appearance = new Appearance();
     @ConfigEntry.Category("modules") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName private Modules modules = new Modules();
@@ -313,6 +289,30 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
         return filtering.filteredStacks;
     }
     
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    @interface DontApplyFieldName {}
+    
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    @interface UseEnumSelectorInstead {}
+    
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    @interface UseSpecialRecipeTypeScreen {}
+    
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    @interface UseFilteringScreen {}
+    
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    @interface UsePercentage {
+        double min();
+        
+        double max();
+    }
+    
     public static class General {
         @ConfigEntry.Gui.Excluded public List<EntryStack> favorites = new ArrayList<>();
         @Comment("Declares whether cheating mode is on.") private boolean cheating = false;

+ 32 - 2
src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java

@@ -7,6 +7,8 @@ package me.shedaniel.rei.impl;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ConfigObject;
 import me.shedaniel.rei.api.EntryRegistry;
 import me.shedaniel.rei.api.EntryStack;
@@ -24,15 +26,24 @@ import java.util.concurrent.CopyOnWriteArrayList;
 @ApiStatus.Internal
 public class EntryRegistryImpl implements EntryRegistry {
     
-    private final CopyOnWriteArrayList<EntryStack> allEntries = Lists.newCopyOnWriteArrayList();
+    private final CopyOnWriteArrayList<EntryStack> preFilteredList = Lists.newCopyOnWriteArrayList();
     private final CopyOnWriteArrayList<EntryStack> entries = Lists.newCopyOnWriteArrayList();
     private final Queue<Pair<EntryStack, Collection<? extends EntryStack>>> queueRegisterEntryStackAfter = Queues.newConcurrentLinkedQueue();
     private List<EntryStack> reloadList;
     private boolean doingDistinct = false;
     
+    private static EntryStack findFirstOrNullEqualsEntryIgnoreAmount(Collection<EntryStack> list, EntryStack obj) {
+        for (EntryStack t : list) {
+            if (t.equalsIgnoreAmount(obj))
+                return t;
+        }
+        return null;
+    }
+    
     public void distinct() {
+        preFilteredList.clear();
         doingDistinct = true;
-        TreeSet<EntryStack> set = new TreeSet<>((i, j) -> i.equalsAll(j) ? 0 : 1);
+        Set<EntryStack> set = Sets.newLinkedHashSet();
         set.addAll(reloadList);
         entries.clear();
         entries.addAll(set);
@@ -58,12 +69,31 @@ public class EntryRegistryImpl implements EntryRegistry {
         return RecipeHelper.getInstance().arePluginsLoading() && !doingDistinct ? reloadList : entries;
     }
     
+    @Override
+    public List<EntryStack> getPreFilteredList() {
+        return preFilteredList;
+    }
+    
+    public void refilter() {
+        long started = System.currentTimeMillis();
+        Set<EntryStack> set = Sets.newLinkedHashSet();
+        set.addAll(ConfigObject.getInstance().getFilteredStacks());
+        preFilteredList.clear();
+        for (EntryStack stack : getStacksList()) {
+            if (findFirstOrNullEqualsEntryIgnoreAmount(set, stack) == null)
+                preFilteredList.add(stack);
+        }
+        long time = System.currentTimeMillis() - started;
+        RoughlyEnoughItemsCore.LOGGER.info("[REI] Refiltered %d entries in %dms.", set.size(), time);
+    }
+    
     public void reset() {
         doingDistinct = false;
         reloadList = Lists.newArrayList();
         queueRegisterEntryStackAfter.clear();
         entries.clear();
         reloadList.clear();
+        preFilteredList.clear();
     }
     
     @Override

+ 18 - 3
src/main/java/me/shedaniel/rei/impl/FluidEntryStack.java

@@ -134,15 +134,30 @@ public class FluidEntryStack extends AbstractEntryStack {
     }
     
     @Override
-    public int hashCode() {
+    public int hashOfAll() {
+        int result = hashIgnoreAmountAndTags();
+        result = 31 * result + amount;
+        return result;
+    }
+    
+    @Override
+    public int hashIgnoreAmountAndTags() {
         int result = 1;
         result = 31 * result + getType().ordinal();
         result = 31 * result + fluid.hashCode();
-        result = 31 * result + amount;
-        result = 31 * result;
         return result;
     }
     
+    @Override
+    public int hashIgnoreTags() {
+        return hashOfAll();
+    }
+    
+    @Override
+    public int hashIgnoreAmount() {
+        return hashIgnoreAmountAndTags();
+    }
+    
     @Nullable
     @Override
     public QueuedTooltip getTooltip(int mouseX, int mouseY) {

+ 91 - 14
src/main/java/me/shedaniel/rei/impl/ItemEntryStack.java

@@ -22,6 +22,9 @@ import net.minecraft.client.render.model.json.ModelTransformation;
 import net.minecraft.client.texture.SpriteAtlasTexture;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.Tag;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.registry.Registry;
 import org.jetbrains.annotations.ApiStatus;
@@ -37,8 +40,6 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
     
     private static final Predicate<BakedModel> IS_SIDE_LIT;
     private static final MatrixStack MATRICES = new MatrixStack();
-    private ItemStack itemStack;
-    private int hash = -1;
     
     static {
         boolean isOn1_15_2 = false;
@@ -52,6 +53,8 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
         IS_SIDE_LIT = isOn1_15_2 ? Executor.call(() -> () -> new ModelSideLit1152Compat()) : Executor.call(() -> () -> new ModelHasDepth1151Compat());
     }
     
+    private ItemStack itemStack;
+    
     public ItemEntryStack(ItemStack itemStack) {
         this.itemStack = itemStack;
     }
@@ -74,7 +77,6 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
     @Override
     public void setAmount(int amount) {
         itemStack.setCount(amount);
-        hash = -1;
     }
     
     @Override
@@ -116,9 +118,65 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
     public boolean equalsIgnoreAmount(EntryStack stack) {
         if (stack.getType() != Type.ITEM)
             return false;
+        if (itemStack.getItem() == Items.GRASS_BLOCK) {
+//            System.out.println(itemStack.toTag(new CompoundTag()).toString() + " " + stack.getItemStack().toTag(new CompoundTag()).toString());
+        }
         if (itemStack.getItem() != stack.getItem())
             return false;
-        return ItemStack.areTagsEqual(itemStack, stack.getItemStack());
+        if (itemStack.getItem() == Items.GRASS_BLOCK) {
+            ItemStack otherStack = stack.getItemStack();
+            CompoundTag o1 = itemStack.getTag();
+            CompoundTag o2 = otherStack.getTag();
+            boolean b = o1 == o2 || ((o1 != null && o2 != null) && equals(o1, o2));
+//            System.out.println(itemStack.toTag(new CompoundTag()).toString() + " " + stack.getItemStack().toTag(new CompoundTag()).toString() + " " + b);
+            return b;
+        }
+        ItemStack otherStack = stack.getItemStack();
+        CompoundTag o1 = itemStack.getTag();
+        CompoundTag o2 = otherStack.getTag();
+        return o1 == o2 || ((o1 != null && o2 != null) && equals(o1, o2));
+    }
+    
+    public boolean equals(CompoundTag o1, CompoundTag o2) {
+        int o1Size = 0;
+        int o2Size = 0;
+        for (String key : o1.getKeys()) {
+            if (key.equals("Count"))
+                continue;
+            o1Size++;
+        }
+        for (String key : o2.getKeys()) {
+            if (key.equals("Count"))
+                continue;
+            o2Size++;
+            if (o2Size > o1Size)
+                return false;
+        }
+        if (o1Size != o2Size)
+            return false;
+        
+        try {
+            for (String key : o1.getKeys()) {
+                if (key.equals("Count"))
+                    continue;
+                Tag value = o1.get(key);
+                Tag otherValue = o2.get(key);
+                if (value == null) {
+                    if (!(otherValue == null && o2.contains(key)))
+                        return false;
+                } else if (value instanceof CompoundTag && otherValue instanceof CompoundTag) {
+                    if (!(value == otherValue || (value != null && otherValue != null) && equals((CompoundTag) value, (CompoundTag) otherValue)))
+                        return false;
+                } else {
+                    if (!value.asString().equals(otherValue.asString()))
+                        return false;
+                }
+            }
+        } catch (ClassCastException | NullPointerException unused) {
+            return false;
+        }
+        
+        return true;
     }
     
     @Override
@@ -131,19 +189,38 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
     }
     
     @Override
-    public int hashCode() {
-        //        if (hash == -1) {
+    public int hashOfAll() {
+        int result = hashIgnoreAmount();
+        result = 31 * result + itemStack.getCount();
+        return result;
+    }
+    
+    @Override
+    public int hashIgnoreTags() {
+        int result = hashIgnoreAmountAndTags();
+        result = 31 * result + itemStack.getCount();
+        return result;
+    }
+    
+    @Override
+    public int hashIgnoreAmount() {
         int result = 1;
         result = 31 * result + getType().hashCode();
         result = 31 * result + itemStack.getItem().hashCode();
-        result = 31 * result + itemStack.getCount();
-        result = 31 * result + (itemStack.hasTag() ? itemStack.getTag().hashCode() : 0);
-        hash = result;
-        //            if (hash == -1) {
-        //                hash = -2;
-        //            }
-        //        }
-        return hash;
+        if (itemStack.hasTag()) {
+            result = 31 * result + itemStack.getTag().asString().hashCode();
+        } else {
+            result = 31 * result;
+        }
+        return result;
+    }
+    
+    @Override
+    public int hashIgnoreAmountAndTags() {
+        int result = 1;
+        result = 31 * result + getType().hashCode();
+        result = 31 * result + itemStack.getItem().hashCode();
+        return result;
     }
     
     @Nullable

+ 1 - 0
src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java

@@ -327,6 +327,7 @@ public class RecipeHelperImpl implements RecipeHelper {
         // Remove duplicate entries
         ((EntryRegistryImpl) EntryRegistry.getInstance()).distinct();
         arePluginsLoading = false;
+        ((EntryRegistryImpl) EntryRegistry.getInstance()).refilter();
         
         // Clear Cache Again!
         ((DisplayHelperImpl) DisplayHelper.getInstance()).resetCache();

+ 4 - 6
src/main/java/me/shedaniel/rei/plugin/cooking/DefaultCookingDisplay.java

@@ -13,13 +13,11 @@ import net.minecraft.block.entity.FurnaceBlockEntity;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.container.Container;
 import net.minecraft.item.Item;
-import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.AbstractCookingRecipe;
 import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
 import org.jetbrains.annotations.ApiStatus;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
@@ -46,6 +44,10 @@ public abstract class DefaultCookingDisplay implements TransferRecipeDisplay {
         this.cookTime = recipe.getCookTime();
     }
     
+    public static List<EntryStack> getFuel() {
+        return fuel;
+    }
+    
     @Override
     public Optional<Identifier> getRecipeLocation() {
         return Optional.ofNullable(recipe).map(AbstractCookingRecipe::getId);
@@ -74,10 +76,6 @@ public abstract class DefaultCookingDisplay implements TransferRecipeDisplay {
         return cookTime;
     }
     
-    public static List<EntryStack> getFuel() {
-        return fuel;
-    }
-    
     @ApiStatus.Internal
     public Optional<AbstractCookingRecipe> getOptionalRecipe() {
         return Optional.ofNullable(recipe);

+ 1 - 1
src/main/java/me/shedaniel/rei/tests/plugin/REITestPlugin.java

@@ -50,7 +50,7 @@ public class REITestPlugin implements REIPluginV0 {
     }
     
     public EntryStack transformStack(EntryStack stack) {
-        stack.setAmount(random.nextInt(Integer.MAX_VALUE));
+        stack.setAmount(random.nextInt(Byte.MAX_VALUE));
         return stack;
     }