diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index 11fa605b3..42622edea 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -287,7 +287,7 @@ private List getPermutations(Item item) { .filter( stack -> stack.getItem() != null && stack.getItem().delegate.name() != null && !ItemInfo.isHidden(stack)) - .collect(Collectors.toCollection(ArrayList::new)); + .collect(Collectors.toList()); } // For optimization it generate itemslist, permutations, orders & collapsibleitems diff --git a/src/main/java/codechicken/nei/ItemStackAmount.java b/src/main/java/codechicken/nei/ItemStackAmount.java new file mode 100644 index 000000000..77765f85a --- /dev/null +++ b/src/main/java/codechicken/nei/ItemStackAmount.java @@ -0,0 +1,119 @@ +package codechicken.nei; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import codechicken.nei.recipe.StackInfo; + +public class ItemStackAmount { + + private final Map itemMap = new LinkedHashMap<>(); + + public void putAll(ItemStackAmount amounts) { + for (Map.Entry entry : amounts.itemMap.entrySet()) { + this.itemMap.put(entry.getKey(), entry.getValue() + this.itemMap.getOrDefault(entry.getKey(), 0L)); + } + } + + public void add(ItemStack item) { + add(item, (long) StackInfo.getAmount(item)); + } + + public void add(ItemStack stack, Long value) { + if (stack == null || stack.getItem() == null) return; + final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false); + + this.itemMap.put(key, value + this.itemMap.getOrDefault(key, 0L)); + } + + public Long get(ItemStack stack) { + if (stack == null || stack.getItem() == null) return null; + final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false); + + return this.itemMap.get(key); + } + + public void put(ItemStack stack, long value) { + if (stack == null || stack.getItem() == null) return; + final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false); + + this.itemMap.put(key, value); + } + + public long getOrDefault(ItemStack stack, long defaultAmount) { + final Long e = get(stack); + + return e == null ? defaultAmount : e; + } + + public void clear() { + this.itemMap.clear(); + } + + public Long remove(ItemStack stack) { + if (stack == null || stack.getItem() == null) return null; + final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false); + + return this.itemMap.remove(key); + } + + public boolean removeIf(Predicate> predicate) { + return this.itemMap.entrySet().removeIf(predicate); + } + + public Set> entrySet() { + return this.itemMap.entrySet(); + } + + public List values() { + List list = new ArrayList<>(); + + for (Map.Entry entry : this.itemMap.entrySet()) { + list.add(StackInfo.loadFromNBT(entry.getKey(), Math.max(0, entry.getValue()))); + } + + return list; + } + + public int size() { + return this.itemMap.size(); + } + + public boolean isEmpty() { + return this.itemMap.isEmpty(); + } + + public static ItemStackAmount of(ItemStackAmount map) { + ItemStackAmount result = new ItemStackAmount(); + result.itemMap.putAll(map.itemMap); + return result; + } + + public static ItemStackAmount of(ItemStackMap map) { + ItemStackAmount result = new ItemStackAmount(); + + for (ItemStackMap.Entry entry : map.entries()) { + result.put(entry.key, entry.value); + } + + return result; + } + + public static ItemStackAmount of(Iterable items) { + ItemStackAmount result = new ItemStackAmount(); + + for (ItemStack stack : items) { + result.add(stack); + } + + return result; + } + +} diff --git a/src/main/java/codechicken/nei/ItemStackMap.java b/src/main/java/codechicken/nei/ItemStackMap.java index bb4cd0505..f2685852c 100644 --- a/src/main/java/codechicken/nei/ItemStackMap.java +++ b/src/main/java/codechicken/nei/ItemStackMap.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.function.Function; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -222,6 +223,19 @@ public void put(ItemStack key, T value) { map.put(key, value); } + public T computeIfAbsent(ItemStack key, Function mappingFunction) { + T value; + if ((value = get(key)) == null) { + T newValue; + if ((newValue = mappingFunction.apply(key)) != null) { + put(key, newValue); + return newValue; + } + } + + return value; + } + public void clear() { itemMap.clear(); size = 0; diff --git a/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java b/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java index 24ddc58cf..56246f106 100644 --- a/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java +++ b/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java @@ -3,15 +3,10 @@ import static codechicken.lib.gui.GuiDraw.fontRenderer; import java.awt.Dimension; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumChatFormatting; import org.lwjgl.opengl.GL11; @@ -24,8 +19,8 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler { - protected static int MAX_COLUMNS = 11; - protected static int MARGIN_TOP = 2; + protected static final int MAX_COLUMNS = 11; + protected static final int MARGIN_TOP = 2; protected String label; protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY; @@ -37,7 +32,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler { protected int rows = 0; protected int length = 0; - public ItemsTooltipLineHandler(String label, List items, boolean saveStackSize) { + public ItemsTooltipLineHandler(String label, List items) { this(label, items, true, 5); } @@ -59,7 +54,12 @@ public ItemsTooltipLineHandler(String label, List items, boolean save this.length, Math.min( this.columns * this.rows, - this.length > MAX_COLUMNS * maxRows ? (MAX_COLUMNS * maxRows - 1) : Integer.MAX_VALUE)); + this.length > MAX_COLUMNS * maxRows ? (MAX_COLUMNS * maxRows) : Integer.MAX_VALUE)); + + if (this.items.size() > this.count) { + String text = "+" + (this.items.size() - this.count); + this.count -= (int) Math.ceil((float) (fontRenderer.getStringWidth(text) - 2) / ItemsGrid.SLOT_SIZE); + } } } @@ -85,6 +85,7 @@ public void draw(int x, int y) { fontRenderer.drawStringWithShadow(this.labelColor + this.label + ":", x, y, 0); + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); GL11.glPushMatrix(); RenderHelper.enableGUIStandardItemLighting(); @@ -100,49 +101,35 @@ public void draw(int x, int y) { String stackSize = !this.saveStackSize || drawStack.stackSize == 0 ? "" : ReadableNumberConverter.INSTANCE.toWideReadableForm(drawStack.stackSize); - GuiContainerManager - .drawItem(col * ItemsGrid.SLOT_SIZE, row * ItemsGrid.SLOT_SIZE, drawStack, true, stackSize); + drawItem(col * ItemsGrid.SLOT_SIZE, row * ItemsGrid.SLOT_SIZE, drawStack, stackSize); } if (this.count < this.items.size()) { + String text = "+" + (this.items.size() - this.count); + fontRenderer.drawStringWithShadow( - "+" + (this.items.size() - this.count), - (this.columns - 1) * ItemsGrid.SLOT_SIZE, - (this.rows - 1) * ItemsGrid.SLOT_SIZE + (ItemsGrid.SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2 - 1, + text, + MAX_COLUMNS * ItemsGrid.SLOT_SIZE - fontRenderer.getStringWidth(text) - 2, + (this.rows - 1) * ItemsGrid.SLOT_SIZE + (ItemsGrid.SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2, 0xee555555); + } - GL11.glDisable(GL12.GL_RESCALE_NORMAL); GL11.glPopMatrix(); + GL11.glPopAttrib(); } - private List groupingItemStacks(List items) { - final Map count = new HashMap<>(); - final Map unique = new LinkedHashMap<>(); - final List result = new ArrayList<>(); - - for (ItemStack stack : items) { - final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack, true); - if (nbTag == null) continue; - - final String GUID = StackInfo.getItemStackGUID(stack); - - if (!unique.containsKey(GUID)) { - count.put(GUID, nbTag.getInteger("Count")); - unique.put(GUID, nbTag); - } else { - count.put(GUID, count.get(GUID) + nbTag.getInteger("Count")); - } - } + protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) { + GuiContainerManager.drawItem(x, y, drawStack, true, stackSize); + } - for (String GUID : unique.keySet()) { - ItemStack stack = StackInfo.loadFromNBT(unique.get(GUID), count.get(GUID)); + private List groupingItemStacks(List items) { + final List result = ItemStackAmount.of(items).values(); - if (unique.get(GUID).hasKey("gtFluidName")) { + for (ItemStack stack : result) { + if (StackInfo.itemStackToNBT(stack).hasKey("gtFluidName")) { stack.stackSize = 0; } - - result.add(stack); } return result; diff --git a/src/main/java/codechicken/nei/LRUCache.java b/src/main/java/codechicken/nei/LRUCache.java new file mode 100644 index 000000000..18517d711 --- /dev/null +++ b/src/main/java/codechicken/nei/LRUCache.java @@ -0,0 +1,21 @@ +package codechicken.nei; + +import java.util.LinkedHashMap; +import java.util.Map; + +// Simple LRUCache +public class LRUCache extends LinkedHashMap { + + private final int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75f, true); + this.capacity = capacity; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + +} diff --git a/src/main/java/codechicken/nei/PositionedStack.java b/src/main/java/codechicken/nei/PositionedStack.java index bcf978eca..ac01e1053 100644 --- a/src/main/java/codechicken/nei/PositionedStack.java +++ b/src/main/java/codechicken/nei/PositionedStack.java @@ -1,13 +1,20 @@ package codechicken.nei; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import net.minecraft.init.Blocks; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraftforge.oredict.OreDictionary; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.api.ItemFilter; +import codechicken.nei.api.ItemInfo; +import codechicken.nei.recipe.GuiRecipe; + /** * Simply an {@link ItemStack} with position. Mainly used in the recipe handlers. */ @@ -40,7 +47,7 @@ public PositionedStack(Object object, int x, int y) { public void generatePermutations() { if (permutated) return; - ArrayList stacks = new ArrayList<>(); + List stacks = new ArrayList<>(); for (ItemStack item : items) { if (item == null || item.getItem() == null) continue; @@ -75,18 +82,60 @@ public void setMaxSize(int i) { } public PositionedStack copy() { - return new PositionedStack(items, relx, rely); + PositionedStack pStack = new PositionedStack(Arrays.asList(items), relx, rely, false); + pStack.permutated = this.permutated; + return pStack; + } + + public List getFilteredPermutations() { + return getFilteredPermutations(null); + } + + public List getFilteredPermutations(ItemFilter additionalFilter) { + List items = Arrays.asList(this.items).stream().filter(getItemFilter(additionalFilter)::matches) + .collect(Collectors.toList()); + + if (items.isEmpty()) { + items.addAll( + Arrays.asList(this.items).stream().filter(item -> !ItemInfo.isHidden(item)) + .collect(Collectors.toList())); + } + + if (items.isEmpty()) { + items.addAll(Arrays.asList(this.items)); + } + + return items; + } + + private ItemFilter getItemFilter(ItemFilter additionalFilter) { + return new AllMultiItemFilter( + additionalFilter, + item -> !ItemInfo.isHidden(item), + PresetsList.getItemFilter(), + GuiRecipe.getSearchItemFilter()); + } + + public boolean setPermutationToRender(ItemStack ingredient) { + + for (int index = 0; index < this.items.length; index++) { + if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(this.items[index], ingredient)) { + setPermutationToRender(index); + return true; + } + } + + return false; } public void setPermutationToRender(int index) { - item = items[index].copy(); + this.item = this.items[index].copy(); - if (item.getItem() == null) { - item = new ItemStack(Blocks.fire); - } else if (item.getItemDamage() == OreDictionary.WILDCARD_VALUE && item.getItem() != null - && item.getItem().isRepairable()) { - item.setItemDamage(0); - } + if (this.item.getItem() == null) { + this.item = new ItemStack(Blocks.fire); + } else if (this.item.getItemDamage() == OreDictionary.WILDCARD_VALUE && this.item.getItem().isRepairable()) { + this.item.setItemDamage(0); + } } public boolean contains(ItemStack ingredient) { diff --git a/src/main/java/codechicken/nei/SearchTokenParser.java b/src/main/java/codechicken/nei/SearchTokenParser.java index d22c1f6d3..fdb7170ca 100644 --- a/src/main/java/codechicken/nei/SearchTokenParser.java +++ b/src/main/java/codechicken/nei/SearchTokenParser.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; @@ -100,16 +99,7 @@ public boolean matches(ItemStack item) { } } - private final LinkedHashMap filtersCache = new LinkedHashMap<>() { - - private static final long serialVersionUID = 1042213947848622164L; - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > 20; - } - }; - + protected final LRUCache filtersCache = new LRUCache<>(20); protected final List searchProviders; protected final ProvidersCache providersCache = new ProvidersCache(); protected final Map prefixRedefinitions = new HashMap<>(); diff --git a/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java b/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java index 2975be1b2..04f44b0a7 100644 --- a/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java +++ b/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java @@ -15,7 +15,7 @@ public class DefaultOverlayRenderer implements IRecipeOverlayRenderer { public DefaultOverlayRenderer(List ai, IStackPositioner positioner) { - positioner = this.positioner = positioner; + this.positioner = positioner; ingreds = new ArrayList<>(); for (PositionedStack stack : ai) ingreds.add(stack.copy()); ingreds = positioner.positionStacks(ingreds); diff --git a/src/main/java/codechicken/nei/api/ItemInfo.java b/src/main/java/codechicken/nei/api/ItemInfo.java index cc0520e3e..aad06c285 100644 --- a/src/main/java/codechicken/nei/api/ItemInfo.java +++ b/src/main/java/codechicken/nei/api/ItemInfo.java @@ -134,6 +134,7 @@ public static void load(World world) { addInputHandlers(); addIDDumps(); addSearchProviders(); + RecipeItemInputHandler.load(); PresetsList.load(); } @@ -295,7 +296,6 @@ private static void parseModItems() { } private static void addInputHandlers() { - GuiContainerManager.addInputHandler(new RecipeItemInputHandler()); GuiContainerManager.addInputHandler(new PopupInputHandler()); } diff --git a/src/main/java/codechicken/nei/api/ShortcutInputHandler.java b/src/main/java/codechicken/nei/api/ShortcutInputHandler.java index 49eda2e76..93df94531 100644 --- a/src/main/java/codechicken/nei/api/ShortcutInputHandler.java +++ b/src/main/java/codechicken/nei/api/ShortcutInputHandler.java @@ -5,7 +5,9 @@ import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; @@ -199,4 +201,8 @@ private static boolean saveRecipeInBookmark(ItemStack stack, boolean saveIngredi return false; } + public static Map handleHotkeys(GuiContainer gui, int mousex, int mousey, ItemStack stack) { + return new HashMap<>(); + } + } diff --git a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java index 029be47b7..dfbd0ede4 100644 --- a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java +++ b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java @@ -18,6 +18,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -377,9 +378,7 @@ private static void safeItemRenderContext(ItemStack stack, int x, int y, FontRen } public static void registerReloadResourceListener() { - if (Minecraft.getMinecraft().getResourceManager() instanceof SimpleReloadableResourceManager) { - SimpleReloadableResourceManager manager = (SimpleReloadableResourceManager) Minecraft.getMinecraft() - .getResourceManager(); + if (Minecraft.getMinecraft().getResourceManager() instanceof SimpleReloadableResourceManager manager) { manager.registerReloadListener(new ResourcePackReloaded()); } } @@ -547,7 +546,6 @@ public void renderObjects(int mousex, int mousey) { public void renderToolTips(int mousex, int mousey) { List tooltip = new LinkedList<>(); - Map hotkeys = new HashMap<>(); FontRenderer font = GuiDraw.fontRenderer; synchronized (instanceTooltipHandlers) { @@ -571,35 +569,16 @@ public void renderToolTips(int mousex, int mousey) { if (!tooltip.isEmpty()) tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); // add space after 'title' - synchronized (instanceTooltipHandlers) { - for (IContainerTooltipHandler handler : instanceTooltipHandlers) { - hotkeys = handler.handleHotkeys(window, mousex, mousey, hotkeys); - } - } - - if (!hotkeys.isEmpty()) { - List hotkeystips = new ArrayList<>(); + if (shouldShowTooltip(window)) { + List hotkeystips = collectHotkeyTips(mousex, mousey); - if (!NEIClientUtils.altKey()) { - hotkeystips.add( - EnumChatFormatting.GRAY - + translate("showHotkeys", EnumChatFormatting.GOLD + "ALT" + EnumChatFormatting.GRAY)); - } else { + if (!hotkeystips.isEmpty()) { - for (Map.Entry entry : hotkeys.entrySet()) { - hotkeystips.add(getHotkeyTip(entry.getKey(), entry.getValue())); + if (tooltip.isEmpty()) { + tooltip.addAll(hotkeystips); + } else { + tooltip.addAll(1, hotkeystips); } - - hotkeystips.sort((a, b) -> Integer.compare(a.indexOf(" - "), b.indexOf(" - "))); - hotkeystips.set( - hotkeystips.size() - 1, - hotkeystips.get(hotkeystips.size() - 1) + GuiDraw.TOOLTIP_LINESPACE); - } - - if (tooltip.isEmpty()) { - tooltip.addAll(hotkeystips); - } else { - tooltip.addAll(1, hotkeystips); } } @@ -635,8 +614,71 @@ public void renderToolTips(int mousex, int mousey) { } } - private String getHotkeyTip(String key, String message) { - return EnumChatFormatting.GOLD + key + EnumChatFormatting.GRAY + " - " + message + EnumChatFormatting.RESET; + private List collectHotkeyTips(int mousex, int mousey) { + Map hotkeys = new HashMap<>(); + + synchronized (instanceTooltipHandlers) { + for (IContainerTooltipHandler handler : instanceTooltipHandlers) { + hotkeys = handler.handleHotkeys(window, mousex, mousey, hotkeys); + } + } + + if (!hotkeys.isEmpty()) { + List hotkeystips = new ArrayList<>(); + + if (!NEIClientUtils.altKey()) { + hotkeystips.add( + EnumChatFormatting.GRAY + translate( + "showHotkeys", + EnumChatFormatting.GOLD + translate("key.alt") + EnumChatFormatting.GRAY)); + } else { + Map> messages = new HashMap<>(); + hotkeys.remove(null); + hotkeys.remove(""); + + for (Map.Entry entry : hotkeys.entrySet()) { + messages.computeIfAbsent(entry.getValue(), m -> new ArrayList<>()).add(entry.getKey()); + } + + for (List keys : messages.values()) { + Collections.sort(keys, (a, b) -> { + if (a.length() != b.length()) { + return Integer.compare(a.length(), b.length()); + } + return a.compareTo(b); + }); + } + + messages.entrySet().stream().sorted((a, b) -> { + String sa = String.join("/", a.getValue()); + String sb = String.join("/", b.getValue()); + + if (sa.length() != sb.length()) { + return Integer.compare(sa.length(), sb.length()); + } + + return sa.compareTo(sb); + }).map(entry -> getHotkeyTip(entry.getValue(), entry.getKey())) + .collect(Collectors.toCollection(() -> hotkeystips)); + + hotkeystips.set( + hotkeystips.size() - 1, + hotkeystips.get(hotkeystips.size() - 1) + GuiDraw.TOOLTIP_LINESPACE); + } + + return hotkeystips; + } + + return Collections.emptyList(); + } + + private String getHotkeyTip(List keys, String message) { + return EnumChatFormatting.GOLD + + String.join(EnumChatFormatting.DARK_GRAY + " / " + EnumChatFormatting.GOLD, keys) + + EnumChatFormatting.GRAY + + " - " + + message + + EnumChatFormatting.RESET; } private static int tooltipPage; diff --git a/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java b/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java index 56bf726f3..bcba5c78b 100644 --- a/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java +++ b/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java @@ -25,16 +25,16 @@ public BookmarkRecipeId(String handlerName, List stacks) { for (Object pos : stacks) { - if (pos instanceof PositionedStack) { - pos = StackInfo.getItemStackWithMinimumDamage(((PositionedStack) pos).items); + if (pos instanceof PositionedStack item) { + pos = StackInfo.getItemStackWithMinimumDamage(item.items); } - if (pos instanceof ItemStack) { - pos = StackInfo.itemStackToNBT((ItemStack) pos); + if (pos instanceof ItemStack item) { + pos = StackInfo.itemStackToNBT(item); } - if (pos instanceof NBTTagCompound) { - ingredients.add((NBTTagCompound) pos); + if (pos instanceof NBTTagCompound item) { + ingredients.add(item); } } } diff --git a/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java b/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java index 28e59afe6..bc30a432e 100644 --- a/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java +++ b/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java @@ -70,8 +70,7 @@ protected ItemStack fillContainer(ItemStack draggedStack, FluidStack fluidStack) } return FluidContainerRegistry.fillFluidContainer(fluidStack, draggedStack); - } else if (draggedStack.getItem() instanceof IFluidContainerItem) { - IFluidContainerItem item = (IFluidContainerItem) draggedStack.getItem(); + } else if (draggedStack.getItem() instanceof IFluidContainerItem item) { if (item.getCapacity(draggedStack) > 0) { item.drain(draggedStack, item.getCapacity(draggedStack), true); diff --git a/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java b/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java index 711e2877d..09ef87020 100644 --- a/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java @@ -1,20 +1,19 @@ package codechicken.nei.recipe; -import static codechicken.lib.gui.GuiDraw.getMousePosition; - import java.awt.Point; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Function; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.item.ItemStack; +import codechicken.lib.gui.GuiDraw; import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.ItemPanels; import codechicken.nei.NEIClientConfig; @@ -88,7 +87,7 @@ public static boolean overlayRecipe(ItemStack stack, BookmarkRecipeId recipeId, public static ArrayList getCraftingHandlers(String outputId, Object... results) { ArrayList craftinghandlers = GuiCraftingRecipe.craftinghandlers; ArrayList serialCraftingHandlers = GuiCraftingRecipe.serialCraftingHandlers; - Function recipeHandlerFunction; + UnaryOperator recipeHandlerFunction; if ("recipeId".equals(outputId)) { ItemStack stack = (ItemStack) results[0]; @@ -139,7 +138,7 @@ protected static BookmarkRecipeId getRecipeId(GuiScreen gui, ItemStack stackover } } - final Point mouseover = getMousePosition(); + final Point mouseover = GuiDraw.getMousePosition(); ItemPanelSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y); if (panelSlot != null) { diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipe.java b/src/main/java/codechicken/nei/recipe/GuiRecipe.java index 498229a9d..9894d9dff 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipe.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.regex.Matcher; -import java.util.stream.Collectors; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; @@ -29,7 +28,6 @@ import codechicken.lib.gui.GuiDraw; import codechicken.nei.Button; import codechicken.nei.GuiNEIButton; -import codechicken.nei.ItemStackSet; import codechicken.nei.ItemsTooltipLineHandler; import codechicken.nei.LayoutManager; import codechicken.nei.NEICPH; @@ -128,15 +126,7 @@ public PermutationTooltipLineHandler(PositionedStack pStack, List ite } public static PermutationTooltipLineHandler getInstance(PositionedStack pStack) { - final ItemFilter filter = TemplateRecipeHandler.getItemFilter(); - List items = Arrays.asList(pStack.items).stream().filter(filter::matches) - .collect(Collectors.toCollection(ArrayList::new)); - - if (items.isEmpty()) { - items = Arrays.asList(pStack.items); - } - - items = new ItemStackSet().addAll(items).keys(); + final List items = pStack.getFilteredPermutations(); if (items.size() > 1) { return new PermutationTooltipLineHandler(pStack, items); @@ -145,6 +135,20 @@ public static PermutationTooltipLineHandler getInstance(PositionedStack pStack) return null; } + @Override + protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) { + + if (StackInfo.equalItemAndNBT(drawStack, this.pStack.item, true)) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GuiDraw.drawRect(x - 1, y - 1, 18, 18, 0x66555555); + GL11.glPopAttrib(); + } + + super.drawItem(x, y, drawStack, stackSize); + } + } public static class ItemRecipeFilter implements IRecipeFilter { @@ -570,6 +574,10 @@ public static IRecipeFilter getRecipeListFilter() { return recipeFilter.filters.size() == 1 ? recipeFilter.filters.get(0) : recipeFilter; } + public static ItemFilter getSearchItemFilter() { + return searchField.getFilter(); + } + private void checkYShift() { yShift = handlerInfo == null ? 0 : handlerInfo.getYShift(); } diff --git a/src/main/java/codechicken/nei/recipe/ItemsHistoryHandler.java b/src/main/java/codechicken/nei/recipe/ItemsHistoryHandler.java index 90f952a1a..dea1b7ad1 100644 --- a/src/main/java/codechicken/nei/recipe/ItemsHistoryHandler.java +++ b/src/main/java/codechicken/nei/recipe/ItemsHistoryHandler.java @@ -17,8 +17,9 @@ class ItemsHistoryHandler implements ICraftingHandler, IUsageHandler { @Override public ICraftingHandler getRecipeHandler(String outputId, Object... results) { - if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(outputId) && results[0] instanceof ItemStack) { - ItemPanels.itemPanel.historyPanel.addItem((ItemStack) results[0]); + if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(outputId) + && results[0] instanceof ItemStack stack) { + ItemPanels.itemPanel.historyPanel.addItem(stack); } return null; @@ -27,8 +28,9 @@ public ICraftingHandler getRecipeHandler(String outputId, Object... results) { @Override public IUsageHandler getUsageHandler(String inputId, Object... ingredients) { - if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(inputId) && ingredients[0] instanceof ItemStack) { - ItemPanels.itemPanel.historyPanel.addItem((ItemStack) ingredients[0]); + if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(inputId) + && ingredients[0] instanceof ItemStack stack) { + ItemPanels.itemPanel.historyPanel.addItem(stack); } return null; diff --git a/src/main/java/codechicken/nei/recipe/RecipeInfo.java b/src/main/java/codechicken/nei/recipe/RecipeInfo.java index 9ab78fabb..e57032309 100644 --- a/src/main/java/codechicken/nei/recipe/RecipeInfo.java +++ b/src/main/java/codechicken/nei/recipe/RecipeInfo.java @@ -29,9 +29,10 @@ public OverlayKey(Class classz, String ident) { @Override public boolean equals(Object obj) { - if (!(obj instanceof OverlayKey)) return false; - OverlayKey key = (OverlayKey) obj; - return Objects.equal(ident, key.ident) && guiClass == key.guiClass; + if (obj instanceof OverlayKey item) { + return Objects.equal(ident, item.ident) && guiClass == item.guiClass; + } + return false; } @Override diff --git a/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java b/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java index 7f45335ce..61a0d1921 100644 --- a/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java +++ b/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java @@ -1,14 +1,22 @@ package codechicken.nei.recipe; +import java.util.Map; + import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.item.ItemStack; import codechicken.nei.ItemPanels; import codechicken.nei.api.ShortcutInputHandler; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerInputHandler; +import codechicken.nei.guihook.IContainerTooltipHandler; + +public class RecipeItemInputHandler implements IContainerInputHandler, IContainerTooltipHandler { -public class RecipeItemInputHandler implements IContainerInputHandler { + public static void load() { + RecipeItemInputHandler recipeHandler = new RecipeItemInputHandler(); + GuiContainerManager.addInputHandler(recipeHandler); + GuiContainerManager.addTooltipHandler(recipeHandler); + } @Override public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) { @@ -21,9 +29,21 @@ public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button || ItemPanels.bookmarkPanel.contains(mousex, mousey)) return false; - ItemStack stackover = GuiContainerManager.getStackMouseOver(gui); + return ShortcutInputHandler.handleMouseClick(GuiContainerManager.getStackMouseOver(gui)); + } + + @Override + public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { + + if ((gui instanceof GuiRecipe) || ItemPanels.itemPanel.contains(mousex, mousey) + || ItemPanels.bookmarkPanel.contains(mousex, mousey) + || ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) { + hotkeys.putAll( + ShortcutInputHandler + .handleHotkeys(gui, mousex, mousey, GuiContainerManager.getStackMouseOver(gui))); + } - return ShortcutInputHandler.handleMouseClick(stackover); + return hotkeys; } @Override diff --git a/src/main/java/codechicken/nei/recipe/StackInfo.java b/src/main/java/codechicken/nei/recipe/StackInfo.java index 612be8ec5..80510f868 100644 --- a/src/main/java/codechicken/nei/recipe/StackInfo.java +++ b/src/main/java/codechicken/nei/recipe/StackInfo.java @@ -2,7 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import net.minecraft.item.ItemStack; @@ -16,7 +16,9 @@ import codechicken.nei.ClientHandler; import codechicken.nei.ItemStackMap; +import codechicken.nei.LRUCache; import codechicken.nei.api.IStackStringifyHandler; +import codechicken.nei.api.ItemInfo; import codechicken.nei.recipe.stackinfo.DefaultStackStringifyHandler; import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler; import codechicken.nei.util.ItemStackKey; @@ -24,18 +26,10 @@ public class StackInfo { private static final FluidStack NULL_FLUID = new FluidStack(FluidRegistry.WATER, 0); - public static final ArrayList stackStringifyHandlers = new ArrayList<>(); - private static final HashMap> guidfilters = new HashMap<>(); + public static final List stackStringifyHandlers = new ArrayList<>(); + private static final Map> guidfilters = new HashMap<>(); private static final ItemStackMap guidcache = new ItemStackMap<>(); - private static final LinkedHashMap fluidcache = new LinkedHashMap<>() { - - private static final long serialVersionUID = 1042213947848622164L; - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > 200; - } - }; + private static final LRUCache fluidcache = new LRUCache<>(200); static { stackStringifyHandlers.add(new DefaultStackStringifyHandler()); @@ -79,8 +73,20 @@ public static ItemStack loadFromNBT(NBTTagCompound nbtTag) { return stack; } + public static ItemStack withAmount(ItemStack stack, long customCount) { + if (stack == null) return null; + final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack); + return nbTag != null ? loadFromNBT(nbTag, customCount) : null; + } + + public static int getAmount(ItemStack stack) { + if (stack == null) return 0; + final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack); + return nbTag != null ? nbTag.getInteger("Count") : 0; + } + public static boolean equalItemAndNBT(ItemStack stackA, ItemStack stackB, boolean useNBT) { - if (!stackA.isItemEqual(stackB)) { + if (stackA == null || stackB == null || !stackA.isItemEqual(stackB)) { return false; } @@ -151,10 +157,10 @@ public static String getItemStackGUID(ItemStack stack) { try { - if (local instanceof NBTTagCompound) { - local = ((NBTTagCompound) local).getTag(rule[i]); - } else if (local instanceof NBTTagList) { - local = ((NBTTagList) local).tagList.get(Integer.parseInt(rule[i])); + if (local instanceof NBTTagCompound item) { + local = item.getTag(rule[i]); + } else if (local instanceof NBTTagList item) { + local = item.tagList.get(Integer.parseInt(rule[i])); } else { break; } @@ -164,8 +170,8 @@ public static String getItemStackGUID(ItemStack stack) { } } - if (local instanceof NBTBase) { - keys.add(((NBTBase) local).toString()); + if (local instanceof NBTBase item) { + keys.add(item.toString()); } else if (local != null) { keys.add(String.valueOf(local)); } @@ -204,7 +210,7 @@ public static ItemStack getItemStackWithMinimumDamage(ItemStack[] stacks) { if (stacks.length > 1) { for (ItemStack stack : stacks) { - if (stack.getItem() != null && stack.getItemDamage() < damage) { + if (stack.getItem() != null && !ItemInfo.isHidden(stack) && stack.getItemDamage() < damage) { damage = stack.getItemDamage(); result = stack; } diff --git a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java index 2a8859f51..e8156c02e 100644 --- a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java @@ -1,9 +1,5 @@ package codechicken.nei.recipe; -import static codechicken.lib.gui.GuiDraw.changeTexture; -import static codechicken.lib.gui.GuiDraw.drawTexturedModalRect; -import static codechicken.lib.gui.GuiDraw.getMousePosition; - import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; @@ -33,20 +29,17 @@ import com.google.common.base.Stopwatch; +import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.ItemList; -import codechicken.nei.ItemList.AllMultiItemFilter; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.NEIServerUtils; import codechicken.nei.PositionedStack; -import codechicken.nei.PresetsList; import codechicken.nei.api.DefaultOverlayRenderer; import codechicken.nei.api.IOverlayHandler; 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; @@ -188,50 +181,31 @@ public PositionedStack getOtherStack() { public List getCycledIngredients(int cycle, List ingredients) { if (!NEIClientUtils.shiftKey() && !disableCycledIngredients) { - if (NEIClientConfig.useJEIStyledCycledIngredients()) { - jeiStyledRenderPermutation(ingredients, cycle); - } else { - randomRenderPermutation(ingredients, cycle); - } + randomRenderPermutation(ingredients, cycle); } return ingredients; } public void randomRenderPermutation(List stacks, long cycle) { - final ItemFilter filter = getItemFilter(); - final int randCycle = Math.abs(new Random(cycle + offset).nextInt()); - - for (PositionedStack stack : stacks) { - if (stack.items.length <= 1) continue; - ArrayList filtered = getFilteredIngredientAlternatives(stack, filter); - if (filtered.isEmpty()) { - stack.setPermutationToRender((int) (randCycle % stack.items.length)); - } else { - stack.setPermutationToRender(filtered.get((int) (randCycle % filtered.size()))); - } - } - - } - - protected void jeiStyledRenderPermutation(List stacks, long cycle) { - final ItemFilter filter = getItemFilter(); + cycle = NEIClientConfig.useJEIStyledCycledIngredients() ? cycle + : Math.abs(new Random(cycle + offset).nextInt()); - for (PositionedStack stack : stacks) { - if (stack.items.length <= 1) continue; - ArrayList filtered = getFilteredIngredientAlternatives(stack, filter); - if (filtered.isEmpty()) { - stack.setPermutationToRender((int) (cycle % stack.items.length)); - } else { - stack.setPermutationToRender(filtered.get((int) (cycle % filtered.size()))); - } + for (PositionedStack pStack : stacks) { + if (pStack.items.length <= 1) continue; + final List items = pStack.getFilteredPermutations(); + pStack.setPermutationToRender(items.get((int) (cycle % items.size()))); } } @Deprecated public void randomRenderPermutation(PositionedStack stack, long cycle) { - Random rand = new Random(cycle + offset); - stack.setPermutationToRender((int) (Math.abs(rand.nextInt()) % stack.items.length)); + cycle = NEIClientConfig.useJEIStyledCycledIngredients() ? cycle + : Math.abs(new Random(cycle + offset).nextInt()); + + if (stack.items.length <= 1) return; + final List items = stack.getFilteredPermutations(); + stack.setPermutationToRender(items.get((int) (cycle % items.size()))); } public void setIngredientPermutation(Collection ingredients, ItemStack ingredient) { @@ -431,25 +405,6 @@ public TemplateRecipeHandler() { RecipeTransferRectHandler.registerRectsToGuis(getRecipeTransferRectGuis(), transferRects); } - public static ItemFilter getItemFilter() { - return new AllMultiItemFilter( - item -> !ItemInfo.isHidden(item), - PresetsList.getItemFilter(), - GuiRecipe.searchField != null ? GuiRecipe.searchField.getFilter() : null); - } - - protected static ArrayList getFilteredIngredientAlternatives(PositionedStack stack, ItemFilter filter) { - final ArrayList filtered = new ArrayList<>(); - - for (int i = 0; i < stack.items.length; i++) { - if (filter.matches(stack.items[i])) { - filtered.add(i); - } - } - - return filtered; - } - /** * Add all RecipeTransferRects to the transferRects list during this call. Afterward they may be added to the input * handler for the corresponding guis from getRecipeTransferRectGuis @@ -553,16 +508,16 @@ public void drawProgressBar(int x, int y, int tx, int ty, int w, int h, float co switch (direction) { case 0: // right - drawTexturedModalRect(x, y, tx, ty, var, h); + GuiDraw.drawTexturedModalRect(x, y, tx, ty, var, h); break; case 1: // down - drawTexturedModalRect(x, y, tx, ty, w, var); + GuiDraw.drawTexturedModalRect(x, y, tx, ty, w, var); break; case 2: // left - drawTexturedModalRect(x + w - var, y, tx + w - var, ty, var, h); + GuiDraw.drawTexturedModalRect(x + w - var, y, tx + w - var, ty, var, h); break; case 3: // up - drawTexturedModalRect(x, y + h - var, tx, ty + h - var, w, var); + GuiDraw.drawTexturedModalRect(x, y + h - var, tx, ty + h - var, w, var); break; } } @@ -650,14 +605,14 @@ public int numRecipes() { public void drawBackground(int recipe) { GL11.glColor4f(1, 1, 1, 1); - changeTexture(getGuiTexture()); - drawTexturedModalRect(0, 0, 5, 11, 166, 65); + GuiDraw.changeTexture(getGuiTexture()); + GuiDraw.drawTexturedModalRect(0, 0, 5, 11, 166, 65); } public void drawForeground(int recipe) { GL11.glColor4f(1, 1, 1, 1); GL11.glDisable(GL11.GL_LIGHTING); - changeTexture(getGuiTexture()); + GuiDraw.changeTexture(getGuiTexture()); drawExtras(recipe); } @@ -704,7 +659,7 @@ public int recipiesPerPage() { @Override public List handleTooltip(GuiRecipe gui, List currenttip, int recipe) { - if (GuiContainerManager.shouldShowTooltip(gui) && currenttip.size() == 0) { + if (GuiContainerManager.shouldShowTooltip(gui) && currenttip.isEmpty()) { Point offset = gui.getRecipePosition(recipe); currenttip = transferRectTooltip(gui, transferRects, offset.x, offset.y, currenttip); } @@ -744,21 +699,32 @@ public boolean mouseClicked(GuiRecipe gui, int button, int recipe) { public boolean mouseScrolled(GuiRecipe gui, int scroll, int recipe) { if (!NEIClientUtils.shiftKey()) return false; - final Point pos = getMousePosition(); + 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 ItemFilter filter = getItemFilter(); + final List items = overStack.getFilteredPermutations(); + int stackIndex = 0; + + for (int index = 0; index < items.size(); index++) { + if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(items.get(index), overStack.item)) { + stackIndex = index; + break; + } + } + + final ItemStack stack = items.get((items.size() - scroll + stackIndex) % items.size()); if (NEIClientConfig.useJEIStyledCycledIngredients()) { - ItemStack stack = overStack.item; for (PositionedStack pStack : getIngredientStacks(recipe)) { - shiftPermutationToRender(pStack, stack, scroll, filter); + if (pStack.containsWithNBT(stack)) { + pStack.setPermutationToRender(stack); + } } } else { - shiftPermutationToRender(overStack, overStack.item, scroll, filter); + overStack.setPermutationToRender(stack); } return true; @@ -767,35 +733,6 @@ public boolean mouseScrolled(GuiRecipe gui, int scroll, int recipe) { return false; } - private void shiftPermutationToRender(PositionedStack pStack, ItemStack stack, int scroll, ItemFilter filter) { - if (!pStack.containsWithNBT(stack)) return; - - final List filtered = getFilteredIngredientAlternatives(pStack, filter); - int index = 0; - - if (filtered.isEmpty()) { - - for (int i = 0; i < pStack.items.length; i++) { - if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(pStack.items[i], stack)) { - index = i; - break; - } - } - - pStack.setPermutationToRender((int) ((pStack.items.length - scroll + index) % pStack.items.length)); - } else { - - for (int i = 0; i < filtered.size(); i++) { - if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(pStack.items[filtered.get(i)], stack)) { - index = i; - break; - } - } - - pStack.setPermutationToRender(filtered.get((int) ((filtered.size() - scroll + index) % filtered.size()))); - } - } - private PositionedStack getIngredientMouseOver(int mousex, int mousey, int recipe) { for (PositionedStack pStack : getIngredientStacks(recipe)) { @@ -814,7 +751,7 @@ private boolean transferRect(GuiRecipe gui, int recipe, boolean usage) { private static boolean transferRect(GuiContainer gui, Collection transferRects, int offsetx, int offsety, boolean usage) { - Point pos = getMousePosition(); + Point pos = GuiDraw.getMousePosition(); Point relMouse = new Point(pos.x - gui.guiLeft - offsetx, pos.y - gui.guiTop - offsety); for (RecipeTransferRect rect : transferRects) { if (rect.rect.contains(relMouse) && (usage ? GuiUsageRecipe.openRecipeGui(rect.outputId, rect.results) @@ -827,7 +764,7 @@ private static boolean transferRect(GuiContainer gui, Collection transferRectTooltip(GuiContainer gui, Collection transferRects, int offsetx, int offsety, List currenttip) { - Point pos = getMousePosition(); + Point pos = GuiDraw.getMousePosition(); Point relMouse = new Point(pos.x - gui.guiLeft - offsetx, pos.y - gui.guiTop - offsety); for (RecipeTransferRect rect : transferRects) { if (rect.rect.contains(relMouse)) { diff --git a/src/main/resources/assets/nei/cfg/hiddenhandlers.cfg b/src/main/resources/assets/nei/cfg/hiddenhandlers.cfg new file mode 100644 index 000000000..7df187b31 --- /dev/null +++ b/src/main/resources/assets/nei/cfg/hiddenhandlers.cfg @@ -0,0 +1,2 @@ +# Each line in this file should either be a comment (line starts with '#'), handler name or handler id. +# If you delete this file, it will be regenerated with the default handlers list. \ No newline at end of file