Optimize permutations (#761)

Co-authored-by: Martin Robertz <dream-master@gmx.net>
This commit is contained in:
slprimeandMartin Robertz authored and GitHub committed 2025-10-17 22:23:10 +03:00
1 parent 40e53eea7a
commit 76db43c835
18 files changed
+170 -133

No files matched your search

@@ -70,8 +70,8 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
}
public void setActiveStack(ItemStack activeStack) {
final ItemStack realStack = items.stream().filter(stack -> StackInfo.equalItemAndNBT(stack, activeStack, true))
.findFirst().orElse(null);
final ItemStack realStack = items.stream()
.filter(stack -> NEIClientUtils.areStacksSameTypeWithNBT(stack, activeStack)).findFirst().orElse(null);
this.activeStackIndex = items.indexOf(realStack);
}
@@ -233,10 +233,6 @@ public class NEIClientConfig {
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.jeiStyleRecipeCatalyst", true));
tag.getTag("inventory.guirecipe.jeiStyleCycledIngredients").setComment("JEI styled cycled ingredients")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.jeiStyleCycledIngredients", true));
tag.getTag("inventory.guirecipe.cycledIngredientsTooltip").setComment("Show cycled ingredients tooltip")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.cycledIngredientsTooltip", true));
@@ -1129,10 +1125,6 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.optimize_gui_overlap_computation");
}
public static boolean useJEIStyledCycledIngredients() {
return getBooleanSetting("inventory.guirecipe.jeiStyleCycledIngredients");
}
public static boolean showCycledIngredientsTooltip() {
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
}
@@ -42,6 +42,31 @@ import codechicken.nei.util.NEIMouseUtils;
public abstract class ShortcutInputHandler {
private static class HotkeysCache {
public int mousex;
public int mousey;
public ItemStack stack;
public int groupId;
public Map<String, String> hotkeys = new HashMap<>();
public boolean matches(int mousex, int mousey, ItemStack stack, int groupId) {
return Math.abs(this.mousex - mousex) < 16 && Math.abs(this.mousey - mousey) < 16
&& (this.stack == null ? stack == null && this.groupId == groupId
: stack != null && NEIClientUtils.areStacksSameTypeWithNBT(stack, this.stack));
}
public void update(int mousex, int mousey, ItemStack stack, int groupId, Map<String, String> hotkeys) {
this.mousex = mousex;
this.mousey = mousey;
this.stack = stack != null ? stack.copy() : null;
this.groupId = groupId;
this.hotkeys = hotkeys;
}
}
private static HotkeysCache hotkeysCache = new HotkeysCache();
public static boolean handleKeyEvent(ItemStack stackover) {
if (!NEIClientConfig.isLoaded()) {
@@ -391,8 +416,13 @@ public abstract class ShortcutInputHandler {
}
public static Map<String, String> handleHotkeys(int mousex, int mousey, ItemStack stack) {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
if (hotkeysCache.matches(mousex, mousey, stack, groupId)) {
return hotkeysCache.hotkeys;
}
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final Map<String, String> hotkeys = new HashMap<>();
if (groupId != -1) {
@@ -419,9 +449,13 @@ public abstract class ShortcutInputHandler {
NEIClientConfig.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.craft_all"));
}
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
return hotkeys;
}
if (stack == null) {
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
return hotkeys;
}
@@ -582,6 +616,8 @@ public abstract class ShortcutInputHandler {
}
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
return hotkeys;
}
@@ -11,7 +11,7 @@ import codechicken.nei.NEIClientUtils;
public class AcceptsFollowingTooltipLineHandler extends ItemsTooltipLineHandler {
protected static final int DEFAULT_MAX_ROWS = 5;
protected static final int DEFAULT_MAX_ROWS = 4;
public Object tooltipGUID;
@@ -179,6 +179,12 @@ public class DebugHandlerWidget extends Widget implements IContainerInputHandler
this.handlerHeight = new IntegerField("handlerHeight", HandlerInfo.DEFAULT_HEIGHT) {
@Override
public void draw(int mx, int my) {
this.field.setEnabled(handler.getRecipeHeight() <= 0);
super.draw(mx, my);
}
@Override
public void onTextChange(String oldText) {
if (getInteger() != handlerInfo.getHeight()) {
@@ -27,7 +27,7 @@ public class FurnaceRecipeHandler extends TemplateRecipeHandler {
}
public List<PositionedStack> getIngredients() {
return getCycledIngredients(cycleticks / 48, Collections.singletonList(ingred));
return Collections.singletonList(ingred);
}
public PositionedStack getResult() {
@@ -351,8 +351,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
if (!cacheKey.equals(this.recipePageCacheKey)) {
this.recipePageCacheKey = cacheKey;
final boolean recipeGrow = this.handlerInfo.getUseCustomScroll()
&& this.handlerInfo.getMaxRecipesPerPage() <= 1;
final boolean expandVertically = this.handlerInfo.expandVertically();
final List<Widget> widgets = new ArrayList<>();
int shiftY = 0;
@@ -361,7 +360,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
.getRecipeWidget();
widget.setLocation(Math.max(0, (this.container.w - widget.w) / 2), shiftY);
if (recipeGrow) {
if (expandVertically) {
widget.h = Math.min(
this.handlerInfo.getHeight() + this.handlerInfo.getYShift(),
this.container.getVisibleHeight());
@@ -111,7 +111,7 @@ public class GuiRecipeCatalyst extends Widget {
1,
1);
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
RenderHelper.enableGUIStandardItemLighting();
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
@@ -133,6 +133,10 @@ public class HandlerInfo {
this.useCustomScroll = useCustomScroll;
}
public boolean expandVertically() {
return this.useCustomScroll && this.maxRecipesPerPage <= 1;
}
public static class Builder {
private final HandlerInfo info;
@@ -44,6 +44,10 @@ public interface IRecipeHandler {
return getRecipeName();
}
default int getRecipeHeight() {
return 0;
}
/**
*
* @return The number of recipes that this handler contains.
@@ -99,7 +99,7 @@ public class InformationHandler extends TemplateRecipeHandler {
@Override
public List<PositionedStack> getIngredients() {
return getCycledIngredients(cycleticks / 20, Collections.singletonList(this.stack));
return Collections.singletonList(this.stack);
}
public InformationPage getPage() {
@@ -5,7 +5,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.function.Function;
import java.util.stream.Stream;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
@@ -17,6 +19,7 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
@@ -29,13 +32,16 @@ import codechicken.nei.recipe.GuiRecipeButton.UpdateRecipeButtonsEvent;
public class NEIRecipeWidget extends Widget {
protected final WeakHashMap<PositionedStack, List<ItemStack>> permutations = new WeakHashMap<>();
protected boolean needUpdate = true;
protected int cycleticks = 0;
protected int lastcycle = 0;
protected final RecipeHandlerRef handlerRef;
protected final HandlerInfo handlerInfo;
protected boolean showAsWidget = false;
protected List<GuiRecipeButton> recipeButtons = null;
protected boolean firstDraw = true;
protected boolean needUpdate = true;
public NEIRecipeWidget(RecipeHandlerRef handlerRef) {
this.handlerRef = handlerRef;
@@ -49,14 +55,18 @@ public class NEIRecipeWidget extends Widget {
}
public void update() {
this.w = this.handlerInfo.getWidth();
this.w = Math.max(166, this.handlerInfo.getWidth());
if (this.showAsWidget || !this.handlerInfo.getUseCustomScroll()
|| this.handlerInfo.getMaxRecipesPerPage() > 1) {
this.h = this.handlerInfo.getHeight() + this.handlerInfo.getYShift();
if (this.showAsWidget || !this.handlerInfo.expandVertically()) {
this.h = this.handlerRef.handler.getRecipeHeight() > 0 ? this.handlerRef.handler.getRecipeHeight()
: this.handlerInfo.getHeight();
this.h += this.handlerInfo.getYShift();
}
this.needUpdate = true;
if (!this.needUpdate && !NEIClientUtils.shiftKey() && (this.cycleticks++) / 20 != this.lastcycle) {
this.lastcycle = this.cycleticks / 20;
this.needUpdate = true;
}
}
public List<GuiRecipeButton> getRecipeButtons() {
@@ -152,7 +162,12 @@ public class NEIRecipeWidget extends Widget {
public void draw(int mouseX, int mouseY) {
final int yShift = this.handlerInfo.getYShift();
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
if (this.needUpdate) {
this.needUpdate = false;
updatePermutations();
}
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GL11.glTranslatef(this.x, this.y + yShift, 0);
this.handlerRef.handler.drawBackground(this.handlerRef.recipeIndex);
@@ -161,14 +176,9 @@ public class NEIRecipeWidget extends Widget {
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glColor4f(1, 1, 1, 1);
if (this.firstDraw) {
this.firstDraw = false;
for (PositionedStack pStack : this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex)) {
pStack.setPermutationToRender(pStack.getFilteredPermutations().get(0));
}
}
GuiContainerManager.enableMatrixStackLogging();
for (PositionedStack pStack : getItems(!this.needUpdate)) {
for (PositionedStack pStack : this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex)) {
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
@@ -176,7 +186,25 @@ public class NEIRecipeWidget extends Widget {
}
}
this.needUpdate = false;
for (PositionedStack pStack : this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex)) {
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, -2130706433));
}
}
final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex);
if (pStackResult != null) {
GuiContainerManager.drawItem(pStackResult.relx, pStackResult.rely, pStackResult.item);
if (pStackResult.contains(mouseX - this.x, mouseY - this.y - yShift)) {
NEIClientUtils.gl2DRenderContext(
() -> GuiDraw.drawRect(pStackResult.relx, pStackResult.rely, 16, 16, -2130706433));
}
}
GuiContainerManager.disableMatrixStackLogging();
this.handlerRef.handler.drawForeground(this.handlerRef.recipeIndex);
final GuiRecipeButton overlayButton = getRecipeButtons().stream()
@@ -300,12 +328,37 @@ public class NEIRecipeWidget extends Widget {
return true;
}
if (scrollPermutations(scroll, mx, my)) {
return true;
}
final GuiRecipe<?> guiRecipe = getGuiRecipe();
return guiRecipe != null
&& this.handlerRef.handler.mouseScrolled(guiRecipe, scroll, this.handlerRef.recipeIndex);
}
protected boolean scrollPermutations(int scroll, int mx, int my) {
if (!NEIClientUtils.shiftKey()) return false;
final PositionedStack overStack = getPositionedStackMouseOver(mx, my);
if (overStack != null && overStack.items.length > 1) {
final List<ItemStack> items = overStack.getFilteredPermutations();
final int stackIndex = overStack.getPermutationIndex(overStack.item);
final ItemStack stack = items.get((items.size() - scroll + stackIndex) % items.size());
Stream.concat(
this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex).stream(),
this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex).stream())
.filter(pStack -> pStack.containsWithNBT(stack))
.forEach(pStack -> pStack.setPermutationToRender(stack));
return true;
}
return false;
}
protected <R> R forEachButtons(Function<GuiRecipeButton, R> callback, R defaultValue) {
for (GuiRecipeButton button : getRecipeButtonsIfInit()) {
@@ -345,12 +398,23 @@ public class NEIRecipeWidget extends Widget {
public PositionedStack getPositionedStackMouseOver(int mx, int my) {
final int yShift = this.handlerInfo.getYShift();
for (PositionedStack pStack : getItems(true)) {
for (PositionedStack pStack : this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex)) {
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
return pStack;
}
}
for (PositionedStack pStack : this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex)) {
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
return pStack;
}
}
final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex);
if (pStackResult != null && pStackResult.contains(mx - this.x, my - this.y - yShift)) {
return pStackResult;
}
return null;
}
@@ -380,20 +444,31 @@ public class NEIRecipeWidget extends Widget {
return guiContainer;
}
protected List<PositionedStack> getItems(boolean disableCycledIngredients) {
final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex);
final List<PositionedStack> itemStacks = new ArrayList<>();
protected void updatePermutations() {
TemplateRecipeHandler.disableCycledIngredients = disableCycledIngredients;
itemStacks.addAll(this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex));
itemStacks.addAll(this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex));
TemplateRecipeHandler.disableCycledIngredients = true;
if (pStackResult != null) {
itemStacks.add(pStackResult);
for (PositionedStack pStack : this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex)) {
if (pStack.items.length > 1) {
final List<ItemStack> permutations = this.permutations
.computeIfAbsent(pStack, stack -> stack.getFilteredPermutations(FavoriteRecipes::contains));
pStack.setPermutationToRender(permutations.get(this.lastcycle % permutations.size()));
}
}
for (PositionedStack pStack : this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex)) {
if (pStack.items.length > 1) {
final List<ItemStack> permutations = this.permutations
.computeIfAbsent(pStack, stack -> stack.getFilteredPermutations(FavoriteRecipes::contains));
pStack.setPermutationToRender(permutations.get(this.lastcycle % permutations.size()));
}
}
final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex);
if (pStackResult != null && pStackResult.items.length > 1) {
final List<ItemStack> permutations = this.permutations
.computeIfAbsent(pStackResult, stack -> stack.getFilteredPermutations(FavoriteRecipes::contains));
pStackResult.setPermutationToRender(permutations.get(this.lastcycle % permutations.size()));
}
return itemStacks;
}
}
@@ -54,7 +54,7 @@ public class ShapedRecipeHandler extends TemplateRecipeHandler {
@Override
public List<PositionedStack> getIngredients() {
return getCycledIngredients(cycleticks / 20, ingredients);
return ingredients;
}
public PositionedStack getResult() {
@@ -64,7 +64,7 @@ public class ShapelessRecipeHandler extends ShapedRecipeHandler {
@Override
public List<PositionedStack> getIngredients() {
return getCycledIngredients(cycleticks / 20, ingredients);
return ingredients;
}
@Override
@@ -9,13 +9,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.init.Blocks;
@@ -31,8 +29,6 @@ import org.lwjgl.opengl.GL11;
import com.google.common.base.Stopwatch;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.ItemList;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
@@ -178,43 +174,16 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
return null;
}
/**
* This will perform default cycling of ingredients, mulitItem capable
*
* @param cycle Current cycle step
* @param ingredients List of ItemStacks to cycle
* @return The provided list of ingredients, with their permutations cycled to a different permutation, if one
* is available
*/
@Deprecated
public List<PositionedStack> getCycledIngredients(int cycle, List<PositionedStack> ingredients) {
if (!NEIClientUtils.shiftKey() && !disableCycledIngredients) {
randomRenderPermutation(ingredients, cycle);
}
return ingredients;
}
public void randomRenderPermutation(List<PositionedStack> stacks, long cycle) {
cycle = NEIClientConfig.useJEIStyledCycledIngredients() ? cycle
: Math.abs(new Random(cycle + offset).nextInt());
for (PositionedStack pStack : stacks) {
if (pStack.items.length <= 1) continue;
final List<ItemStack> items = pStack.getFilteredPermutations(FavoriteRecipes::contains);
pStack.setPermutationToRender(items.get((int) (cycle % items.size())));
}
}
@Deprecated
public void randomRenderPermutation(List<PositionedStack> stacks, long cycle) {}
@Deprecated
public void randomRenderPermutation(PositionedStack stack, long cycle) {
cycle = NEIClientConfig.useJEIStyledCycledIngredients() ? cycle
: Math.abs(new Random(cycle + offset).nextInt());
if (stack.items.length <= 1) return;
final List<ItemStack> items = stack.getFilteredPermutations(FavoriteRecipes::contains);
stack.setPermutationToRender(items.get((int) (cycle % items.size())));
}
public void randomRenderPermutation(PositionedStack stack, long cycle) {}
public void setIngredientPermutation(Collection<PositionedStack> ingredients, ItemStack ingredient) {
for (PositionedStack stack : ingredients) {
@@ -406,8 +375,6 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
*/
public LinkedList<RecipeTransferRect> transferRects = new LinkedList<>();
public static boolean disableCycledIngredients = true;
public TemplateRecipeHandler() {
loadTransferRects();
RecipeTransferRectHandler.registerRectsToGuis(getRecipeTransferRectGuis(), transferRects);
@@ -641,7 +608,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
}
public void onUpdate() {
if (!NEIClientUtils.shiftKey()) cycleticks++;
cycleticks++;
}
public boolean hasOverlay(GuiContainer gui, Container container, int recipe) {
@@ -700,49 +667,9 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
@Override
public boolean mouseScrolled(GuiRecipe<?> gui, int scroll, int recipe) {
if (!NEIClientUtils.shiftKey()) return false;
final Point pos = GuiDraw.getMousePosition();
final Point offset = gui.getRecipePosition(recipe);
final Point relMouse = new Point(pos.x - gui.guiLeft - offset.x, pos.y - gui.guiTop - offset.y);
final PositionedStack overStack = getIngredientMouseOver(relMouse.x, relMouse.y, recipe);
if (overStack != null && overStack.items.length > 1) {
final List<ItemStack> items = overStack.getFilteredPermutations();
final int stackIndex = getPermutationIndex(overStack.item, items);
final ItemStack stack = items.get((items.size() - scroll + stackIndex) % items.size());
if (NEIClientConfig.useJEIStyledCycledIngredients()) {
Stream.concat(getIngredientStacks(recipe).stream(), getOtherStacks(recipe).stream())
.filter(pStack -> pStack.containsWithNBT(stack))
.forEach(pStack -> pStack.setPermutationToRender(stack));
} else {
overStack.setPermutationToRender(stack);
}
return true;
}
return false;
}
private int getPermutationIndex(ItemStack stack, List<ItemStack> items) {
for (int index = 0; index < items.size(); index++) {
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(items.get(index), stack)) {
return index;
}
}
return -1;
}
private PositionedStack getIngredientMouseOver(int mousex, int mousey, int recipe) {
return Stream.concat(getIngredientStacks(recipe).stream(), getOtherStacks(recipe).stream())
.filter(pStack -> new Rectangle4i(pStack.relx, pStack.rely, 18, 18).contains(mousex, mousey))
.findFirst().orElse(null);
}
private boolean transferRect(GuiRecipe<?> gui, int recipe, boolean usage) {
Point offset = gui.getRecipePosition(recipe);
return transferRect(gui, transferRects, offset.x, offset.y, usage);
@@ -251,7 +251,7 @@ public class ScrollContainer extends WidgetContainer {
getVisibleHeight(),
() -> drawContent(mousex, mousey));
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
if (canScrollVertical) {
this.verticalScrollBar.draw(this, mousex, mousey, getScrollBarOpacity());