From 9873de760f9f398e5bb0ecb8a769244801879b2d Mon Sep 17 00:00:00 2001 From: slprime Date: Fri, 26 Jan 2024 17:53:31 +0200 Subject: [PATCH 1/2] Change Search Recipe Algorithm --- src/main/java/codechicken/nei/ItemList.java | 13 +- .../java/codechicken/nei/NEIClientConfig.java | 9 +- .../codechicken/nei/api/IRecipeFilter.java | 40 --- .../codechicken/nei/recipe/GuiRecipe.java | 290 ++++++++++-------- .../nei/recipe/IRecipeHandler.java | 8 - .../nei/recipe/RecipeHandlerQuery.java | 8 +- .../nei/recipe/SearchRecipeHandler.java | 120 ++++++++ .../nei/recipe/TemplateRecipeHandler.java | 164 ++-------- 8 files changed, 318 insertions(+), 334 deletions(-) create mode 100644 src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index 0fb9a667e..23efae78a 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -1,6 +1,7 @@ package codechicken.nei; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -84,20 +85,18 @@ public AllMultiItemFilter(List filters) { this.filters = filters; } - public AllMultiItemFilter() { - this(new LinkedList<>()); + public AllMultiItemFilter(ItemFilter... filters) { + this(Arrays.asList(filters)); } - public AllMultiItemFilter(ItemFilter a, ItemFilter b) { - this.filters = new ArrayList<>(); - this.filters.add(a); - this.filters.add(b); + public AllMultiItemFilter() { + this(new LinkedList<>()); } @Override public boolean matches(ItemStack item) { for (ItemFilter filter : filters) try { - if (!filter.matches(item)) return false; + if (filter != null && !filter.matches(item)) return false; } catch (Exception e) { NEIClientConfig.logger.error("Exception filtering " + item + " with " + filter, e); } diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index 11d56baa5..54c8616e6 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -432,8 +432,13 @@ private static void setWorldDefaults() { } public static void unloadWorld() { - ItemPanels.bookmarkPanel.saveBookmarks(); - world.saveNBT(); + if (ItemPanels.bookmarkPanel != null) { + ItemPanels.bookmarkPanel.saveBookmarks(); + } + + if (world != null) { + world.saveNBT(); + } } public static int getKeyBinding(String string) { diff --git a/src/main/java/codechicken/nei/api/IRecipeFilter.java b/src/main/java/codechicken/nei/api/IRecipeFilter.java index 65fd942d0..bf5e7d039 100644 --- a/src/main/java/codechicken/nei/api/IRecipeFilter.java +++ b/src/main/java/codechicken/nei/api/IRecipeFilter.java @@ -1,54 +1,14 @@ package codechicken.nei.api; -import java.lang.reflect.Method; -import java.util.HashMap; import java.util.List; import codechicken.nei.PositionedStack; import codechicken.nei.recipe.IRecipeHandler; -import codechicken.nei.recipe.TemplateRecipeHandler; public interface IRecipeFilter { public static interface IRecipeFilterProvider { - static HashMap override = new HashMap<>(); - - static boolean filteringAvailable(IRecipeHandler handler) { - return handler instanceof TemplateRecipeHandler && !overrideMethod(handler.getClass().getName()); - } - - static boolean overrideMethod(String className) { - try { - - if (override.containsKey(className)) { - return override.get(className); - } - - String[] methods = new String[] { "getResultStack", "getOtherStacks", "getIngredientStacks" }; - Class cls = Class.forName(className); - - for (String method : methods) { - Method m = cls.getMethod(method, Integer.TYPE); - if (!m.getDeclaringClass().getName().equals("codechicken.nei.recipe.TemplateRecipeHandler")) { - override.put(className, true); - return true; - } - } - - Method m = cls.getMethod("numRecipes"); - if (!m.getDeclaringClass().getName().equals("codechicken.nei.recipe.TemplateRecipeHandler")) { - override.put(className, true); - return true; - } - - override.put(className, false); - return false; - } catch (Throwable e) { - return true; - } - } - public IRecipeFilter getFilter(); } diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipe.java b/src/main/java/codechicken/nei/recipe/GuiRecipe.java index 812d9528f..0e133b080 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipe.java @@ -13,7 +13,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; @@ -112,7 +111,7 @@ public abstract class GuiRecipe extends GuiContainer i private final Rectangle area = new Rectangle(); private final GuiRecipeTabs recipeTabs; private final GuiRecipeCatalyst guiRecipeCatalyst; - private H handler; + private SearchRecipeHandler handler; private HandlerInfo handlerInfo; private int yShift = 0; @@ -175,10 +174,8 @@ public AllMultiRecipeFilter(List filters) { this.filters = filters; } - public AllMultiRecipeFilter(IRecipeFilter a, IRecipeFilter b) { - this.filters = new ArrayList<>(); - this.filters.add(a); - this.filters.add(b); + public AllMultiRecipeFilter(IRecipeFilter filters) { + this(Arrays.asList(filters)); } public AllMultiRecipeFilter() { @@ -207,22 +204,24 @@ public void execute() { if (currentScreen instanceof GuiRecipe) { final GuiRecipe guiRecipe = (GuiRecipe) currentScreen; - final IRecipeHandler handler = guiRecipe.handler; + final SearchRecipeHandler searchHandler = guiRecipe.handler; - if (handler != null && IRecipeFilterProvider.filteringAvailable(handler)) { + if (searchHandler != null && searchHandler.searchingAvailable()) { if (GuiRecipe.searchField.text().isEmpty()) { - guiRecipe.updateRecipeList(handler, null); + searchHandler.setSearchIndices(null); + guiRecipe.changePage(0); } else { final IRecipeFilter filter = new ItemRecipeFilter(GuiRecipe.searchField.getFilter()); - ArrayList filtered = ((TemplateRecipeHandler) handler).getSearchResult(filter); + ArrayList filtered = searchHandler.getSearchResult(filter); if (filtered == null) { stop(); } if (interrupted()) return; - guiRecipe.updateRecipeList(handler, filtered); + searchHandler.setSearchIndices(filtered); + guiRecipe.changePage(0); } } @@ -233,12 +232,12 @@ public void execute() { }; - public static final RecipeSearchField searchField = new RecipeSearchField("") { + protected static final RecipeSearchField searchField = new RecipeSearchField("") { @Override protected boolean noResults() { GuiScreen currentScreen = NEIClientUtils.mc().currentScreen; - return !(currentScreen instanceof GuiRecipe) || ((GuiRecipe) currentScreen).handler.numRecipes() > 0; + return !(currentScreen instanceof GuiRecipe) || ((GuiRecipe) currentScreen).numRecipes() > 0; } @Override @@ -248,7 +247,7 @@ public void onTextChange(String oldText) { }; - protected static Button toggleSearch = new Button() { + protected static final Button toggleSearch = new Button() { @Override public boolean onButtonPress(boolean rightclick) { @@ -332,7 +331,7 @@ private CompatibilityHacks() { trueGuiTop = guiTop; isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream() - .map(pattern -> pattern.matcher(handler.getHandlerId())).anyMatch(Matcher::matches); + .map(pattern -> pattern.matcher(handler.original.getHandlerId())).anyMatch(Matcher::matches); if (isHeightHackApplied) { // guiTop is the top edge of the recipe screen on the y-axis. Old recipe handlers expect a single paging // widget at the top and at the bottom of the recipe screen with a height of 16px each, but GTNH NEI @@ -462,34 +461,23 @@ private void initOverlayButtons() { itemPresenceCacheRecipe = -1; } - public static IRecipeFilter getRecipeListFilter(IRecipeHandler handler) { - if (IRecipeFilterProvider.filteringAvailable(handler)) { - LinkedList filters = new LinkedList<>(); - - synchronized (recipeFilterers) { - for (IRecipeFilterProvider p : recipeFilterers) { - IRecipeFilter filter = p.getFilter(); - if (filter != null) { - filters.add(filter); - } - } - } - - if (!filters.isEmpty()) { - return filters.size() == 1 ? filters.get(0) : new AllMultiRecipeFilter(filters); - } + public static IRecipeFilter getRecipeListFilter() { + if (recipeFilterers.isEmpty()) { + return null; } - return null; - } + final AllMultiRecipeFilter recipeFilter = new AllMultiRecipeFilter(); - protected void updateRecipeList(IRecipeHandler h, ArrayList filtered) { - synchronized (handler) { - if (handler != null && handler == h && IRecipeFilterProvider.filteringAvailable(handler)) { - ((TemplateRecipeHandler) handler).applySearch(filtered); - changePage(0); + synchronized (recipeFilterers) { + for (IRecipeFilterProvider p : recipeFilterers) { + IRecipeFilter filter = p.getFilter(); + if (filter != null) { + recipeFilter.filters.add(filter); + } } } + + return recipeFilter.filters.size() == 1 ? recipeFilter.filters.get(0) : recipeFilter; } private void checkYShift() { @@ -500,11 +488,11 @@ public void setRecipePage(int idx) { setRecipePage(idx, 0); } - public void setRecipePage(int idx, int recipe) { + public void setRecipePage(int idx, int refIndex) { recipetype = (currenthandlers.size() + idx) % currenthandlers.size(); - handler = currenthandlers.get(recipetype); - handlerInfo = GuiRecipeTab.getHandlerInfo(handler); + handler = new SearchRecipeHandler<>(currenthandlers.get(recipetype)); + handlerInfo = GuiRecipeTab.getHandlerInfo(handler.original); if (!limitToOneRecipe) { searchField.setText(""); @@ -512,7 +500,7 @@ public void setRecipePage(int idx, int recipe) { toggleSearch.state = 0; } - page = Math.min(Math.max(0, recipe), handler.numRecipes() - 1) / getRecipesPerPage(); + page = Math.min(Math.max(0, refIndex), this.numRecipes() - 1) / getRecipesPerPage(); changePage(0); recipeTabs.calcPageNumber(); checkYShift(); @@ -524,26 +512,24 @@ public List getOverlayButtons() { } public int openTargetRecipe(BookmarkRecipeId recipeId) { - int recipe = -1; + int refIndex = -1; int recipetype = 0; this.recipeId = recipeId; if (this.recipeId != null) { for (int j = 0; j < currenthandlers.size(); j++) { - IRecipeHandler localHandler = currenthandlers.get(j); + H localHandler = currenthandlers.get(j); HandlerInfo localHandlerInfo = GuiRecipeTab.getHandlerInfo(localHandler); if (localHandlerInfo.getHandlerName().equals(this.recipeId.handlerName)) { + recipetype = j; if (!this.recipeId.ingredients.isEmpty()) { - for (int i = 0; i < localHandler.numRecipes(); i++) { - if (this.recipeId.equalsIngredients(localHandler.getIngredientStacks(i))) { - recipetype = j; - recipe = i; - break; - } - } + refIndex = SearchRecipeHandler.findFirst( + localHandler, + (recipeIndex) -> this.recipeId + .equalsIngredients(localHandler.getIngredientStacks(recipeIndex))); } break; @@ -551,16 +537,16 @@ public int openTargetRecipe(BookmarkRecipeId recipeId) { } } - setRecipePage(recipetype, Math.max(0, recipe)); + setRecipePage(recipetype, Math.max(0, refIndex)); - return recipe; + return refIndex; } public List getFocusedRecipeIngredients() { - for (int idx : getRecipeIndices()) { - if (recipeInFocus(idx)) { - return handler.getIngredientStacks(idx); + for (int recipeIndex : getRecipeIndices()) { + if (recipeInFocus(recipeIndex)) { + return handler.original.getIngredientStacks(recipeIndex); } } @@ -569,16 +555,16 @@ public List getFocusedRecipeIngredients() { public int prepareFocusedRecipeResultStackSize(ItemStack stackover) { - for (int idx : getRecipeIndices()) { - if (recipeInFocus(idx)) { - final PositionedStack result = handler.getResultStack(idx); + for (int recipeIndex : getRecipeIndices()) { + if (recipeInFocus(recipeIndex)) { + final PositionedStack result = handler.original.getResultStack(recipeIndex); int stackSize = 0; if (result != null && StackInfo.equalItemAndNBT(result.item, stackover, true)) { stackSize += result.item.stackSize; } - final List stacks = handler.getOtherStacks(idx); + final List stacks = handler.original.getOtherStacks(recipeIndex); for (PositionedStack pStack : stacks) { if (StackInfo.equalItemAndNBT(pStack.item, stackover, true)) { stackSize += pStack.item.stackSize; @@ -592,15 +578,15 @@ public int prepareFocusedRecipeResultStackSize(ItemStack stackover) { return stackover.stackSize; } - protected boolean recipeInFocus(int idx) { - final PositionedStack result = handler.getResultStack(idx); - if (result != null && isMouseOver(result, idx)) { + protected boolean recipeInFocus(int recipeIndex) { + final PositionedStack result = handler.original.getResultStack(recipeIndex); + if (result != null && isMouseOver(result, recipeIndex)) { return true; } - final List stacks = handler.getOtherStacks(idx); + final List stacks = handler.original.getOtherStacks(recipeIndex); for (PositionedStack stack : stacks) { - if (isMouseOver(stack, idx)) { + if (isMouseOver(stack, recipeIndex)) { return true; } } @@ -613,15 +599,24 @@ public String getHandlerName() { } public H getHandler() { - return handler; + return handler.original; } public List getRecipeIndices() { final int recipesPerPage = getRecipesPerPage(); - int minIndex = page * recipesPerPage; - int maxIndex = Math.min(handler.numRecipes(), (page + 1) * recipesPerPage); + final int minIndex = page * recipesPerPage; + final int maxIndex = Math.min(this.numRecipes(), (page + 1) * recipesPerPage); + final ArrayList range = new ArrayList<>(); + + for (int index = minIndex; index < maxIndex; index++) { + range.add(handler.ref(index)); + } + + return range; + } - return IntStream.range(minIndex, maxIndex).boxed().collect(Collectors.toList()); + private int numRecipes() { + return handler == null ? 0 : handler.numRecipes(); } @Override @@ -647,8 +642,8 @@ public void keyTyped(char c, int i) { } try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { - for (int recipe : getRecipeIndices()) { - if (handler.keyTyped(this, c, i, recipe)) { + for (int recipeIndex : getRecipeIndices()) { + if (handler.original.keyTyped(this, c, i, recipeIndex)) { return; } } @@ -673,7 +668,7 @@ public void keyTyped(char c, int i) { @Override protected void mouseClicked(int mousex, int mousey, int button) { - if (!limitToOneRecipe && handler != null && IRecipeFilterProvider.filteringAvailable(handler)) { + if (!limitToOneRecipe && handler != null && handler.searchingAvailable()) { if (toggleSearch.contains(mousex - guiLeft, mousey - guiTop)) { toggleSearch.handleClick(mousex - guiLeft, mousey - guiTop, button); } else if (searchField.contains(mousex - guiLeft, mousey - guiTop)) { @@ -681,20 +676,25 @@ protected void mouseClicked(int mousex, int mousey, int button) { } else { searchField.onGuiClick(mousex - guiLeft, mousey - guiTop); } - } try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { - for (int recipe : getRecipeIndices()) if (handler.mouseClicked(this, button, recipe)) return; + for (int recipeIndex : getRecipeIndices()) { + if (handler.original.mouseClicked(this, button, recipeIndex)) { + return; + } + } } - if (recipeTabs.mouseClicked(mousex, mousey, button)) return; + if (recipeTabs.mouseClicked(mousex, mousey, button)) { + return; + } super.mouseClicked(mousex, mousey, button); } @Override - public void mouseScrolled(int i) { + public void mouseScrolled(int scroll) { // Height hacking is not necessary here since mouse scrolling is a new feature, added in // GTNH NEI. So no old mods will use this. Though not hacking the height here does mean that // the value of the height field will be different from in other mouseover methods, which @@ -702,14 +702,18 @@ public void mouseScrolled(int i) { // First, invoke scroll handling over recipe handler tabbar. Makes sure it is not overwritten by recipe // handler-specific scroll behavior. - if (recipeTabs.mouseScrolled(i)) return; + if (recipeTabs.mouseScrolled(scroll)) return; - for (int recipe : getRecipeIndices()) if (handler.mouseScrolled(this, i, recipe)) return; + for (int recipeIndex : getRecipeIndices()) { + if (handler.original.mouseScrolled(this, scroll, recipeIndex)) { + return; + } + } // If shift is held, try switching to the next recipe handler. Replicates the GuiRecipeTabs.mouseScrolled() // without the checking for the cursor being inside the tabbar. if (NEIClientUtils.shiftKey()) { - if (i < 0) { + if (scroll < 0) { nextType(); } else { prevType(); @@ -720,7 +724,7 @@ public void mouseScrolled(int i) { // Finally, if nothing else has handled scrolling, try changing to the next recipe page. if (new Rectangle(guiLeft, guiTop, xSize, ySize).contains(GuiDraw.getMousePosition())) { - if (i > 0) { + if (scroll > 0) { prevPage(); } else { nextPage(); @@ -748,10 +752,13 @@ protected void actionPerformed(GuiButton guibutton) { if (overlayButtons != null && guibutton.id >= OVERLAY_BUTTON_ID_START && guibutton.id < OVERLAY_BUTTON_ID_START + overlayButtons.length) { - mc.displayGuiScreen(firstGui); - overlayRecipe( - page * getRecipesPerPage() + guibutton.id - OVERLAY_BUTTON_ID_START, - NEIClientUtils.shiftKey()); + final List indices = getRecipeIndices(); + final int refIndex = guibutton.id - OVERLAY_BUTTON_ID_START; + + if (refIndex >= 0 && refIndex < indices.size()) { + mc.displayGuiScreen(firstGui); + overlayRecipe(indices.get(refIndex), NEIClientUtils.shiftKey()); + } } } @@ -763,22 +770,26 @@ public void updateScreen() { /** updateScreen() that's safe to call when this isn't the currently open GUI */ public void updateAsTooltip() { - handler.onUpdate(); + handler.original.onUpdate(); refreshPage(); } @Override public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { + try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { - for (int i : getRecipeIndices()) currenttip = handler.handleTooltip(this, currenttip, i); + for (int recipeIndex : getRecipeIndices()) { + currenttip = handler.original.handleTooltip(this, currenttip, recipeIndex); + } } + recipeTabs.handleTooltip(mousex, mousey, currenttip); if (currenttip.isEmpty() && searchField.isVisible() && new Rectangle(searchField.x + searchField.w, 15, 44, 16) .contains(mousex - guiLeft, mousey - guiTop)) { - final String s = String.format("%d/%d", page + 1, (handler.numRecipes() - 1) / getRecipesPerPage() + 1); + final String s = String.format("%d/%d", page + 1, (this.numRecipes() - 1) / getRecipesPerPage() + 1); if (fontRendererObj.getStringWidth(s) >= 45) { currenttip.add(s); @@ -791,8 +802,11 @@ && new Rectangle(searchField.x + searchField.w, 15, 44, 16) @Override public List handleItemTooltip(GuiContainer gui, ItemStack stack, int mousex, int mousey, List currenttip) { + try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { - for (int i : getRecipeIndices()) currenttip = handler.handleItemTooltip(this, stack, currenttip, i); + for (int recipeIndex : getRecipeIndices()) { + currenttip = handler.original.handleItemTooltip(this, stack, currenttip, recipeIndex); + } } return currenttip; @@ -819,19 +833,19 @@ protected void prevType() { setRecipePage(--recipetype); } - protected void overlayRecipe(int recipe, final boolean shift) { + protected void overlayRecipe(int recipeIndex, final boolean shift) { - if (handler == null || !handler.hasOverlay(firstGui, firstGui.inventorySlots, recipe)) { + if (handler == null || !handler.original.hasOverlay(firstGui, firstGui.inventorySlots, recipeIndex)) { return; } final boolean moveItems = shift || !NEIClientConfig.requireShiftForOverlayRecipe(); - final IRecipeOverlayRenderer renderer = handler.getOverlayRenderer(firstGui, recipe); - final IOverlayHandler overlayHandler = handler.getOverlayHandler(firstGui, recipe); + final IRecipeOverlayRenderer renderer = handler.original.getOverlayRenderer(firstGui, recipeIndex); + final IOverlayHandler overlayHandler = handler.original.getOverlayHandler(firstGui, recipeIndex); if (renderer == null || moveItems) { if (overlayHandler != null) { - overlayHandler.overlayRecipe(firstGui, handler, recipe, moveItems); + overlayHandler.overlayRecipe(firstGui, handler.original, recipeIndex, moveItems); } } else { LayoutManager.overlayRenderer = renderer; @@ -840,7 +854,7 @@ protected void overlayRecipe(int recipe, final boolean shift) { protected void changePage(int shift) { final int recipesPerPage = getRecipesPerPage(); - final int numRecipes = handler != null ? handler.numRecipes() : 0; + final int numRecipes = this.numRecipes(); if (numRecipes > 0) { final int numPages = (int) Math.ceil(numRecipes / (float) recipesPerPage); @@ -857,8 +871,8 @@ public void refreshPage() { changePage(0); refreshSlots(); final int recipesPerPage = getRecipesPerPage(); - final boolean multiplepages = handler.numRecipes() > recipesPerPage; - final int numRecipes = Math.min(handler.numRecipes() - (page * recipesPerPage), recipesPerPage); + final boolean multiplepages = this.numRecipes() > recipesPerPage; + final int numRecipes = Math.min(this.numRecipes() - (page * recipesPerPage), recipesPerPage); area.width = handlerInfo.getWidth(); area.height = handlerInfo.getHeight() * numRecipes; @@ -884,12 +898,13 @@ public void refreshPage() { overlay.visible = false; } } else { + final List indices = getRecipeIndices(); for (int i = 0; i < overlayButtons.length; i++) { - if (i >= numRecipes) { + if (i >= indices.size()) { overlayButtons[i].visible = false; } else { - final int curRecipe = page * recipesPerPage + i; - overlayButtons[i].visible = handler.hasOverlay(firstGui, firstGui.inventorySlots, curRecipe); + overlayButtons[i].visible = handler.original + .hasOverlay(firstGui, firstGui.inventorySlots, indices.get(i)); } } } @@ -899,8 +914,11 @@ public void refreshPage() { private void refreshSlots() { slotcontainer.inventorySlots.clear(); - for (int i : getRecipeIndices()) { - Point p = getRecipePosition(i); + final List indices = getRecipeIndices(); + + for (int refIndex = 0; refIndex < indices.size(); refIndex++) { + int recipeIndex = indices.get(refIndex); + Point p = getRecipePosition(refIndex); // Legacy recipe handlers only expect a single paging widget at the top of the recipe screen, in contrast, // GTNH NEI moves the recipe paging widget from the bottom to the top, which means said legacy handlers will // position item slots 16px too high in the screen. @@ -908,36 +926,40 @@ private void refreshSlots() { p.translate(0, 16); } - List stacks = handler.getIngredientStacks(i); + TemplateRecipeHandler.disableCycledIngredients = false; + + List stacks = handler.original.getIngredientStacks(recipeIndex); for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y); - stacks = handler.getOtherStacks(i); + stacks = handler.original.getOtherStacks(recipeIndex); for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y); - PositionedStack result = handler.getResultStack(i); + PositionedStack result = handler.original.getResultStack(recipeIndex); if (result != null) slotcontainer.addSlot(result, p.x, p.y); if (!limitToOneRecipe) { - List catalysts = RecipeCatalysts.getRecipeCatalysts(handler); + List catalysts = RecipeCatalysts.getRecipeCatalysts(handler.original); for (PositionedStack catalyst : catalysts) { int xOffset = -GuiRecipeCatalyst.ingredientSize + 1; int yOffset = BG_TOP_Y + GuiRecipeCatalyst.fullBorder; slotcontainer.addSlot(catalyst, xOffset, yOffset); } } + + TemplateRecipeHandler.disableCycledIngredients = true; } } private int itemPresenceCacheRecipe = -1; private ArrayList itemPresenceCacheSlots; - private void updateItemPresenceCache(int recipe) { + private void updateItemPresenceCache(int recipeIndex) { if (itemPresenceCacheSlots == null) { itemPresenceCacheSlots = new ArrayList<>(); } - itemPresenceCacheRecipe = recipe; + itemPresenceCacheRecipe = recipeIndex; itemPresenceCacheSlots.clear(); - List ingredients = handler.getIngredientStacks(recipe); + List ingredients = handler.original.getIngredientStacks(recipeIndex); ArrayList invStacks = ((List) firstGui.inventorySlots.inventorySlots).stream() .filter( s -> s != null && s.getStack() != null @@ -963,10 +985,10 @@ public void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { final int ySkip = limitToOneRecipe ? 25 : 0; if (!limitToOneRecipe) { - String s = handler.getRecipeName().trim(); + String s = handler.original.getRecipeName().trim(); fontRendererObj.drawStringWithShadow(s, (xSize - fontRendererObj.getStringWidth(s)) / 2, 5, 0xffffff); - if (IRecipeFilterProvider.filteringAvailable(handler)) { + if (handler.searchingAvailable()) { toggleSearch.draw(mouseX - guiLeft, mouseY - guiTop); } @@ -974,7 +996,7 @@ public void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { searchField.draw(mouseX - guiLeft, mouseY - guiTop); s = NEIClientUtils.cropText( fontRendererObj, - String.format("%d/%d", page + 1, (handler.numRecipes() - 1) / recipesPerPage + 1), + String.format("%d/%d", page + 1, (this.numRecipes() - 1) / recipesPerPage + 1), 45); fontRendererObj.drawStringWithShadow( s, @@ -982,7 +1004,7 @@ public void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { 19, 0xffffff); } else { - s = NEIClientUtils.translate("recipe.page", page + 1, (handler.numRecipes() - 1) / recipesPerPage + 1); + s = NEIClientUtils.translate("recipe.page", page + 1, (this.numRecipes() - 1) / recipesPerPage + 1); fontRendererObj.drawStringWithShadow(s, (xSize - fontRendererObj.getStringWidth(s)) / 2, 19, 0xffffff); } @@ -993,18 +1015,24 @@ public void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { GL11.glPushMatrix(); GL11.glTranslatef(5, 32 - ySkip + yShift, 0); try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { + List indices = getRecipeIndices(); + + for (int refIndex = 0; refIndex < indices.size(); refIndex++) { + int recipeIndex = indices.get(refIndex); - for (int i : getRecipeIndices()) { - handler.drawForeground(i); + handler.original.drawForeground(recipeIndex); if (drawItemPresence && firstGui != null && firstGui.inventorySlots != null - && (isMouseOverOverlayButton(i - page * recipesPerPage) || limitToOneRecipe)) { - List ingredients = handler.getIngredientStacks(i); - if (itemPresenceCacheRecipe != i || itemPresenceCacheSlots == null + && (isMouseOverOverlayButton(refIndex) || limitToOneRecipe)) { + + List ingredients = handler.original.getIngredientStacks(recipeIndex); + + if (itemPresenceCacheRecipe != recipeIndex || itemPresenceCacheSlots == null || itemPresenceCacheSlots.size() != ingredients.size()) { - updateItemPresenceCache(i); + updateItemPresenceCache(recipeIndex); } + for (int j = 0; j < ingredients.size(); j++) { PositionedStack stack = ingredients.get(j); boolean isPresent = itemPresenceCacheSlots.get(j); @@ -1016,6 +1044,7 @@ public void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { } } + GL11.glPopMatrix(); } @@ -1053,8 +1082,8 @@ public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) { GL11.glTranslatef(guiLeft + 5, guiTop - ySkip + 32 + yShift, 0); try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { - for (int i : getRecipeIndices()) { - handler.drawBackground(i); + for (int recipeIndex : getRecipeIndices()) { + handler.original.drawBackground(recipeIndex); GL11.glTranslatef(0, handlerInfo.getHeight(), 0); } @@ -1103,10 +1132,10 @@ public GuiScreen getFirstScreenGeneral() { return firstGuiGeneral; } - public boolean isMouseOver(PositionedStack stack, int recipe) { - Slot stackSlot = slotcontainer - .getSlotWithStack(stack, getRecipePosition(recipe).x, getRecipePosition(recipe).y); + public boolean isMouseOver(PositionedStack stack, int refIndex) { + Point p = getRecipePosition(refIndex); Point mousepos = GuiDraw.getMousePosition(); + Slot stackSlot = slotcontainer.getSlotWithStack(stack, p.x, p.y); Slot mouseoverSlot = getSlotAtPosition(mousepos.x, mousepos.y); return stackSlot == mouseoverSlot; @@ -1137,12 +1166,12 @@ private int getRecipesPerPage(HandlerInfo handlerInfo) { handlerInfo.getMaxRecipesPerPage()), 1); } else if (handler != null) { - return handler.recipiesPerPage(); + return handler.original.recipiesPerPage(); } return 1; } - public Point getRecipePosition(int recipe) { + public Point getRecipePosition(int refIndex) { // Legacy recipe handlers using the height hack might use getRecipePosition in combination with guiTop/height to // position certain elements like tooltips. Since guiTop is moved down by 16px during height hacking, we need to // reduce the vertical shift here to 16px instead of 32px. @@ -1150,7 +1179,7 @@ public Point getRecipePosition(int recipe) { 5, (isHeightHackApplied ? 16 : 32) - (limitToOneRecipe ? 25 : 0) + yShift - + ((recipe % getRecipesPerPage()) * handlerInfo.getHeight())); + + ((refIndex % getRecipesPerPage()) * handlerInfo.getHeight())); } public abstract ArrayList getCurrentRecipeHandlers(); @@ -1201,11 +1230,14 @@ public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) { protected static BookmarkRecipeId getCurrentRecipe(GuiScreen gui) { - if (gui instanceof GuiRecipe && !((GuiRecipe) gui).handler.isEmpty()) { - GuiRecipe gRecipe = (GuiRecipe) gui; + if (gui instanceof GuiRecipe && ((GuiRecipe) gui).handler.numRecipes() > 0) { + final GuiRecipe gRecipe = (GuiRecipe) gui; + final List indices = gRecipe.getRecipeIndices(); + final int curRecipe = indices.isEmpty() ? 0 : indices.get(0); + return new BookmarkRecipeId( gRecipe.handlerInfo.getHandlerName(), - gRecipe.handler.getIngredientStacks(gRecipe.page * gRecipe.getRecipesPerPage())); + gRecipe.handler.original.getIngredientStacks(curRecipe)); } return null; diff --git a/src/main/java/codechicken/nei/recipe/IRecipeHandler.java b/src/main/java/codechicken/nei/recipe/IRecipeHandler.java index 88c415bf4..cf875f4c9 100644 --- a/src/main/java/codechicken/nei/recipe/IRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/IRecipeHandler.java @@ -50,14 +50,6 @@ default String getRecipeTabName() { */ int numRecipes(); - /** - * - * @return true if this list contains no elements - */ - default boolean isEmpty() { - return numRecipes() == 0; - } - /** * Draw the background of this recipe handler (basically the slot layout image). * diff --git a/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java b/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java index 855c61a43..122603c6b 100644 --- a/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java +++ b/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java @@ -34,7 +34,6 @@ class RecipeHandlerQuery { ArrayList runWithProfiling(String profilerSection) { TaskProfiler profiler = ProfilerRecipeHandler.getProfiler(); - TemplateRecipeHandler.disableCycledIngredients = true; profiler.start(profilerSection); try { ArrayList handlers = getRecipeHandlersParallel(); @@ -48,7 +47,6 @@ ArrayList runWithProfiling(String profilerSection) { displayRecipeLookupError(); return new ArrayList<>(0); } finally { - TemplateRecipeHandler.disableCycledIngredients = false; profiler.end(); } } @@ -72,7 +70,8 @@ private ArrayList getSerialHandlersWithRecipes() { error = true; return null; } - }).filter(h -> h != null && !h.isEmpty()).collect(Collectors.toCollection(ArrayList::new)); + }).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1) + .collect(Collectors.toCollection(ArrayList::new)); } private ArrayList getHandlersWithRecipes() throws InterruptedException, ExecutionException { @@ -85,7 +84,8 @@ private ArrayList getHandlersWithRecipes() throws InterruptedException, Execu error = true; return null; } - }).filter(h -> h != null && !h.isEmpty()).collect(Collectors.toCollection(ArrayList::new))).get(); + }).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1) + .collect(Collectors.toCollection(ArrayList::new))).get(); } private boolean isHidden(T handler) { diff --git a/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java b/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java new file mode 100644 index 000000000..2887b99c6 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java @@ -0,0 +1,120 @@ +package codechicken.nei.recipe; + +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import codechicken.nei.ItemList; +import codechicken.nei.api.IRecipeFilter; + +class SearchRecipeHandler { + + public H original; + + private ArrayList filteredRecipes; + + private ArrayList searchRecipes; + + public SearchRecipeHandler(H handler) { + this.original = handler; + + if (this.original.numRecipes() == 0) { + this.filteredRecipes = new ArrayList<>(); + } else { + final Stream items = IntStream.range(0, this.original.numRecipes()).boxed(); + final IRecipeFilter filter = this.searchingAvailable() ? GuiRecipe.getRecipeListFilter() : null; + + if (filter == null) { + this.filteredRecipes = items.collect(Collectors.toCollection(ArrayList::new)); + } else { + this.filteredRecipes = items.filter(recipe -> mathRecipe(this.original, recipe, filter)) + .collect(Collectors.toCollection(ArrayList::new)); + } + } + } + + protected static boolean mathRecipe(IRecipeHandler handler, int recipe, IRecipeFilter filter) { + return filter.matches( + handler, + handler.getIngredientStacks(recipe), + handler.getResultStack(recipe), + handler.getOtherStacks(recipe)); + } + + public boolean searchingAvailable() { + return SearchRecipeHandler.searchingAvailable(this.original); + } + + private static boolean searchingAvailable(IRecipeHandler handler) { + return handler instanceof TemplateRecipeHandler; + } + + public static int findFirst(IRecipeHandler handler, Predicate predicate) { + final IRecipeFilter filter = searchingAvailable(handler) ? GuiRecipe.getRecipeListFilter() : null; + int refIndex = -1; + + for (int recipeIndex = 0; recipeIndex < handler.numRecipes(); recipeIndex++) { + if (filter == null || mathRecipe(handler, recipeIndex, filter)) { + refIndex++; + + if (predicate.test(recipeIndex)) { + return refIndex; + } + } + } + + return -1; + } + + public ArrayList getSearchResult(IRecipeFilter filter) { + + if (filteredRecipes.isEmpty() || !this.searchingAvailable()) { + return null; + } + + ArrayList filtered = null; + final ArrayList recipes = IntStream.range(0, filteredRecipes.size()).boxed() + .collect(Collectors.toCollection(ArrayList::new)); + + try { + filtered = ItemList.forkJoinPool.submit( + () -> recipes.parallelStream() + .filter(recipe -> mathRecipe(this.original, filteredRecipes.get(recipe), filter)) + .collect(Collectors.toCollection(ArrayList::new))) + .get(); + + filtered.sort((a, b) -> a - b); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + + } + + return filtered; + } + + public void setSearchIndices(ArrayList searchRecipes) { + this.searchRecipes = searchRecipes; + } + + public int ref(int index) { + + if (searchRecipes != null) { + index = searchRecipes.get(index); + } + + return filteredRecipes.get(index); + } + + public int numRecipes() { + + if (searchRecipes != null) { + return searchRecipes.size(); + } + + return filteredRecipes.size(); + } + +} diff --git a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java index b3132c541..87342f9cc 100644 --- a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java @@ -18,8 +18,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.init.Blocks; @@ -37,16 +35,17 @@ import codechicken.lib.vec.Rectangle4i; import codechicken.nei.ItemList; import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.LayoutManager; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.NEIServerUtils; import codechicken.nei.PositionedStack; import codechicken.nei.api.DefaultOverlayRenderer; import codechicken.nei.api.IOverlayHandler; -import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.IRecipeOverlayRenderer; import codechicken.nei.api.IStackPositioner; import codechicken.nei.api.ItemFilter; +import codechicken.nei.api.ItemInfo; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerInputHandler; import codechicken.nei.guihook.IContainerTooltipHandler; @@ -199,9 +198,7 @@ public List getCycledIngredients(int cycle, List stacks, long cycle) { - final ItemFilter filter = new AllMultiItemFilter( - ItemList.getItemListFilter(), - GuiRecipe.searchField.getFilter()); + final ItemFilter filter = getItemFilter(); final int randCycle = Math.abs(new Random(cycle + offset).nextInt()); for (PositionedStack stack : stacks) { @@ -217,9 +214,7 @@ protected void randomRenderPermutation(List stacks, long cycle) } protected void jeiStyledRenderPermutation(List stacks, long cycle) { - final ItemFilter filter = new AllMultiItemFilter( - ItemList.getItemListFilter(), - GuiRecipe.searchField.getFilter()); + final ItemFilter filter = getItemFilter(); for (PositionedStack stack : stacks) { if (stack.items.length <= 1) continue; @@ -428,17 +423,20 @@ public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, */ public LinkedList transferRects = new LinkedList<>(); - public static boolean disableCycledIngredients = false; - - protected ArrayList filteredRecipes; - - protected ArrayList searchRecipes; + public static boolean disableCycledIngredients = true; public TemplateRecipeHandler() { loadTransferRects(); RecipeTransferRectHandler.registerRectsToGuis(getRecipeTransferRectGuis(), transferRects); } + protected static ItemFilter getItemFilter() { + return new AllMultiItemFilter( + item -> !ItemInfo.hiddenItems.contains(item), + LayoutManager.presetsPanel != null ? LayoutManager.presetsPanel.getFilter() : null, + GuiRecipe.searchField != null ? GuiRecipe.searchField.getFilter() : null); + } + protected static ArrayList getFilteredIngredientAlternatives(PositionedStack stack, ItemFilter filter) { final ArrayList filtered = new ArrayList<>(); @@ -591,7 +589,8 @@ public Class getGuiClass() { public TemplateRecipeHandler newInstance() { try { - // Use the non parallel version since we might already be using the forkJoinPool and might have no threads + // Use the non parallel version since we might already be using the forkJoinPool + // and might have no threads // available // This will ideally have been pre-cached elsewhere and will be a NOOP findFuelsOnce(); @@ -644,129 +643,8 @@ public String specifyTransferRect() { return null; } - public boolean isEmpty() { - - if (arecipes.isEmpty()) { - return true; - } - - final IRecipeFilter filter = GuiRecipe.getRecipeListFilter(this); - - if (filter == null) { - return false; - } - - final boolean disableCycledIngredients = TemplateRecipeHandler.disableCycledIngredients; - TemplateRecipeHandler.disableCycledIngredients = true; - - boolean isEmpty = !IntStream.range(0, arecipes.size()).boxed().anyMatch( - recipe -> filter.matches( - this, - arecipes.get(recipe).getIngredients(), - arecipes.get(recipe).getResult(), - arecipes.get(recipe).getOtherStacks())); - - TemplateRecipeHandler.disableCycledIngredients = disableCycledIngredients; - - return isEmpty; - } - - protected synchronized void applyFilter() { - - if (arecipes.isEmpty() && this.filteredRecipes == null) { - this.filteredRecipes = new ArrayList<>(); - } - - if (this.filteredRecipes != null) { - return; - } - - final Stream items = IntStream.range(0, arecipes.size()).boxed(); - final IRecipeFilter filter = GuiRecipe.getRecipeListFilter(this); - - if (filter == null) { - this.filteredRecipes = items.collect(Collectors.toCollection(ArrayList::new)); - } else { - final boolean disableCycledIngredients = TemplateRecipeHandler.disableCycledIngredients; - TemplateRecipeHandler.disableCycledIngredients = true; - - this.filteredRecipes = items - .filter( - recipe -> filter.matches( - this, - arecipes.get(recipe).getIngredients(), - arecipes.get(recipe).getResult(), - arecipes.get(recipe).getOtherStacks())) - .collect(Collectors.toCollection(ArrayList::new)); - - TemplateRecipeHandler.disableCycledIngredients = disableCycledIngredients; - } - - } - - public void applySearch(ArrayList searchRecipes) { - this.searchRecipes = searchRecipes; - } - - public ArrayList getSearchResult(IRecipeFilter filter) { - if (filteredRecipes == null) { - applyFilter(); - } - - ArrayList filtered = null; - - if (!filteredRecipes.isEmpty()) { - final ArrayList recipes = IntStream.range(0, filteredRecipes.size()).boxed() - .collect(Collectors.toCollection(ArrayList::new)); - final boolean disableCycledIngredients = TemplateRecipeHandler.disableCycledIngredients; - TemplateRecipeHandler.disableCycledIngredients = true; - - try { - filtered = ItemList.forkJoinPool.submit( - () -> recipes.parallelStream() - .filter( - (recipe) -> filter.matches( - this, - arecipes.get(filteredRecipes.get(recipe)).getIngredients(), - arecipes.get(filteredRecipes.get(recipe)).getResult(), - arecipes.get(filteredRecipes.get(recipe)).getOtherStacks())) - .collect(Collectors.toCollection(ArrayList::new))) - .get(); - filtered.sort((a, b) -> a - b); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - - TemplateRecipeHandler.disableCycledIngredients = disableCycledIngredients; - } - - return filtered; - } - - private int ref(int index) { - - if (filteredRecipes == null) { - applyFilter(); - } - - if (searchRecipes != null) { - index = searchRecipes.get(index); - } - - return filteredRecipes.get(index); - } - public int numRecipes() { - - if (filteredRecipes == null) { - applyFilter(); - } - - if (searchRecipes != null) { - return searchRecipes.size(); - } - - return filteredRecipes.size(); + return arecipes.size(); } public void drawBackground(int recipe) { @@ -779,23 +657,23 @@ public void drawForeground(int recipe) { GL11.glColor4f(1, 1, 1, 1); GL11.glDisable(GL11.GL_LIGHTING); changeTexture(getGuiTexture()); - drawExtras(ref(recipe)); + drawExtras(recipe); } public List getIngredientStacks(int recipe) { - return arecipes.get(ref(recipe)).getIngredients(); + return arecipes.get(recipe).getIngredients(); } public PositionedStack getResultStack(int recipe) { try { - return arecipes.get(ref(recipe)).getResult(); + return arecipes.get(recipe).getResult(); } catch (ArrayIndexOutOfBoundsException ignored) { return null; } } public List getOtherStacks(int recipe) { - return arecipes.get(ref(recipe)).getOtherStacks(); + return arecipes.get(recipe).getOtherStacks(); } public void onUpdate() { @@ -871,9 +749,7 @@ public boolean mouseScrolled(GuiRecipe gui, int scroll, int recipe) { final PositionedStack overStack = getIngredientMouseOver(relMouse.x, relMouse.y, recipe); if (overStack != null && overStack.items.length > 1) { - final ItemFilter filter = new AllMultiItemFilter( - ItemList.getItemListFilter(), - GuiRecipe.searchField.getFilter()); + final ItemFilter filter = getItemFilter(); if (NEIClientConfig.useJEIStyledCycledIngredients()) { ItemStack stack = overStack.item; From 4953edde4b76c8e6aa9e8c8221b13efc7da916c6 Mon Sep 17 00:00:00 2001 From: slprime Date: Fri, 26 Jan 2024 19:19:02 +0200 Subject: [PATCH 2/2] fixed rounding in chain calculation --- src/main/java/codechicken/nei/BookmarkCraftingChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/codechicken/nei/BookmarkCraftingChain.java b/src/main/java/codechicken/nei/BookmarkCraftingChain.java index 8adbdd928..ff1621a61 100644 --- a/src/main/java/codechicken/nei/BookmarkCraftingChain.java +++ b/src/main/java/codechicken/nei/BookmarkCraftingChain.java @@ -276,7 +276,7 @@ private boolean calculateShift(CraftingChainRequest request, int stackIndex) { if (!request.initialItems.contains(item.recipeIndex) && item.factor > 0) { long ingrCount = calculateCount(request, request.inputs.get(item.stackIndex)); long outputCount = calculateCount(request, request.outputs.get(item.stackIndex)); - long shift = (ingrCount - outputCount) / item.factor; + long shift = (long) Math.ceil((ingrCount - outputCount) / (double) item.factor); if (shift > 0 && shift < minShift) { minShift = (int) shift;