Compare commits

...
35 Commits
Author SHA1 Message Date
Martin Robertz 99fb5a656e update 2025-10-02 12:13:08 +02:00
DarkShadow44 4b663222b1 Force tag compounds to be initialized so doesn't happen concurrently later (#730) 2025-10-02 07:15:28 +02:00
Martin Robertz 3e5d0088bc update 2025-10-01 17:21:16 +02:00
948be399d2 allow the steam furnace multiblock to show up in NEI under furnaces (#702)
Co-authored-by: Martin Robertz <[email protected]>
Co-authored-by: EnderProyects <[email protected]>
2025-09-28 13:39:45 +02:00
DarkShadow44 5f6091924d Add more logging for fuel crash (#736) 2025-09-28 01:02:47 +03:00
Dredonkey c5bd1f897a Fix pressing '2' when hovering crafting output not putting the crafted item(s) in the hotbar (#733) 2025-09-27 10:17:40 +00:00
Julia 61d4b6ab62 Revert "Replace lambda with method reference" (#735) 2025-09-27 03:09:17 -07:00
Julia 791d05d5f5 Support Inline Comments After # (#734) 2025-09-26 18:11:47 -07:00
Mike_be 66001275c4 Remove UXV and MAX biolab from NEI recipe catalysts (#732) 2025-09-26 21:40:03 +02:00
slprime db6b83c906 Try fix Fuels (#729) 2025-09-24 13:17:34 +02:00
slprime 58ef6c85e0 Fix NBT Copy (#728) 2025-09-22 20:28:05 -07:00
Martin Robertz ab0aeef872 update 2025-09-21 21:38:41 +02:00
slprime 23b7c69e2f Fix None hotkeys for java8 (#726) 2025-09-21 19:44:20 +02:00
slprime bb89384493 Fix Bookmarks Shadow Items (#724) 2025-09-18 19:07:51 +02:00
slprime e96e4dfdde Create Item Untranslator (#719) 2025-09-17 12:42:35 +00:00
slprime 0a6a6771d8 Fixed Favorite Recipes in bookmarks for ingredients (#720) 2025-09-15 19:21:52 +02:00
sbwaz c96199a727 [Extended+] Filter permutations on the left. Fail on recipe expression, when not searching a recipe. (#716) 2025-09-13 11:18:29 +02:00
azod ab4ea2cfa1 feat(moboverlay): add chat message (on/off) (#715) 2025-09-11 03:45:06 -03:00
Mike_be b679883d02 Remove old entries which resulted in ghost catalysts (#714) 2025-09-08 20:11:32 +02:00
Worive 0048ef80d0 Fix(MobSpawnOverlay): avoid IOB from checking light value from block outside of world (#711) 2025-09-07 11:03:21 +00:00
slprime 84576aa7a2 Fix Permutation Filter (#712) 2025-09-06 22:32:14 +02:00
Dream Master c5cd5b9e61 update 2025-09-05 16:08:54 +02:00
slprimeandMartin Robertz b1bedcf403 Recipe search improvement (#706)
Co-authored-by: Martin Robertz <[email protected]>
2025-09-04 15:59:51 +02:00
Matthew Bentham bba0a11466 Add missing lang key for favorite keybind (#709) 2025-09-04 13:20:53 +02:00
slprime b80948f2f2 Fix Subset Name (#705) 2025-08-31 18:34:19 +02:00
slprime 28539bbacc Fix Keyboard IndexOutOfBoundsException (#704) 2025-08-31 18:33:59 +02:00
UltraProdigy ec28bdf116 Make EFR Lingering Potion Recipes Visible in NEI (#703) 2025-08-30 09:10:05 -04:00
sbwaz e5d55a9bd8 Advanced search expressions and improved recipe search in a new pattern mode: extended+ (#692) 2025-08-24 14:35:59 +07:00
slprime c1f7489de1 Presets: Custom Subsets Mode (#671) 2025-08-22 12:22:09 +00:00
slprime fa6fd01a3a Add Option to Disable Hotkeys Tooltip (#680) 2025-08-22 12:15:23 +00:00
slprime 453ba6667f Add Favorite Hotkey (#681) 2025-08-22 19:12:42 +07:00
slprime 4e11a45852 Add the ability to create a chain of items with zero (#682) 2025-08-22 12:06:27 +00:00
slprime 95274339a0 Change panel position algorithm (#683) 2025-08-22 12:03:05 +00:00
slprime 31fcd6af51 Crate Craftables Panel (#696) 2025-08-22 11:58:41 +00:00
koolkrafter5 860bc059ca Allow shift+scrolling other stacks in recipes (#698) 2025-08-18 19:57:08 +07:00
80 changed files with 3753 additions and 885 deletions

No files matched your search

+23
View File
@@ -1,3 +1,26 @@
compileJava {
options.encoding = "UTF-8"
}
generateGrammarSource {
maxHeapSize = "128m"
arguments += ['-package', 'codechicken.nei.search', '-visitor', '-no-listener']
outputDirectory = file("${projectDir}/build/generated/sources/search/antlr/codechicken/nei/search/")
}
compileJava.dependsOn generateGrammarSource
sourcesJar.dependsOn generateGrammarSource
sourceSets {
main {
java.srcDirs += ["build/generated/sources/search/antlr/"]
}
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
+1
View File
@@ -1,5 +1,6 @@
//version: 1707058017
plugins {
id 'antlr'
id 'com.gtnewhorizons.gtnhconvention'
}
+12 -2
View File
@@ -2,9 +2,19 @@
dependencies {
api('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
implementation('com.github.GTNewHorizons:GTNHLib:0.6.39:dev')
implementation('com.github.GTNewHorizons:GTNHLib:0.7.0:dev')
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
compileOnly 'maven.modrinth:forge-essentials:7.5.1'
compileOnly('maven.modrinth:forge-essentials:7.5.1')
antlr('org.antlr:antlr4:4.13.2')
shadowImplementation('org.antlr:antlr4-runtime:4.13.2')
testImplementation(platform('org.junit:junit-bom:5.13.4'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation("org.mockito:mockito-core:5.+")
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Add any additional repositiroes for your dependencies here
// Add any additional repositories for your dependencies here
repositories {
maven {
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.43'
}
+45
View File
@@ -0,0 +1,45 @@
lexer grammar SearchExpressionLexer;
// Antlr4 generates imports with .*
@header {
// CHECKSTYLE:OFF
import codechicken.nei.SearchTokenParser;
}
@members {
private SearchTokenParser searchParser;
public SearchExpressionLexer(CharStream input, SearchTokenParser searchParser) {
this(input);
this.searchParser = searchParser;
}
}
// Lexer rules
REGEX_LEFT : 'r'? '/' -> pushMode(REGEX) ;
QUOTE_LEFT : '"' -> pushMode(QUOTED) ;
DASH : '-' ;
RECIPE_INGREDIENTS : '<&' | '<' ;
RECIPE_RESULT : '>&' | '>' ;
RECIPE_OTHERS : '^&' | '^' ;
PREFIX : CLEAN_SYMBOL {searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
OR : '|' ;
LEFT_BRACKET : '{' ;
RIGHT_BRACKET : '}' ;
PLAIN_TEXT : (CLEAN_SYMBOL | ESCAPED_SYMBOL)+ {!searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
NEWLINE_OR_TAB : [\t\r\n] -> skip ;
SPACE : ' ' ;
fragment ESCAPED_SYMBOL : '\\' . ;
fragment CLEAN_SYMBOL : ~[-<>^{}|/\\ "] ;
mode REGEX;
REGEX_CONTENT : (~[/] | '\\/')+ ;
REGEX_RIGHT : '/' -> popMode ;
mode QUOTED;
QUOTED_CONTENT : (~["] | '\\"')+ ;
QUOTE_RIGHT : '"' -> popMode ;
+70
View File
@@ -0,0 +1,70 @@
parser grammar SearchExpressionParser;
// Antlr4 generates imports with .*
@header {
// CHECKSTYLE:OFF
}
options { tokenVocab=SearchExpressionLexer; }
// Parser rules
// Should only be used for recipes. Thanks to antlr inability to properly
// inherit grammars, this is included where it's not supposed to be
recipeSearchExpression
: recipeClauseExpression*
;
recipeClauseExpression
: ingredientsPrefix=RECIPE_INGREDIENTS searchExpression[0, $ingredientsPrefix.getText().length() > 1]
| resultPrefix=RECIPE_RESULT searchExpression[1, $resultPrefix.getText().length() > 1]
| othersPrefix=RECIPE_OTHERS searchExpression[2, $othersPrefix.getText().length() > 1]
| searchExpression[3, false]
;
// General search expression rules
searchExpression[int type, boolean allRecipe]
: orExpression
;
orExpression
: sequenceExpression (OR sequenceExpression)*
;
sequenceExpression
: (SPACE* unaryExpression SPACE*)+
;
unaryExpression
: LEFT_BRACKET orExpression RIGHT_BRACKET?
| prefixedExpression
| negateExpression
| token['\0']
;
negateExpression
: DASH unaryExpression
;
prefixedExpression
locals [
Character prefix
]
: prefixToken=PREFIX { $prefix = $prefixToken.getText().charAt(0); } token[$prefix]
;
token[Character prefix]
: regex[prefix]
| quoted[prefix]
| PLAIN_TEXT
;
regex[Character prefix]
: REGEX_LEFT REGEX_CONTENT REGEX_RIGHT?
;
quoted[Character prefix]
: QUOTE_LEFT QUOTED_CONTENT QUOTE_RIGHT?
;
@@ -0,0 +1,141 @@
package codechicken.nei;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
extends Widget implements IContainerTooltipHandler {
final DrawableResource DASH = new DrawableBuilder("nei:textures/dash.png", 0, 0, 6, 1).setTextureSize(6, 1).build();
protected int mouseDownItemIndex = -1;
protected int splittingLineColor = 0xffffffff;
protected T grid;
public boolean isEmpty() {
return this.grid.isEmpty();
}
public void draw(int mousex, int mousey) {
drawSplittingLine();
this.grid.draw(mousex, mousey);
}
public void update() {
this.grid.update();
}
public abstract int setPanelWidth(int width);
public void resize(GuiContainer gui) {
this.grid.setGridSize(x, y + 4, w, h - 8);
this.grid.refresh(gui);
}
@Override
public ItemStack getStackMouseOver(int mousex, int mousey) {
final ItemsGridSlot slot = getSlotMouseOver(mousex, mousey);
return slot == null ? null : slot.getItemStack();
}
public ItemsGridSlot getSlotMouseOver(int mousex, int mousey) {
return this.grid.getSlotMouseOver(mousex, mousey);
}
protected void drawSplittingLine() {
float alpha = (this.splittingLineColor >> 24 & 255) / 255.0F;
float red = (this.splittingLineColor >> 16 & 255) / 255.0F;
float green = (this.splittingLineColor >> 8 & 255) / 255.0F;
float blue = (this.splittingLineColor & 255) / 255.0F;
int width = this.grid.columns * ItemsGrid.SLOT_SIZE;
int repeat = width / 6;
int shiftX = this.x + (this.w - width) / 2;
GL11.glColor4f(red, green, blue, alpha);
for (int i = 0; i < repeat; i++) {
DASH.draw(shiftX + i * 6, y);
}
GL11.glColor4f(1, 1, 1, 1);
}
@Override
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
if (this.mouseDownItemIndex >= 0 && ItemPanels.itemPanel.draggedStack == null
&& NEIClientUtils.getHeldItem() == null
&& NEIClientConfig.hasSMPCounterPart()) {
final ItemsGridSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
if (mouseOverSlot == null || mouseOverSlot.itemIndex != this.mouseDownItemIndex || heldTime > 500) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(
this.grid.getItem(this.mouseDownItemIndex));
this.mouseDownItemIndex = -1;
}
}
}
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (handleClickExt(mousex, mousey, button)) return true;
final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null) {
if (button == 2) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack());
} else {
mouseDownItemIndex = hoverSlot.itemIndex;
}
return true;
}
return false;
}
protected abstract ItemStack getDraggedStackWithQuantity(ItemStack itemStack);
public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) {
final ItemStack hoverSlot = getStackMouseOver(mousex, mousey);
return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null;
}
@Override
public void mouseUp(int mousex, int mousey, int button) {
final ItemsGridSlot hoverSlot = getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.itemIndex == this.mouseDownItemIndex
&& ItemPanels.itemPanel.draggedStack == null) {
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
if (button == 0) {
GuiCraftingRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
} else if (button == 1) {
GuiUsageRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
}
} else {
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
}
}
this.mouseDownItemIndex = -1;
}
}
@@ -207,11 +207,11 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
return false;
}
public boolean addRecipe(Recipe recipe, int groupId) {
public boolean addRecipe(Recipe recipe, int multiplier, int groupId) {
final RecipeId recipeId = recipe.getRecipeId();
if (recipe != null && this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
this.grid.addRecipe(recipe, groupId);
this.grid.addRecipe(recipe, multiplier, groupId);
return true;
}
@@ -234,7 +234,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
if (item instanceof Recipe recipe) {
if (uniqueRecipe.add(recipe.getRecipeId())) {
this.grid.addRecipe(recipe, groupId);
this.grid.addRecipe(recipe, 1, groupId);
}
} else if (item instanceof RecipeId recipeId) {
@@ -305,18 +305,20 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
final RecipeId recipeId = slot.getRecipeId();
final BookmarkGroup group = slot.getGroup();
if (recipeId == null || !removeFullRecipe) {
this.grid.removeRecipe(slot.itemIndex, removeFullRecipe);
if (group.crafting != null && group.collapsed) {
if (!removeFullRecipe) {
return true;
}
this.grid.removeGroup(groupId);
return true;
}
if (group.crafting != null && group.collapsed && removeFullRecipe) {
this.grid.removeGroup(groupId);
return true;
} else if (group.crafting != null) {
if (recipeId != null && group.crafting != null) {
Set<RecipeId> recipes = group.crafting.recipeRelations.getOrDefault(recipeId, Collections.emptySet());
if (removeFullRecipe && recipes.isEmpty()) {
if (recipes.isEmpty()) {
for (Map.Entry<RecipeId, Set<RecipeId>> entry : group.crafting.recipeRelations.entrySet()) {
if (entry.getValue().contains(recipeId)) {
recipes = entry.getValue();
@@ -326,8 +328,12 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
if (!recipes.isEmpty()) {
boolean removed = false;
if (!removeFullRecipe) {
return true;
}
boolean removed = false;
for (RecipeId relRecipeId : recipes) {
removed = this.grid.removeRecipe(relRecipeId, groupId) || removed;
}
@@ -336,6 +342,11 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
}
if (recipeId == null || !removeFullRecipe) {
this.grid.removeRecipe(slot.itemIndex, removeFullRecipe);
return true;
}
return this.grid.removeRecipe(recipeId, groupId);
}
@@ -435,7 +446,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
protected int resizeHeader(GuiContainer gui) {
final LayoutStyleMinecraft layout = (LayoutStyleMinecraft) LayoutManager.getLayoutStyle();
final int rows = (int) Math.ceil((double) layout.buttonCount / layout.numButtons);
final int diff = rows * 19 + getMarginTop(gui) - y;
final int diff = rows * 19 + PADDING - y;
if (diff > 0) {
y += diff;
@@ -493,24 +504,31 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
}
protected String getPositioningSettingName() {
return "world.panels.bookmarks";
}
public Rectangle4i calculateBounds() {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final int width = (gui.width - gui.xSize) / 2 - PADDING * 2;
final Rectangle4i bounds = new Rectangle4i(
PADDING,
PADDING,
(gui.width - 176) / 2 - PADDING * 2,
gui.height - PADDING * 2);
public int getMarginLeft(GuiContainer gui) {
return PADDING;
}
int paddingLeft = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.bookmarks.left").getIntValue() / 100000.0);
int paddingTop = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.bookmarks.top").getIntValue() / 100000.0);
int paddingRight = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.bookmarks.right").getIntValue() / 100000.0);
int paddingBottom = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.bookmarks.bottom").getIntValue() / 100000.0);
public int getMarginTop(GuiContainer gui) {
return PADDING;
}
bounds.h = Math.max(ItemsGrid.SLOT_SIZE, bounds.h - paddingTop - paddingBottom);
bounds.y = bounds.y + Math.min(paddingTop, bounds.h - ItemsGrid.SLOT_SIZE);
public int getWidth(GuiContainer gui) {
return gui.width - (gui.xSize + gui.width) / 2 - PADDING * 2;
}
bounds.w = Math.max(ItemsGrid.SLOT_SIZE, Math.min(bounds.w - paddingLeft - paddingRight, width - paddingLeft));
bounds.x = bounds.x + Math.min(paddingLeft, bounds.w - ItemsGrid.SLOT_SIZE);
public int getHeight(GuiContainer gui) {
return gui.height - getMarginTop(gui) - PADDING;
return bounds;
}
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
@@ -1195,7 +1213,10 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
&& !handlerName.isEmpty()
&& ingredients != null
&& !ingredients.isEmpty()) {
addRecipe(Recipe.of(Arrays.asList(stack), handlerName, ingredients), BookmarkGrid.DEFAULT_GROUP_ID);
addRecipe(
Recipe.of(Arrays.asList(stack), handlerName, ingredients),
saveSize ? 1 : 0,
BookmarkGrid.DEFAULT_GROUP_ID);
} else {
addItem(stack, saveSize);
}
@@ -1209,7 +1230,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
@Deprecated
public void addRecipe(BookmarkRecipe recipe, boolean saveSize, int groupId) {
addRecipe(recipe.getRecipe(), groupId);
addRecipe(recipe.getRecipe(), saveSize ? 1 : 0, groupId);
}
@Deprecated
+1 -1
View File
@@ -15,7 +15,7 @@ public abstract class Button extends Widget {
}
public int contentWidth() {
return getRenderIcon() == null ? getStringWidth(label) : getRenderIcon().width;
return getRenderIcon() == null ? getStringWidth(getRenderLabel()) : getRenderIcon().width;
}
@Override
@@ -1,10 +1,13 @@
package codechicken.nei;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -22,9 +25,11 @@ import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.resources.IResource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
@@ -147,6 +152,7 @@ public class ClientHandler {
loadHeightHackHandlers();
loadHiddenHandlers();
loadEnableAutoFocus();
loadGuiCraftablesBlacklist();
ItemInfo.preInit();
StackInfo.loadGuidFilters();
}
@@ -164,11 +170,34 @@ public class ClientHandler {
}, callback);
}
public static void loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
public static boolean loadSettingsResource(String resource, Consumer<Stream<String>> callback) {
final ResourceLocation location = new ResourceLocation("nei", resource);
try {
final IResource res = Minecraft.getMinecraft().getResourceManager().getResource(location);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(res.getInputStream(), StandardCharsets.UTF_8))) {
NEIClientConfig.logger.info("Loading '{}' from jar {}", resource, location);
callback.accept(
IOUtils.readLines(reader).stream()
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
.map(String::trim).filter(line -> !line.isEmpty()));
return true;
}
} catch (Exception io) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, location);
}
return false;
}
public static boolean loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
Consumer<Stream<String>> callback) {
File file = new File(NEIClientConfig.configDir, resource);
if (!file.exists()) {
if (!file.exists() && createDefault != null) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
createDefault.accept(file, writer);
@@ -177,13 +206,20 @@ public class ClientHandler {
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
callback.accept(
IOUtils.readLines(reader).stream().filter(line -> !line.startsWith("#") && !line.trim().isEmpty()));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
if (file.exists()) {
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
callback.accept(
IOUtils.readLines(reader).stream()
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
.map(String::trim).filter(line -> !line.isEmpty()));
return true;
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
}
}
return false;
}
public static void loadSerialHandlers() {
@@ -208,8 +244,13 @@ public class ClientHandler {
public static void loadEnableAutoFocus() {
loadSettingsFile(
"enableautofocus.cfg",
lines -> AutoFocusWidget.enableAutoFocusPrefixes = lines
.collect(Collectors.toCollection(ArrayList::new)));
lines -> AutoFocusWidget.enableAutoFocusPrefixes = lines.collect(Collectors.toList()));
}
public static void loadGuiCraftablesBlacklist() {
loadSettingsFile(
"guicraftablesblacklist.cfg",
lines -> ItemCraftablesPanel.guiBlacklist = lines.collect(Collectors.toList()));
}
public static void loadHiddenItems() {
@@ -1,24 +0,0 @@
package codechicken.nei;
import net.minecraft.item.ItemStack;
import codechicken.nei.api.ItemFilter;
public class DoubleFilteredFilter implements ItemFilter {
private ItemFilter filter;
private ItemList.PatternItemFilter patternFilter;
public DoubleFilteredFilter(ItemFilter filter, String itemFilter) {
this.filter = filter;
this.patternFilter = new ItemList.PatternItemFilter(SearchField.getPattern(itemFilter));
}
@Override
public boolean matches(ItemStack item) {
if (filter.matches(item)) {
return patternFilter.matches(item);
}
return false;
}
}
@@ -38,6 +38,10 @@ public class FormattedTextField extends GuiTextField {
this.formatter = formatter;
}
public TextFormatter getFormatter() {
return this.formatter;
}
public void setPlaceholder(String placeholder) {
this.placeholder = placeholder;
}
@@ -0,0 +1,289 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.NEIClientUtils.Alignment;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ReadableNumberConverter;
public class ItemCraftablesPanel
extends AbstractSubpanel<ItemsGrid<ItemCraftablesPanel.CraftablesGridSlot, MouseContext>> {
public static class CraftablesGridSlot extends ItemsGridSlot {
protected final long realAmount;
protected final boolean isFluidDisplay;
protected final RecipeId recipeId;
public CraftablesGridSlot(int slotIndex, int itemIndex, ItemStack itemStack, RecipeId recipeId) {
super(slotIndex, itemIndex, itemStack);
this.realAmount = StackInfo.getAmount(itemStack);
this.isFluidDisplay = StackInfo.itemStackToNBT(itemStack).hasKey("gtFluidName");
this.recipeId = recipeId;
}
@Override
public RecipeId getRecipeId() {
return this.recipeId;
}
@Override
public <M extends MouseContext> void afterDraw(Rectangle4i rect, M mouseContext) {
drawStackSize(rect);
}
protected void drawStackSize(Rectangle4i rect) {
long stackSize = this.realAmount;
if (stackSize > 1) {
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
String amountString = "";
if (stackSize < 10_000) {
amountString = String.valueOf(stackSize);
} else {
amountString = ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize);
}
if (this.isFluidDisplay) {
amountString += "L";
}
NEIClientUtils.drawNEIOverlayText(
amountString,
new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2),
panelFactor,
0xFFFFFF,
true,
this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
}
}
}
public static List<String> guiBlacklist = new ArrayList<>();
private final int MIN_DELAY = 600;
private Map<String, ArrayList<ICraftingHandler>> identHandlers = new HashMap<>();
private Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
private GuiContainer lastGuiContainer = null;
private ItemStackAmount lastPlayerInventory = null;
private long lastPlayerInventorySync = 0;
public ItemCraftablesPanel() {
this.grid = new ItemsGrid<>() {
protected List<CraftablesGridSlot> gridMask;
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
}
@Override
public List<CraftablesGridSlot> getMask() {
if (this.gridMask == null) {
int maxSlotIndex = this.rows * this.columns;
final AtomicInteger slotIndex = new AtomicInteger(0);
final List<CraftablesGridSlot> gridMask = new ArrayList<>();
this.realItems.clear();
if (!ItemCraftablesPanel.this.availableRecipes.isEmpty()) {
ItemCraftablesPanel.this.availableRecipes.entrySet().stream()
.sorted(Map.Entry.comparingByKey(ItemSorter.instance)).forEach(entry -> {
this.realItems.add(entry.getKey());
gridMask.add(
new CraftablesGridSlot(
slotIndex.get(),
slotIndex.get(),
entry.getKey(),
entry.getValue()));
slotIndex.incrementAndGet();
});
this.gridMask = gridMask.subList(0, Math.min(gridMask.size(), maxSlotIndex));
} else {
this.gridMask = Collections.emptyList();
}
}
return this.gridMask;
}
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
hovered.slotIndex,
hovered.slotIndex / this.columns,
hovered.slotIndex % this.columns);
}
return null;
}
};
}
@Override
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
return ItemQuantityField.prepareStackWithQuantity(itemStack, StackInfo.getAmount(itemStack));
}
@Override
public void draw(int mousex, int mousey) {
if (!this.availableRecipes.isEmpty()) {
super.draw(mousex, mousey);
}
}
@Override
public int setPanelWidth(int width) {
if (getGuiContainer() != this.lastGuiContainer) updateCraftables();
final int columns = width / ItemsGrid.SLOT_SIZE;
final int useRows = NEIClientConfig.getIntSetting("inventory.craftables.useRows");
final int rows = (int) Math.min(Math.ceil(this.availableRecipes.size() * 1f / columns), useRows);
this.w = width;
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
return rows;
}
public void update() {
this.splittingLineColor = NEIClientConfig.getSetting("inventory.craftables.color").getHexValue();
updateCraftables();
super.update();
}
private void updateCraftables() {
if (!NEIClientConfig.showCraftablesPanelWidget()) {
clearCraftables();
} else if (this.lastPlayerInventory == null
|| Math.abs(System.currentTimeMillis() - this.lastPlayerInventorySync) >= MIN_DELAY) {
final GuiContainer firstGui = getGuiContainer();
this.lastPlayerInventorySync = System.currentTimeMillis();
if (firstGui != null && !ItemCraftablesPanel.guiBlacklist.contains(firstGui.getClass().getName())) {
final ItemStackAmount inv = AutoCraftingManager.getInventoryItems(firstGui);
if (this.lastGuiContainer != (this.lastGuiContainer = firstGui)
|| !inv.equals(this.lastPlayerInventory)) {
this.lastPlayerInventory = inv;
this.availableRecipes = generateCraftables(firstGui);
this.grid.onGridChanged();
}
} else {
clearCraftables();
}
}
}
private void clearCraftables() {
if (!this.availableRecipes.isEmpty()) {
this.availableRecipes = Collections.emptyMap();
this.lastPlayerInventory = null;
this.grid.onGridChanged();
}
}
private GuiContainer getGuiContainer() {
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
return (firstGui instanceof GuiRecipe gui) ? gui.firstGui : firstGui;
}
private Map<ItemStack, RecipeId> generateCraftables(GuiContainer firstGui) {
final List<IRecipeHandler> availableHandlers = getAvailableHandlers(firstGui);
final Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
if (!availableHandlers.isEmpty()) {
final List<ItemStack> invStacks = ItemCraftablesPanel.this.lastPlayerInventory.values();
for (IRecipeHandler handler : availableHandlers) {
availableRecipes.putAll(filterHandlerRecipes(handler, firstGui, invStacks));
}
}
return availableRecipes;
}
private List<IRecipeHandler> getAvailableHandlers(GuiContainer firstGui) {
final List<IRecipeHandler> availableHandlers = new ArrayList<>();
for (String ident : RecipeInfo.getOverlayHandlerIdents(firstGui)) {
availableHandlers.addAll(identHandlers.computeIfAbsent(ident, GuiCraftingRecipe::getCraftingHandlers));
}
return availableHandlers;
}
private Map<ItemStack, RecipeId> filterHandlerRecipes(IRecipeHandler handler, GuiContainer firstGui,
List<ItemStack> invStacks) {
final Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
IntStream.range(0, handler.numRecipes()).parallel().forEach(recipeIndex -> {
if (existsIngredients(firstGui, handler.getIngredientStacks(recipeIndex), invStacks)) {
final Recipe recipe = Recipe.of(handler, recipeIndex);
if (!recipe.getResults().isEmpty()) {
synchronized (availableRecipes) {
availableRecipes.put(recipe.getResult(), recipe.getRecipeId());
}
}
}
});
return availableRecipes;
}
private boolean existsIngredients(GuiContainer firstGui, List<PositionedStack> ingredients,
List<ItemStack> invStacks) {
final Map<ItemStack, Integer> usedItems = new HashMap<>();
for (PositionedStack pStack : ingredients) {
final ItemStack used = invStacks.stream().filter(
is -> usedItems.getOrDefault(is, is.stackSize) >= pStack.item.stackSize && pStack.contains(is))
.findAny().orElse(null);
if (used != null) {
usedItems.put(used, usedItems.getOrDefault(used, used.stackSize) - pStack.item.stackSize);
} else {
return false;
}
}
return !usedItems.isEmpty();
}
}
@@ -3,85 +3,100 @@ package codechicken.nei;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.StackInfo;
public class ItemHistoryPanel extends Widget implements IContainerTooltipHandler {
public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemHistoryPanel.HistoryGridSlot, MouseContext>> {
protected int mouseDownItemIndex = -1;
public static class HistoryGridSlot extends ItemsGridSlot {
protected ItemsGrid<ItemsGridSlot, MouseContext> grid = new ItemsGrid<>() {
protected List<ItemsGridSlot> gridMask;
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
public HistoryGridSlot(int slotIndex, int itemIndex, ItemStack item) {
super(slotIndex, itemIndex, item);
}
@Override
public List<ItemsGridSlot> getMask() {
if (this.gridMask == null) {
this.gridMask = new ArrayList<>();
for (int slotIndex = 0; slotIndex < Math.min(size(), this.rows * this.columns); slotIndex++) {
this.gridMask.add(new ItemsGridSlot(slotIndex, slotIndex, getItem(slotIndex)));
}
}
return this.gridMask;
public RecipeId getRecipeId() {
return FavoriteRecipes.getFavorite(this.item);
}
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
hovered.slotIndex,
hovered.slotIndex / this.columns,
hovered.slotIndex % this.columns);
}
return null;
}
};
public boolean isEmpty() {
return grid.isEmpty();
}
public ItemHistoryPanel() {
this.grid = new ItemsGrid<>() {
protected List<HistoryGridSlot> gridMask;
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
}
@Override
public List<HistoryGridSlot> getMask() {
if (this.gridMask == null) {
this.gridMask = new ArrayList<>();
for (int slotIndex = 0; slotIndex < Math.min(size(), this.rows * this.columns); slotIndex++) {
this.gridMask.add(new HistoryGridSlot(slotIndex, slotIndex, getItem(slotIndex)));
}
}
return this.gridMask;
}
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final HistoryGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
hovered.slotIndex,
hovered.slotIndex / this.columns,
hovered.slotIndex % this.columns);
}
return null;
}
};
}
@Override
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
return ItemQuantityField.prepareStackWithQuantity(itemStack, 0);
}
@Override
public void draw(int mousex, int mousey) {
if (NEIClientConfig.getIntSetting("inventory.history.splittingMode") == 0) {
GuiDraw.drawRect(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue());
} else {
drawSplittingArea(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue());
if (this.grid.size() > 0) {
super.draw(mousex, mousey);
}
}
this.grid.draw(mousex, mousey);
@Override
public int setPanelWidth(int width) {
final int columns = width / ItemsGrid.SLOT_SIZE;
final int useRows = NEIClientConfig.getIntSetting("inventory.history.useRows");
final int rows = (int) Math.min(Math.ceil(this.grid.size() * 1f / columns), useRows);
this.w = width;
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
return rows;
}
public void update() {
this.grid.update();
this.splittingLineColor = NEIClientConfig.getSetting("inventory.history.color").getHexValue();
super.update();
}
public void addItem(ItemStack stack) {
if (stack != null) {
ItemStack is = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack), 0);
ItemStack is = StackInfo.withAmount(stack, 0);
this.grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true));
this.grid.realItems.add(0, is);
@@ -94,124 +109,4 @@ public class ItemHistoryPanel extends Widget implements IContainerTooltipHandler
}
}
public void resize(GuiContainer gui) {
this.grid.setGridSize(x, y + 4, w, h - 8);
this.grid.refresh(gui);
}
@Override
public ItemStack getStackMouseOver(int mousex, int mousey) {
final ItemsGridSlot slot = getSlotMouseOver(mousex, mousey);
return slot == null ? null : slot.getItemStack();
}
public ItemsGridSlot getSlotMouseOver(int mousex, int mousey) {
return grid.getSlotMouseOver(mousex, mousey);
}
private void drawSplittingArea(int x, int y, int width, int height, int color) {
float alpha = (color >> 24 & 255) / 255.0F;
float red = (color >> 16 & 255) / 255.0F;
float green = (color >> 8 & 255) / 255.0F;
float blue = (color & 255) / 255.0F;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_LINE_STIPPLE);
GL11.glColor4f(red, green, blue, alpha);
GL11.glLineWidth(2F);
GL11.glLineStipple(2, (short) 0x00FF);
GL11.glBegin(GL11.GL_LINE_LOOP);
GL11.glVertex2i(x, y);
GL11.glVertex2i(x + width, y);
GL11.glVertex2i(x + width, y + height);
GL11.glVertex2i(x, y + height);
GL11.glEnd();
GL11.glLineStipple(1, (short) 0xFFFF);
GL11.glLineWidth(1F);
GL11.glDisable(GL11.GL_LINE_STIPPLE);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glColor4f(1F, 1F, 1F, 1F);
GL11.glPopMatrix();
}
@Override
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
if (this.mouseDownItemIndex >= 0 && ItemPanels.itemPanel.draggedStack == null
&& NEIClientUtils.getHeldItem() == null
&& NEIClientConfig.hasSMPCounterPart()) {
final ItemsGridSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
if (mouseOverSlot == null || mouseOverSlot.itemIndex != this.mouseDownItemIndex || heldTime > 500) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(
this.grid.getItem(this.mouseDownItemIndex));
this.mouseDownItemIndex = -1;
}
}
}
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (handleClickExt(mousex, mousey, button)) return true;
final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null) {
if (button == 2) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack());
} else {
mouseDownItemIndex = hoverSlot.itemIndex;
}
return true;
}
return false;
}
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
return ItemQuantityField.prepareStackWithQuantity(itemStack, 0);
}
public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) {
final ItemStack hoverSlot = getStackMouseOver(mousex, mousey);
return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null;
}
@Override
public void mouseUp(int mousex, int mousey, int button) {
final ItemsGridSlot hoverSlot = getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.itemIndex == this.mouseDownItemIndex
&& ItemPanels.itemPanel.draggedStack == null) {
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
if (button == 0) {
GuiCraftingRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
} else if (button == 1) {
GuiUsageRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
}
} else {
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
}
}
this.mouseDownItemIndex = -1;
}
}
+64 -2
View File
@@ -20,6 +20,7 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraftforge.fluids.IFluidContainerItem;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
@@ -30,6 +31,8 @@ import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.search.TooltipFilter;
import codechicken.nei.util.ItemUntranslator;
import cpw.mods.fml.common.FMLLog;
public class ItemList {
@@ -97,7 +100,13 @@ public class ItemList {
public boolean matches(ItemStack item) {
String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName());
if (displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find()) {
if (displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
return true;
}
displayName = ItemUntranslator.getInstance().getItemStackDisplayName(item);
if (!displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
return true;
}
@@ -105,7 +114,7 @@ public class ItemList {
displayName = EnumChatFormatting
.getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item));
return displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find();
return displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find();
}
return false;
@@ -290,6 +299,57 @@ public class ItemList {
.collect(Collectors.toList());
}
private void runChecked(ItemStack stack, Runnable action, String reason) {
int hashOld = 0;
if (stack.hasTagCompound()) {
hashOld = stack.stackTagCompound.hashCode();
}
action.run();
if (stack.hasTagCompound() && hashOld != stack.stackTagCompound.hashCode()) {
FMLLog.warning("NEI: Forced tag update with reason (" + reason + ") for " + stack);
}
}
private void forceTagCompoundInitialization(ItemStack stack) {
Item item = stack.getItem();
if (item == null) {
return;
}
if (item instanceof IFluidContainerItem) {
IFluidContainerItem fluidItem = (IFluidContainerItem) item;
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
}
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
runChecked(stack, () -> item.getDamage(stack), "getDamage");
runChecked(stack, () -> item.showDurabilityBar(stack), "showDurabilityBar");
runChecked(stack, () -> item.getAttributeModifiers(stack), "getAttributeModifiers");
runChecked(stack, () -> item.getDurabilityForDisplay(stack), "getDurabilityForDisplay");
runChecked(stack, () -> item.getItemStackLimit(stack), "getItemStackLimit");
runChecked(stack, () -> item.getToolClasses(stack), "getToolClasses");
runChecked(stack, () -> item.getUnlocalizedNameInefficiently(stack), "getUnlocalizedNameInefficiently");
runChecked(stack, () -> item.hasEffect(stack), "hasEffect");
/*
* unused... for now runChecked(stack, () -> GameRegistry.getFuelValue(stack), "getFuelValue");
* runChecked(stack, () -> item.getUnlocalizedName(stack), "getUnlocalizedName"); runChecked(stack, () ->
* item.doesContainerItemLeaveCraftingGrid(stack), "doesContainerItemLeaveCraftingGrid"); runChecked(stack,
* () -> item.getItemUseAction(stack), "getItemUseAction"); runChecked(stack, () ->
* item.getMaxItemUseDuration(stack), "getMaxItemUseDuration"); runChecked(stack, () ->
* item.getPotionEffect(stack), "getPotionEffect"); runChecked(stack, () -> item.isPotionIngredient(stack),
* "isPotionIngredient"); runChecked(stack, () -> item.getRarity(stack), "getRarity"); runChecked(stack, ()
* -> item.hasCustomEntity(stack), "hasCustomEntity"); runChecked(stack, () ->
* item.getSmeltingExperience(stack), "getSmeltingExperience"); runChecked(stack, () ->
* item.getMaxDamage(stack), "getMaxDamage"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
* runChecked(stack, stack::isItemDamaged, "isItemDamaged"); runChecked(stack, () -> item.hasEffect(stack,
* 0), "hasEffect"); runChecked(stack, () -> item.getItemEnchantability(stack), "getItemEnchantability");
* runChecked(stack, () -> item.isBeaconPayment(stack), "isBeaconPayment"); runChecked(stack, () ->
* item.getDamage(stack), "getDamage"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
* runChecked(stack, () -> item.getDamage(stack), "getDamage");
*/
}
// Generate itemlist, permutations, orders, collapsibleitems, and informationhandler stacks
@Override
@SuppressWarnings("unchecked")
@@ -329,6 +389,8 @@ public class ItemList {
items.add(stack);
}
forceTagCompoundInitialization(stack);
CollapsibleItems.putItem(stack);
TooltipFilter.getSearchTooltip(stack);
InformationHandler.populateStacks(stack);
+61 -28
View File
@@ -8,6 +8,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsPanelGrid.ItemsPanelGridSlot;
import codechicken.nei.util.NEIMouseUtils;
@@ -32,7 +33,8 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
}
public ItemHistoryPanel historyPanel = new ItemHistoryPanel();
public Button toggleGroups;
public ItemCraftablesPanel craftablesPanel = new ItemCraftablesPanel();
protected Button toggleGroups;
@Deprecated
public static class ItemPanelSlot extends ItemsPanelGridSlot {
@@ -83,6 +85,11 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
toggleGroups = new Button("G") {
@Override
public String getRenderLabel() {
return NEIClientUtils.translate("itempanel.collapsed.button");
}
@Override
public String getButtonTip() {
return NEIClientUtils.translate("itempanel.collapsed.button.tip");
@@ -108,24 +115,31 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
return String.format("%d/%d", getPage(), Math.max(1, getNumPages()));
}
protected String getPositioningSettingName() {
return "world.panels.items";
}
public Rectangle4i calculateBounds() {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final int width = (gui.width - gui.xSize) / 2 - PADDING * 2;
final Rectangle4i bounds = new Rectangle4i(
(gui.width + gui.xSize) / 2 + PADDING,
PADDING,
(gui.width - 176) / 2 - PADDING * 2,
gui.height - PADDING * 2);
public int getMarginLeft(GuiContainer gui) {
return (gui.width + gui.xSize) / 2 + PADDING;
}
int paddingLeft = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.items.left").getIntValue() / 100000.0);
int paddingTop = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.items.top").getIntValue() / 100000.0);
int paddingRight = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.items.right").getIntValue() / 100000.0);
int paddingBottom = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.items.bottom").getIntValue() / 100000.0);
public int getMarginTop(GuiContainer gui) {
return PADDING;
}
bounds.h = Math.max(ItemsGrid.SLOT_SIZE, bounds.h - paddingTop - paddingBottom);
bounds.y = bounds.y + Math.min(paddingTop, bounds.h - ItemsGrid.SLOT_SIZE);
public int getWidth(GuiContainer gui) {
return gui.width - (gui.xSize + gui.width) / 2 - PADDING * 2;
}
bounds.w = Math.max(ItemsGrid.SLOT_SIZE, Math.min(bounds.w - paddingLeft - paddingRight, width - paddingRight));
bounds.x = bounds.x + Math.max(0, width - bounds.w - paddingRight);
public int getHeight(GuiContainer gui) {
return gui.height - getMarginTop(gui) - PADDING;
return bounds;
}
@Override
@@ -141,26 +155,38 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
}
protected int resizeFooter(GuiContainer gui) {
if (!NEIClientConfig.showItemQuantityWidget() && NEIClientConfig.isSearchWidgetCentered()
&& !NEIClientConfig.showHistoryPanelWidget()) {
int footerY = y + h;
int footerHeight = 0;
if (grid.getPerPage() == 0) {
return 0;
}
if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) {
footerY = LayoutManager.quantity.y;
footerHeight = LayoutManager.quantity.h + PanelWidget.PADDING;
}
if (NEIClientConfig.showHistoryPanelWidget()) {
historyPanel.x = x;
historyPanel.w = w;
historyPanel.h = 8 + ItemsGrid.SLOT_SIZE * NEIClientConfig.getIntSetting("inventory.history.useRows");
if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) {
historyPanel.y = LayoutManager.quantity.y - historyPanel.h - PanelWidget.PADDING;
return LayoutManager.quantity.h + historyPanel.h + PanelWidget.PADDING * 2;
} else {
historyPanel.y = y + h - historyPanel.h;
return historyPanel.h + PanelWidget.PADDING;
if (historyPanel.setPanelWidth(w) != 0) {
footerY = historyPanel.y = footerY - historyPanel.h;
footerHeight += historyPanel.h;
}
}
if (NEIClientConfig.showCraftablesPanelWidget()) {
craftablesPanel.x = x;
if (craftablesPanel.setPanelWidth(w) != 0) {
footerY = craftablesPanel.y = footerY - craftablesPanel.h;
footerHeight += craftablesPanel.h;
}
}
return LayoutManager.quantity.h + PanelWidget.PADDING;
return footerHeight;
}
@Override
@@ -168,20 +194,27 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
super.setVisible();
if (grid.getPerPage() > 0) {
if (!CollapsibleItems.isEmpty() && !grid.isEmpty()) {
LayoutManager.addWidget(toggleGroups);
}
if (NEIClientConfig.showHistoryPanelWidget() && (!grid.isEmpty() || !historyPanel.isEmpty())) {
if (NEIClientConfig.showCraftablesPanelWidget()) {
LayoutManager.addWidget(craftablesPanel);
}
if (NEIClientConfig.showHistoryPanelWidget()) {
LayoutManager.addWidget(historyPanel);
}
}
}
@Override
public void resize(GuiContainer gui) {
super.resize(gui);
historyPanel.resize(gui);
this.historyPanel.resize(gui);
this.craftablesPanel.resize(gui);
}
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
@@ -104,6 +104,16 @@ public class ItemStackAmount {
return this.itemMap.isEmpty();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ItemStackAmount items) {
return this.itemMap.equals(items.itemMap);
}
return false;
}
public static ItemStackAmount of(ItemStackAmount map) {
ItemStackAmount result = new ItemStackAmount();
result.itemMap.putAll(map.itemMap);
@@ -20,6 +20,7 @@ import org.lwjgl.opengl.GL30;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.StackInfo;
public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends ItemsGrid.MouseContext> {
@@ -171,6 +172,10 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
public <M extends MouseContext> void afterDraw(Rectangle4i rect, M mouseContext) {}
public RecipeId getRecipeId() {
return null;
}
public void drawItem(Rectangle4i rect) {
drawItem(getItemStack(), rect);
}
@@ -14,6 +14,7 @@ import net.minecraft.item.ItemStack;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.Recipe.RecipeId;
public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot, ItemsPanelGrid.MouseContext> {
@@ -37,6 +38,11 @@ public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot,
return this.bgItemStack;
}
@Override
public RecipeId getRecipeId() {
return FavoriteRecipes.getFavorite(this.item);
}
@Override
public void beforeDraw(Rectangle4i rect, MouseContext mouseContext) {
super.beforeDraw(rect, mouseContext);
@@ -4,6 +4,10 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map.Entry;
import org.lwjgl.input.Keyboard;
import codechicken.nei.util.NEIKeyboardUtils;
/**
* Good old down/held/up keystate tracker
*/
@@ -26,7 +30,8 @@ public class KeyManager {
public static void tickKeyStates() {
for (Entry<String, KeyState> entry : keyStates.entrySet()) {
final boolean down = NEIClientConfig.isKeyHashDown(entry.getKey());
final int keyCode = NEIKeyboardUtils.unhash(NEIClientConfig.getKeyBinding(entry.getKey()));
final boolean down = keyCode > 0 && keyCode < Keyboard.KEYBOARD_SIZE && Keyboard.isKeyDown(keyCode);
final KeyState state = entry.getValue();
if (down) {
@@ -52,7 +52,7 @@ public class LayoutStyleMinecraft extends LayoutStyle {
public void layout(GuiContainer gui, VisiblityData visiblity) {
reset();
leftSize = ItemPanels.bookmarkPanel.getWidth(gui);
leftSize = (gui.width - gui.xSize) / 2 - 4;
numButtons = Math.max(leftSize / 20, 1);
LayoutManager.delete.state = 0x4;
@@ -61,6 +61,7 @@ import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeCatalysts;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.util.ItemUntranslator;
import codechicken.nei.util.NEIKeyboardUtils;
import codechicken.obfuscator.ObfuscationRun;
@@ -146,6 +147,8 @@ public class NEIClientConfig {
tag.getTag("inventory.widgetsenabled").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.widgetsenabled"));
tag.getTag("findFuelsParallel").getBooleanValue(true);
tag.getTag("inventory.autocrafting").getBooleanValue(true);
tag.getTag("inventory.dynamicFontSize").getBooleanValue(true);
tag.getTag("inventory.hidden").getBooleanValue(false);
@@ -261,15 +264,21 @@ public class NEIClientConfig {
tag.getTag("inventory.history.enabled").setComment("Enable/disable History Panel").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.history.enabled", true));
tag.getTag("inventory.history.historyColor").setComment("Color of the history area display")
.getHexValue(0xee555555);
API.addOption(new OptionIntegerField("inventory.history.historyColor", 0, OptionIntegerField.UNSIGNED_INT_MAX));
tag.getTag("inventory.history.color").setComment("Color of the history area display").getHexValue(0xee555555);
API.addOption(new OptionIntegerField("inventory.history.color", 0, OptionIntegerField.UNSIGNED_INT_MAX));
tag.getTag("inventory.history.useRows").setComment("Rows used in historical areas").getIntValue(2);
API.addOption(new OptionIntegerField("inventory.history.useRows", 1, 5));
tag.getTag("inventory.history.splittingMode").getIntValue(1);
API.addOption(new OptionCycled("inventory.history.splittingMode", 2, true));
tag.getTag("inventory.craftables.enabled").setComment("Enable/disable Craftables Panel").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.craftables.enabled", true));
tag.getTag("inventory.craftables.color").setComment("Color of the craftables area display")
.getHexValue(0xee555555);
API.addOption(new OptionIntegerField("inventory.craftables.color", 0, OptionIntegerField.UNSIGNED_INT_MAX));
tag.getTag("inventory.craftables.useRows").setComment("Rows used in craftables areas").getIntValue(2);
API.addOption(new OptionIntegerField("inventory.craftables.useRows", 1, 5));
tag.getTag("inventory.collapsibleItems.enabled").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.collapsibleItems.enabled", true) {
@@ -391,6 +400,8 @@ public class NEIClientConfig {
});
tag.getTag("inventory.hotkeysHelpText").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.hotkeysHelpText", true));
tag.getTag("inventory.showHotkeys").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.showHotkeys", true));
tag.getTag("loadPluginsInParallel").getBooleanValue(true);
tag.getTag("itemLoadingTimeout").getIntValue(500);
@@ -416,6 +427,24 @@ public class NEIClientConfig {
tag.getTag("inventory.autocrafting").setComment("Autocrafting").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.autocrafting", true));
tag.getTag("inventory.itemUntranslator").setComment("Item Untranslator").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.itemUntranslator", true) {
@Override
public boolean onClick(int button) {
super.onClick(button);
if (enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
} else {
ItemUntranslator.getInstance().unload();
}
return true;
}
});
tag.getTag("tools.handler_load_from_config").setComment("ADVANCED: Load handlers from config")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("tools.handler_load_from_config", true) {
@@ -492,12 +521,15 @@ public class NEIClientConfig {
API.addOption(new OptionCycled("inventory.search.widgetAutofocus", 3, true));
tag.getTag("inventory.search.patternMode").setComment("Search Mode").getIntValue(1);
API.addOption(new OptionCycled("inventory.search.patternMode", 3, true) {
API.addOption(new OptionCycled("inventory.search.patternMode", 4, true) {
@Override
public boolean onClick(int button) {
if (!super.onClick(button)) {
return false;
}
SearchField.searchParser.clearCache();
return super.onClick(button);
return true;
}
});
@@ -550,7 +582,7 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
// SearchField.searchParser.clearCache();
SearchField.searchParser.clearCache();
return super.onClick(button);
}
@@ -567,8 +599,11 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
if (!super.onClick(button)) {
return false;
}
SearchField.searchParser.clearCache();
return super.onClick(button);
return true;
}
@Override
@@ -592,8 +627,11 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
if (!super.onClick(button)) {
return false;
}
SearchField.searchParser.clearCache();
return super.onClick(button);
return true;
}
@Override
@@ -616,8 +654,11 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
if (!super.onClick(button)) {
return false;
}
SearchField.searchParser.clearCache();
return super.onClick(button);
return true;
}
@Override
@@ -640,8 +681,11 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
if (!super.onClick(button)) {
return false;
}
SearchField.searchParser.clearCache();
return super.onClick(button);
return true;
}
@Override
@@ -664,8 +708,11 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
if (!super.onClick(button)) {
return false;
}
SearchField.searchParser.clearCache();
return super.onClick(button);
return true;
}
@Override
@@ -683,6 +730,9 @@ public class NEIClientConfig {
});
tag.getTag("inventory.search.logSearchExceptions").setComment("Search exceptions for extended+")
.getBooleanValue(false);
String prefixRedefinitions = tag.getTag("inventory.search.prefixRedefinitions").setComment(
"Redefine search prefixes by providing a char-to-char map (JSON). The keys are the original prefixes, the values the new ones. Example: { \"$\": \"€\", \"#\": \"+\", \"@\": \"°\", \"%\": \"!\" }")
.getValue("{}");
@@ -693,6 +743,7 @@ public class NEIClientConfig {
};
Map<Character, Character> charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType());
SearchField.searchParser.prefixRedefinitions.putAll(charMap);
SearchField.searchParser.clearCache();
} catch (JsonParseException e) {
logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e);
}
@@ -744,6 +795,7 @@ public class NEIClientConfig {
API.addHashBind("gui.hide", Keyboard.KEY_O);
API.addHashBind("gui.search", Keyboard.KEY_F);
API.addKeyBind("gui.bookmark", Keyboard.KEY_A);
API.addHashBind("gui.favorite", Keyboard.KEY_F + NEIClientUtils.SHIFT_HASH);
API.addHashBind("gui.remove_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH);
API.addKeyBind("gui.bookmark_pull_items", Keyboard.KEY_V);
API.addKeyBind("gui.overlay", Keyboard.KEY_S);
@@ -858,7 +910,7 @@ public class NEIClientConfig {
final int hash = getKeyBinding(string);
if (hash != Keyboard.CHAR_NONE && Keyboard.getEventKeyState()) {
return KeyManager.keyStates.containsKey(string) ? hash == Keyboard.getEventKey()
return KeyManager.keyStates.containsKey(string) ? Keyboard.isKeyDown(NEIKeyboardUtils.unhash(hash))
: hash == NEIClientUtils.getKeyHash();
}
@@ -1165,6 +1217,10 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.history.enabled");
}
public static boolean showCraftablesPanelWidget() {
return getBooleanSetting("inventory.craftables.enabled");
}
public static int getGridRenderingCacheMode() {
return OpenGlHelper.framebufferSupported ? getIntSetting("inventory.gridRenderingCacheMode") : 0;
}
@@ -1173,6 +1229,10 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.collapsibleItems.enabled");
}
public static boolean enableItemUntranslator() {
return getBooleanSetting("inventory.itemUntranslator");
}
public static boolean getMagnetMode() {
return enabledActions.contains("magnet");
}
@@ -21,6 +21,7 @@ import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.commands.CommandBookmarkAdd;
import codechicken.nei.commands.CommandRecipeId;
import codechicken.nei.commands.CommandUntranslator;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerSlotClickHandler;
@@ -68,6 +69,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
public static void registerClientCommands() {
ClientCommandHandler.instance.registerCommand(new CommandBookmarkAdd());
ClientCommandHandler.instance.registerCommand(new CommandRecipeId());
ClientCommandHandler.instance.registerCommand(new CommandUntranslator());
}
public static void updateUnlimitedItems(InventoryPlayer inventory) {
@@ -136,7 +138,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
return true;
}
if (button == 1 && slot instanceof SlotCrafting) // right click
if (button == 1 && modifier != 2 && slot instanceof SlotCrafting) // right click
{
for (int i1 = 0; i1 < 64; i1++) // click this slot 64 times
manager.handleSlotClick(slot.slotNumber, button, 0);
+6 -35
View File
@@ -84,44 +84,15 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
public abstract String getLabelText();
protected abstract String getPositioningSettingName();
public abstract int getMarginLeft(GuiContainer gui);
public abstract int getMarginTop(GuiContainer gui);
public abstract int getWidth(GuiContainer gui);
public abstract int getHeight(GuiContainer gui);
public abstract Rectangle4i calculateBounds();
public void resize(GuiContainer gui) {
final Rectangle4i margin = new Rectangle4i(
getMarginLeft(gui),
getMarginTop(gui),
getWidth(gui),
getHeight(gui));
final Rectangle4i bounds = calculateBounds();
final String settingName = getPositioningSettingName();
int paddingLeft = (int) Math
.ceil(margin.w * NEIClientConfig.getSetting(settingName + ".left").getIntValue() / 100000.0);
int paddingTop = (int) Math
.ceil(margin.h * NEIClientConfig.getSetting(settingName + ".top").getIntValue() / 100000.0);
int paddingRight = (int) Math
.ceil(margin.w * NEIClientConfig.getSetting(settingName + ".right").getIntValue() / 100000.0);
int paddingBottom = (int) Math
.ceil(margin.h * NEIClientConfig.getSetting(settingName + ".bottom").getIntValue() / 100000.0);
int deltaHeight = Math.min(0, margin.h - paddingTop - paddingBottom - ItemsGrid.SLOT_SIZE) / 2;
paddingLeft = Math.min(paddingLeft, Math.max(0, margin.w - paddingRight - ItemsGrid.SLOT_SIZE));
paddingRight = Math.min(paddingRight, Math.max(0, margin.w - paddingLeft - ItemsGrid.SLOT_SIZE));
paddingTop = Math.min(margin.h - ItemsGrid.SLOT_SIZE, Math.max(0, paddingTop + deltaHeight));
paddingBottom = Math.min(margin.h - paddingTop - ItemsGrid.SLOT_SIZE, Math.max(0, paddingBottom - deltaHeight));
x = margin.x + paddingLeft;
y = margin.y + paddingTop;
w = margin.w - paddingLeft - paddingRight;
h = margin.h - paddingTop - paddingBottom;
x = bounds.x;
y = bounds.y;
w = bounds.w;
h = bounds.h;
final int header = resizeHeader(gui);
final int footer = resizeFooter(gui);
+28 -6
View File
@@ -21,6 +21,7 @@ import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import codechicken.core.CommonUtils;
import codechicken.nei.SubsetWidget.SubsetTag;
import codechicken.nei.api.API;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
@@ -33,6 +34,7 @@ import codechicken.nei.util.NBTJson;
public class PresetsList {
public enum PresetMode {
SUBSET,
HIDE,
REMOVE,
GROUP;
@@ -66,11 +68,11 @@ public class PresetsList {
}
}
protected static class RecipesFilter implements IRecipeFilterProvider, IRecipeFilter {
protected static class PresetRecipeFilter implements IRecipeFilterProvider, IRecipeFilter {
public Set<String> cache;
public IRecipeFilter getFilter() {
public IRecipeFilter getRecipeFilter() {
if (cache == null) {
cache = PresetsList.presets.stream().filter(p -> p.enabled && p.mode == PresetMode.REMOVE)
@@ -81,17 +83,20 @@ public class PresetsList {
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
public boolean matches(IRecipeHandler handler, int recipeIndex) {
if (matchPositionedStack(ingredients, false)) {
if (matchPositionedStack(handler.getIngredientStacks(recipeIndex), false)) {
return false;
}
final PositionedStack result = handler.getResultStack(recipeIndex);
if (result != null && matchPositionedStack(result)) {
return true;
}
final List<PositionedStack> others = handler.getOtherStacks(recipeIndex);
if (!others.isEmpty() && matchPositionedStack(others, true)) {
return true;
}
@@ -143,7 +148,7 @@ public class PresetsList {
}
public static final List<Preset> presets = new ArrayList<>();
protected static RecipesFilter recipeFilter = new RecipesFilter();
protected static PresetRecipeFilter recipeFilter = new PresetRecipeFilter();
protected static ItemPanelFilter itemFilter = new ItemPanelFilter();
static {
@@ -216,6 +221,8 @@ public class PresetsList {
if (!preset.items.isEmpty()) {
presets.add(preset);
}
updateSubsets();
}
public static void savePresets() {
@@ -241,9 +248,24 @@ public class PresetsList {
recipeFilter.cache = null;
itemFilter.cache = null;
updateSubsets();
CollapsibleItems.saveStates();
CollapsibleItems.load();
LayoutManager.markItemsDirty();
}
private static void updateSubsets() {
SubsetWidget.removeTag("Presets");
for (Preset preset : PresetsList.presets) {
if (preset.enabled && preset.mode == PresetMode.SUBSET) {
SubsetWidget.addTag(
new SubsetTag(
"Presets." + preset.name,
stack -> preset.items.contains(Preset.getIdentifier(stack))));
}
}
}
}
@@ -3,10 +3,13 @@ package codechicken.nei;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.SearchField.GuiSearchField;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.util.TextHistory;
public abstract class RecipeSearchField extends TextField {
public abstract class RecipeSearchField extends TextField implements ItemFilterProvider, IRecipeFilterProvider {
private static final TextHistory history = new TextHistory();
@@ -17,9 +20,12 @@ public abstract class RecipeSearchField extends TextField {
@Override
protected void initInternalTextField() {
field = new GuiSearchField();
final GuiSearchField field = new GuiSearchField();
field.setMaxStringLength(maxSearchLength);
field.setCursorPositionZero();
field.setSkipRecipeTokens(true);
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
this.field = field;
}
public boolean isVisible() {
@@ -58,10 +64,16 @@ public abstract class RecipeSearchField extends TextField {
return EnumChatFormatting.getTextWithoutFormattingCodes(s);
}
@Override
public ItemFilter getFilter() {
return ((GuiSearchField) field).getFilter();
}
@Override
public IRecipeFilter getRecipeFilter() {
return ((GuiSearchField) field).getRecipeFilter();
}
@Override
public void setFocus(boolean focus) {
final boolean previousFocus = field.isFocused();
+24 -3
View File
@@ -16,6 +16,8 @@ import codechicken.lib.gui.GuiDraw;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.recipe.StackInfo;
@@ -88,9 +90,10 @@ public class SearchField extends TextField implements ItemFilterProvider {
}
}
public static class GuiSearchField extends FormattedTextField {
public static class GuiSearchField extends FormattedTextField implements ItemFilterProvider, IRecipeFilterProvider {
protected final SearchTokenParser searchParser;
protected boolean skipRecipeTokens = false;
public GuiSearchField() {
this(SearchField.searchParser);
@@ -104,12 +107,26 @@ public class SearchField extends TextField implements ItemFilterProvider {
setPlaceholder(NEIClientUtils.translate("inventory.search.placeholder"));
}
public void setSkipRecipeTokens(boolean skipRecipeTokens) {
this.skipRecipeTokens = skipRecipeTokens;
}
@Override
public ItemFilter getFilter() {
return getFilter(getText());
}
@Override
public IRecipeFilter getRecipeFilter() {
return getRecipeFilter(getText());
}
public ItemFilter getFilter(String filterText) {
return this.searchParser.getFilter(filterText);
return this.searchParser.getFilter(filterText, this.skipRecipeTokens);
}
public IRecipeFilter getRecipeFilter(String filterText) {
return this.searchParser.getRecipeFilter(filterText);
}
}
@@ -272,7 +289,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
default:
break;
}
// regex and extended+
if (!search.isEmpty()) {
try {
return Pattern.compile(search, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
@@ -305,6 +322,10 @@ public class SearchField extends TextField implements ItemFilterProvider {
case 2:
text = text.replaceAll("[{}()\\[\\].+*?^$\\\\|]", "\\\\$0");
break;
case 3:
text = text.replaceAll("\"", "\\\\$0");
text = "\"" + text + "\"";
return text;
default:
break;
}
@@ -1,66 +1,96 @@
package codechicken.nei;
import java.util.List;
import java.util.StringJoiner;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.FormattedTextField.TextFormatter;
import codechicken.nei.SearchTokenParser.SearchToken;
import codechicken.nei.search.SearchExpressionFormatVisitor;
import codechicken.nei.search.SearchExpressionUtils;
import codechicken.nei.search.SearchToken;
public class SearchTextFormatter implements TextFormatter {
protected final SearchTokenParser searchParser;
protected String contextToken = "";
public SearchTextFormatter(SearchTokenParser searchParser) {
this.searchParser = searchParser;
}
public void setContextToken(String contextToken) {
this.contextToken = contextToken;
}
@Override
public String format(String text) {
final String[] parts = (text + "| ").split("\\|");
StringJoiner formattedText = new StringJoiner(EnumChatFormatting.GRAY + "|");
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode == 3) {
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (spaceModeEnabled == 1) {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
final SearchExpressionFormatVisitor visitor = new SearchExpressionFormatVisitor(searchParser);
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
} else {
final StringBuilder formattedText = new StringBuilder();
for (int i = 0; i < parts.length - 1; i++) {
final String filterText = parts[i];
final List<SearchToken> tokens = searchParser.splitSearchText(filterText);
StringBuilder formattedPart = new StringBuilder();
int startIndex = 0;
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
final String filterText = orQuery[1];
for (SearchToken token : tokens) {
formattedPart.append(filterText.substring(startIndex, token.start));
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
if (token.firstChar != null) {
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) {
if (!contextPart[0].isEmpty()) {
formattedText.append(
SearchExpressionUtils.HIGHLIGHTS.RECIPE + contextPart[0].substring(0, 1)
+ EnumChatFormatting.RESET);
contextPart[1] = contextPart[0].substring(1) + contextPart[1];
}
if (contextPart[1].isEmpty()) {
continue;
}
int startIndex = 0;
for (SearchToken token : searchParser.splitSearchText(contextPart[1])) {
formattedText.append(contextPart[1].substring(startIndex, token.start));
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;
if (token.firstChar != null) {
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
}
if (token.ignore != null) {
formattedText
.append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + String.valueOf(token.ignore));
}
if (token.firstChar != null) {
formattedText.append(tokenColor + String.valueOf(token.firstChar));
}
if (token.quotes) {
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
if (!token.rawText.isEmpty()) {
formattedText.append(tokenColor + token.rawText);
}
if (token.quotes) {
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
startIndex = token.end;
}
formattedText.append(contextPart[1].substring(startIndex, contextPart[1].length()));
}
if (token.ignore) {
formattedPart.append(EnumChatFormatting.BLUE + "-");
}
if (token.firstChar != null) {
formattedPart.append(tokenColor + String.valueOf(token.firstChar));
}
if (token.quotes) {
formattedPart.append(EnumChatFormatting.GOLD + "\"");
}
if (!token.rawText.isEmpty()) {
formattedPart.append(tokenColor + token.rawText);
}
if (token.quotes) {
formattedPart.append(EnumChatFormatting.GOLD + "\"");
}
startIndex = token.end;
}
formattedPart.append(filterText.substring(startIndex, filterText.length()));
formattedText.add(formattedPart);
return formattedText.toString();
}
return formattedText.toString();
}
}
@@ -1,14 +1,11 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.Language;
@@ -18,9 +15,16 @@ import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.filter.AllMultiRecipeFilter;
import codechicken.nei.filter.AnyMultiRecipeFilter;
import codechicken.nei.filter.RecipeFilter;
import codechicken.nei.filter.RecipeFilter.FilterContext;
import codechicken.nei.search.ItemFilterVisitor;
import codechicken.nei.search.RecipeFilterVisitor;
import codechicken.nei.search.SearchExpressionUtils;
import codechicken.nei.search.SearchToken;
public class SearchTokenParser {
@@ -39,20 +43,6 @@ public class SearchTokenParser {
}
}
public static class SearchToken {
public boolean ignore = false;
public boolean quotes = false;
public Character firstChar = null;
public String[] words;
public String rawText = "";
public int start = 0;
public int end = 0;
}
public static interface ISearchParserProvider {
public ItemFilter getFilter(String searchText);
@@ -83,7 +73,7 @@ public class SearchTokenParser {
}
}
private static class IsRegisteredItemFilter implements ItemFilter {
public static class IsRegisteredItemFilter implements ItemFilter {
public ItemFilter filter;
@@ -99,10 +89,14 @@ public class SearchTokenParser {
}
}
public static final Pattern SPACE_PATTERN = Pattern.compile(" ");
protected final LRUCache<String, ItemFilter> filtersCache = new LRUCache<>(20);
protected final LRUCache<String, IRecipeFilter> recipesCache = new LRUCache<>(20);
protected final List<ISearchParserProvider> searchProviders;
protected final ProvidersCache providersCache = new ProvidersCache();
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
protected String prefixes = null;
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
this.searchProviders = searchProviders;
@@ -114,22 +108,22 @@ public class SearchTokenParser {
public void addProvider(ISearchParserProvider provider) {
this.searchProviders.add(provider);
this.providersCache.clear();
this.filtersCache.clear();
clearCache();
}
public void clearCache() {
this.providersCache.clear();
this.filtersCache.clear();
this.recipesCache.clear();
this.prefixes = null;
}
protected List<ISearchParserProvider> getProviders() {
public List<ISearchParserProvider> getProviders() {
Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage();
if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) {
providersCache.providers = new ArrayList<>();
providersCache.languageCode = currentLanguage.getLanguageCode();
Map<Character, ISearchParserProvider> providers = new HashMap<>();
final List<ISearchParserProvider> providersList = new ArrayList<>();
final Map<Character, ISearchParserProvider> providers = new HashMap<>();
for (int index = this.searchProviders.size() - 1; index >= 0; index--) {
ISearchParserProvider provider = this.searchProviders.get(index);
@@ -138,177 +132,360 @@ public class SearchTokenParser {
if (provider.getSearchMode() == SearchMode.PREFIX && !providers.containsKey(provider.getPrefix())) {
providers.put(provider.getPrefix(), provider);
} else if (provider.getSearchMode() == SearchMode.ALWAYS) {
providersCache.providers.add(provider);
providersList.add(provider);
}
}
}
providersCache.providers.addAll(providers.values());
providersList.addAll(providers.values());
providersCache.languageCode = currentLanguage.getLanguageCode();
providersCache.providers = providersList;
}
return providersCache.providers;
}
public boolean hasRedefinedPrefix(char ch) {
return getPrefixes().indexOf(ch) >= 0;
}
public ISearchParserProvider getProvider(char ch) {
return getProviders().stream()
.filter(
provider -> provider.getSearchMode() == SearchMode.PREFIX
&& getRedefinedPrefix(provider.getPrefix()) == ch)
.findFirst().orElse(null);
if (!hasRedefinedPrefix(ch)) {
return null;
}
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchMode.PREFIX && getRedefinedPrefix(provider.getPrefix()) == ch) {
return provider;
}
}
return null;
}
public List<ItemFilter> getAlwaysProvidersFilters(String searchText) {
final List<ItemFilter> filters = new ArrayList<>();
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchMode.ALWAYS) {
filters.add(provider.getFilter(searchText));
}
}
return filters;
}
public synchronized ItemFilter getFilter(String filterText) {
return getFilter(filterText, false);
}
public synchronized ItemFilter getFilter(String rawText, boolean skipRecipeTokens) {
final String filterText = EnumChatFormatting.getTextWithoutFormattingCodes(rawText).toLowerCase();
if (filterText == null || filterText.isEmpty()) {
return new EverythingItemFilter();
}
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode != 3) {
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
final List<ItemFilter> searchTokens = new ArrayList<>();
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
if (subQuery[1].isEmpty()) {
continue;
}
final List<ItemFilter> tokens = new ArrayList<>();
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
continue;
}
if (!contextQuery[0].isEmpty()) {
contextQuery[0] = contextQuery[0].substring(1);
}
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
final ItemFilter result = token.getFilter(this);
if (result != null) {
tokens.add(result);
}
}
}
if (tokens.size() == 1) {
searchTokens.add(tokens.get(0));
} else if (!tokens.isEmpty()) {
searchTokens.add(new AllMultiItemFilter(tokens));
}
}
if (searchTokens.isEmpty()) {
return new EverythingItemFilter();
} else if (searchTokens.size() == 1) {
return new IsRegisteredItemFilter(searchTokens.get(0));
} else {
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
}
});
} else {
return this.filtersCache.computeIfAbsent(filterText, text -> {
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (spaceMode == 1) {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
if (skipRecipeTokens) {
return new IsRegisteredItemFilter(
SearchExpressionUtils.visitRecipeSearchExpression(text, new ItemFilterVisitor(this)));
} else {
return new IsRegisteredItemFilter(
SearchExpressionUtils.visitSearchExpression(text, new ItemFilterVisitor(this)));
}
});
}
}
public synchronized IRecipeFilter getRecipeFilter(String filterText) {
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
return this.filtersCache.computeIfAbsent(filterText, text -> {
final String[] parts = text.split("\\|");
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(this::parseSearchText).filter(s -> s != null)
.collect(Collectors.toList());
if (filterText == null || filterText.isEmpty()) {
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
}
if (searchTokens.isEmpty()) {
return new EverythingItemFilter();
} else if (searchTokens.size() == 1) {
return new IsRegisteredItemFilter(searchTokens.get(0));
} else {
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
}
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
});
if (patternMode != 3) {
return this.recipesCache.computeIfAbsent(filterText, input -> {
final List<IRecipeFilter> searchTokens = new ArrayList<>();
for (String[] orQueryPart : splitByDelimiters(input, "|", false)) {
if (orQueryPart[1].isEmpty()) {
continue;
}
final List<IRecipeFilter> contextList = new ArrayList<>();
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
if (contextQuery[1].isEmpty()) {
continue;
}
final List<IRecipeFilter> tokens = new ArrayList<>();
FilterContext context = FilterContext.ANY;
if (!contextQuery[0].isEmpty()) {
context = FilterContext.fromChar(contextQuery[0].charAt(0));
contextQuery[0] = contextQuery[0].substring(1);
}
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
final ItemFilter result = token.getFilter(this);
if (result != null) {
tokens.add(
new RecipeFilter(context, token.ignore == null || token.ignore != '!', result));
}
}
if (tokens.size() == 1) {
contextList.add(tokens.get(0));
} else if (!tokens.isEmpty()) {
contextList.add(new AllMultiRecipeFilter(tokens));
}
}
if (contextList.size() == 1) {
searchTokens.add(contextList.get(0));
} else if (!contextList.isEmpty()) {
searchTokens.add(new AllMultiRecipeFilter(contextList));
}
}
if (searchTokens.isEmpty()) {
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
} else if (searchTokens.size() == 1) {
return searchTokens.get(0);
} else {
return new AnyMultiRecipeFilter(searchTokens);
}
});
} else {
return this.recipesCache.computeIfAbsent(filterText, text -> {
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (spaceMode == 1) {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
return SearchExpressionUtils.visitRecipeSearchExpression(text, new RecipeFilterVisitor(this));
});
}
}
public List<SearchToken> splitSearchText(String filterText) {
if (filterText.isEmpty()) {
return Collections.emptyList();
}
final List<SearchToken> tokens = new ArrayList<>();
final String prefixes = getPrefixes();
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
// The regular expression first tries to match a string that starts with a space or the beginning of the string,
// followed by a sequence of characters that do not contain special characters -"@#$%&, and ends with a space
// and the characters -"@#$%& or the end of the string.
final String patternPart1 = "(?<tokenA>(^|\\s+)(?:[^" + Pattern.quote(" -\"" + prefixes)
+ "]).+?(?=\\s+["
+ Pattern.quote("-\"" + prefixes)
+ "]|$))";
// If the first condition is not met, it tries to match a sequence of - characters, followed by @#$%&
// characters, then either a quoted string or non-space characters.
final String patternPart2 = "((?<ignore>-*)(?<firstChar>[" + Pattern.quote(prefixes)
+ "]*)(?<tokenB>\\\".*?(?:\\\"|$)|\\S+\\s*))";
final Pattern pattern = Pattern.compile(spaceMode == 0 ? patternPart2 : (patternPart1 + "|" + patternPart2));
final Matcher filterMatcher = pattern.matcher(filterText);
final String prefixes = getPrefixes();
SearchToken lastToken = null;
int lastEnd = 0;
while (filterMatcher.find()) {
String firstChar = filterMatcher.group("firstChar");
SearchToken token = new SearchToken();
token.start = filterMatcher.start();
token.end = filterMatcher.end();
token.ignore = "-".equals(filterMatcher.group("ignore"));
token.rawText = spaceMode == 0 ? null : filterMatcher.group("tokenA");
for (String[] part : splitByDelimiters(filterText, "-!" + prefixes, true)) {
final SearchToken token = createToken(lastEnd, part, prefixes);
final int length = part[0].length() + part[1].length();
if (firstChar != null && !firstChar.isEmpty()) {
token.firstChar = firstChar.charAt(0);
if (token.isSimpleFilter() && token.rawText.isEmpty()) {
lastEnd += length;
continue;
}
if (token.rawText == null) { // spaceMode == 0
token.rawText = filterMatcher.group("tokenB");
token.quotes = token.rawText.length() > 1 && token.rawText.startsWith("\"")
&& token.rawText.endsWith("\"");
if (spaceMode == 0) { // and
tokens.add(lastToken = token);
} else if (spaceMode == 1) { // space
if (token.quotes) {
token.rawText = token.rawText.substring(1, token.rawText.length() - 1);
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
lastToken.words[0] = lastToken.rawText;
lastToken.end = token.end;
} else {
tokens.add(lastToken = token);
}
} else if (spaceMode == 2) { // and-x
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
final String[] newWords = new String[lastToken.words.length + token.words.length];
System.arraycopy(lastToken.words, 0, newWords, 0, lastToken.words.length);
System.arraycopy(token.words, 0, newWords, lastToken.words.length, token.words.length);
lastToken.words = newWords;
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
lastToken.end = token.end;
} else {
tokens.add(lastToken = token);
}
token.words = new String[] { token.rawText.trim() };
} else if (spaceMode == 2) {
token.words = token.rawText.trim().split("\\s+");
} else {
token.words = new String[] { token.rawText.trim() };
}
tokens.add(token);
lastEnd += length;
}
return tokens;
}
private String getPrefixes() {
StringBuilder prefixes = new StringBuilder().append('\0');
private SearchToken createToken(int lastEnd, String[] part, String prefixes) {
final SearchToken token = new SearchToken();
String pref = part[0];
String query = part[1].trim();
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchMode.PREFIX) {
prefixes.append(getRedefinedPrefix(provider.getPrefix()));
token.start = lastEnd;
token.end = lastEnd + pref.length() + query.length();
if (pref.startsWith("-")) {
token.ignore = '-';
pref = pref.substring(1);
} else if (pref.startsWith("!")) {
token.ignore = '!';
pref = pref.substring(1);
}
if (!pref.isEmpty()) {
char firstChar = pref.charAt(0);
if (prefixes.indexOf(firstChar) >= 0) {
token.firstChar = firstChar;
pref = pref.substring(1);
}
}
return prefixes.toString();
String text = pref + query;
token.quotes = text.length() > 1 && text.startsWith("\"") && text.endsWith("\"");
if (token.quotes) {
text = text.substring(1, text.length() - 1);
}
token.rawText = text;
token.words = new String[] { token.quotes ? text.replaceAll("\\\\\"", "\"") : text };
return token;
}
public List<String[]> splitByDelimiters(String input, String delimiters, boolean space) {
if (delimiters == null || delimiters.isEmpty()) {
return Collections.singletonList(new String[] { "", input });
}
final List<String[]> tokens = new ArrayList<>();
final int length = input.length();
boolean insideQuotes = false;
String token = "";
int lastEnd = 0;
int index = 0;
while (index < length) {
final char ch = input.charAt(index);
if (!insideQuotes && delimiters.indexOf(ch) >= 0
&& (!space || index == 0 || input.charAt(index - 1) == ' ')) {
if (lastEnd == index) {
token += String.valueOf(ch);
} else {
tokens.add(new String[] { token, input.substring(lastEnd, index) });
token = String.valueOf(ch);
}
lastEnd = index + 1;
} else if (!insideQuotes && space && ch != ' ' && (index > 0 && input.charAt(index - 1) == ' ')) {
tokens.add(new String[] { token, input.substring(lastEnd, index) });
token = "";
lastEnd = index;
}
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
insideQuotes = !insideQuotes;
}
index++;
}
tokens.add(new String[] { token, input.substring(lastEnd) });
return tokens;
}
private String getPrefixes() {
if (this.prefixes == null) {
final StringBuilder builder = new StringBuilder();
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) {
builder.append(getRedefinedPrefix(provider.getPrefix()));
}
}
this.prefixes = builder.toString();
}
return this.prefixes;
}
public char getRedefinedPrefix(char prefix) {
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
}
private ItemFilter parseSearchText(String filterText) {
if (filterText.isEmpty()) {
return null;
}
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
final List<SearchToken> tokens = splitSearchText(filterText);
for (SearchToken token : tokens) {
if (!token.rawText.isEmpty()) {
ItemFilter result = parseToken(token);
if (token.ignore) {
searchTokens.filters.add(new NegatedItemFilter(result));
} else {
searchTokens.filters.add(result);
}
} else if (!token.ignore) {
searchTokens.filters.add(new NothingItemFilter());
}
}
return searchTokens;
}
private ItemFilter parseToken(SearchToken token) {
final ISearchParserProvider provider = token.firstChar == null ? null : this.getProvider(token.firstChar);
if (provider == null || provider.getSearchMode() == SearchMode.NEVER) {
final List<ItemFilter> filters = new ArrayList<>();
for (ISearchParserProvider _provider : getProviders()) {
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
AllMultiItemFilter filter = generateFilters(_provider, token.words);
if (!filter.filters.isEmpty()) {
filters.add(filter);
}
}
}
return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters);
} else {
return generateFilters(provider, token.words);
}
}
private AllMultiItemFilter generateFilters(ISearchParserProvider provider, String[] words) {
final AllMultiItemFilter filters = new AllMultiItemFilter();
for (String work : words) {
final ItemFilter filter = provider.getFilter(work);
if (filter != null) {
filters.filters.add(filter);
}
}
return filters;
}
}
@@ -13,6 +13,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -199,7 +200,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
public String name() {
int idx = this.fullname.indexOf('.');
int idx = this.fullname.lastIndexOf('.');
return idx < 0 ? this.fullname : this.fullname.substring(idx + 1);
}
@@ -369,12 +370,18 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
private static class DefaultParserProvider implements ISearchParserProvider {
public ItemFilter getFilter(String searchText) {
final String pathPart = searchText.replaceAll("\\s+", "").toLowerCase();
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
Pattern pattern = null;
if (patternMode != 3) {
searchText = searchText.replaceAll("\\s+", "").toLowerCase();
} else {
pattern = SearchField.getPattern(searchText, patternMode);
}
final AnyMultiItemFilter filter = new AnyMultiItemFilter();
final Set<ItemStack> filteredItems = new HashSet<>();
for (SubsetTag tag : tags.values()) {
if (tag.filter != null && tag.path.contains(pathPart)) {
if (tag.filter != null && matches(tag.path, searchText, pattern)) {
filteredItems.addAll(tag.items);
filter.filters.add(tag.filter);
}
@@ -395,6 +402,14 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
public SearchMode getSearchMode() {
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search.subsetsSearchMode"));
}
private boolean matches(String name, String searchText, Pattern pattern) {
if (pattern != null) {
return pattern.matcher(name).find();
} else {
return name.contains(searchText);
}
}
}
/**
@@ -426,6 +441,17 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
}
public static void removeTag(String path) {
updateState.stop();
synchronized (tags) {
tags.keySet().removeIf(
parentpath -> parentpath.equals(path.toLowerCase())
|| parentpath.startsWith(path.toLowerCase() + "."));
updateHiddenItems();
}
}
public static boolean isHidden(ItemStack item) {
try {
if (hiddenReadLock.tryLock(5, TimeUnit.SECONDS)) {
@@ -38,7 +38,11 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
public void tickKeyStates() {
if (Minecraft.getMinecraft().currentScreen != null) return;
if (KeyManager.keyStates.get("world.moboverlay").down) mobOverlay = (mobOverlay + 1) % 2;
if (KeyManager.keyStates.get("world.moboverlay").down) {
mobOverlay = (mobOverlay + 1) % 2;
NEIClientUtils
.printChatMessage(new ChatComponentText(NEIClientUtils.translate("chat.moboverlay." + mobOverlay)));
}
if (KeyManager.keyStates.get("world.chunkoverlay").down) {
chunkOverlay = (chunkOverlay + 1) % (NEIModContainer.isGT5Loaded() ? 4 : 3);
// 0-2 (or 3 with gt)
@@ -73,9 +77,11 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
}
World world = entity.worldObj;
int worldHeight = world.getHeight();
int x1 = (int) entity.posX;
int z1 = (int) entity.posZ;
int y1 = (int) MathHelper.clip(entity.posY, 16, world.getHeight() - 16);
int y1 = (int) MathHelper.clip(entity.posY, 16, worldHeight - 16);
for (int i = 0; i <= 32; i++) {
int x = x1 - 16 + i;
@@ -94,6 +100,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
for (int k = 0; k <= 32; k++) {
int y = y1 - 16 + k;
if (y >= worldHeight) {
mobSpawnCache[bufIndex + k] = -1;
continue;
}
if (y > maxHeight) {
mobSpawnCache[bufIndex + k] = -1;
break;
@@ -177,6 +189,10 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
private static final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0);
private static byte getSpawnMode(Chunk chunk, int x, int y, int z) {
if (y >= chunk.worldObj.getHeight()) {
return 0;
}
if (chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15) >= 8
|| !SpawnerAnimals.canCreatureTypeSpawnAtLocation(EnumCreatureType.monster, chunk.worldObj, x, y, z)) {
return 0;
@@ -1,18 +1,14 @@
package codechicken.nei.api;
import java.util.List;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.IRecipeHandler;
public interface IRecipeFilter {
public static interface IRecipeFilterProvider {
public IRecipeFilter getFilter();
public IRecipeFilter getRecipeFilter();
}
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others);
public boolean matches(IRecipeHandler handler, int recipeIndex);
}
@@ -65,6 +65,7 @@ import codechicken.nei.search.IdentifierFilter;
import codechicken.nei.search.ModNameFilter;
import codechicken.nei.search.OreDictionaryFilter;
import codechicken.nei.search.TooltipFilter;
import codechicken.nei.util.ItemUntranslator;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.registry.GameRegistry;
@@ -137,6 +138,10 @@ public class ItemInfo {
addSearchProviders();
RecipeItemInputHandler.load();
PresetsList.load();
if (NEIClientConfig.enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
}
}
private static void addSearchProviders() {
@@ -19,15 +19,18 @@ import codechicken.nei.BookmarkContainerInfo;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemQuantityField;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.SearchField;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkGroup;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiFavoriteButton;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.Recipe;
@@ -112,6 +115,10 @@ public abstract class ShortcutInputHandler {
return GuiUsageRecipe.openRecipeGui("item", stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.favorite")) {
return saveFavoriteTree(stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.bookmark")) {
return saveRecipeInBookmark(stackover, NEIClientUtils.shiftKey(), NEIClientUtils.controlKey());
}
@@ -247,6 +254,25 @@ public abstract class ShortcutInputHandler {
return false;
}
private static boolean saveFavoriteTree(ItemStack stackover) {
final Point mouse = GuiDraw.getMousePosition();
if (ItemPanels.itemPanel.contains(mouse.x, mouse.y)
|| ItemPanels.itemPanel.historyPanel.contains(mouse.x, mouse.y)) {
final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(FavoriteRecipes.getFavorite(stackover));
if (handlerRef != null) {
final GuiFavoriteButton button = new GuiFavoriteButton(handlerRef, 0, 0);
button.saveRecipeInBookmark();
return true;
}
return true;
}
return false;
}
private static boolean saveRecipeInBookmark(ItemStack stackover, boolean saveIngredients, boolean saveStackSize) {
final Point mousePos = GuiDraw.getMousePosition();
@@ -262,7 +288,8 @@ public abstract class ShortcutInputHandler {
recipe.getIngredients());
singleRecipe.setCustomRecipeId(recipe.getRecipeId());
ItemPanels.bookmarkPanel.addRecipe(singleRecipe, BookmarkGrid.DEFAULT_GROUP_ID);
ItemPanels.bookmarkPanel
.addRecipe(singleRecipe, saveStackSize ? 1 : 0, BookmarkGrid.DEFAULT_GROUP_ID);
}
} else {
@@ -297,25 +324,38 @@ public abstract class ShortcutInputHandler {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
Recipe recipe = null;
if (useFavorites && (ItemPanels.itemPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey))) {
recipe = Recipe.of(FavoriteRecipes.getFavorite(stackover));
if (recipe != null) {
recipe.getIngredients().stream().forEach(ingr -> {
final List<ItemStack> permutations = ingr.getPermutations();
for (int index = 0; index < permutations.size(); index++) {
if (FavoriteRecipes.contains(permutations.get(index))) {
ingr.setActiveIndex(index);
break;
}
}
});
if (useFavorites) {
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
}
} else if (gui instanceof GuiRecipe guiRecipe) {
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot != null) {
final RecipeId recipeId = itemSlot.getRecipeId();
if (recipeId != null && (recipe = Recipe.of(recipeId)) != null) {
recipe.getIngredients().stream().forEach(ingr -> {
final List<ItemStack> permutations = ingr.getPermutations();
for (int index = 0; index < permutations.size(); index++) {
if (FavoriteRecipes.contains(permutations.get(index))) {
ingr.setActiveIndex(index);
break;
}
}
});
}
}
}
if (gui instanceof GuiRecipe guiRecipe) {
recipe = guiRecipe.getFocusedRecipe();
}
@@ -378,15 +418,56 @@ public abstract class ShortcutInputHandler {
final RecipeId recipeId = getHotkeyRecipeId(gui, mousex, mousey, stack, slot);
if (slot != null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
NEIClientUtils.translate(slot.isIngredient() ? "bookmark.remove_item" : "bookmark.remove_recipe"));
final BookmarkGroup group = slot.getGroup();
if (slot.isIngredient()) {
if (group.crafting != null && group.collapsed) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
}
NEIClientUtils.translate("bookmark.group.remove_recipe"));
} else if (slot.getRecipeId() == null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
NEIClientUtils.translate("bookmark.remove_item"));
} else if (group.crafting != null && group.crafting.recipeRelations.entrySet().stream().anyMatch(
entry -> entry.getKey().equals(slot.getRecipeId())
|| entry.getValue().contains(slot.getRecipeId()))) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
} else {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
if (slot.isIngredient()) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
NEIClientUtils.translate("bookmark.remove_item"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
} else {
final BookmarkItem item = slot.getBookmarkItem();
if (ItemPanels.bookmarkPanel.getGrid().createChainItems(groupId).values().stream()
.noneMatch(
m -> !m.isIngredient && !m.equals(item) && item.equalsRecipe(m))) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
NEIClientUtils.translate("bookmark.remove_recipe"));
} else {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
NEIClientUtils.translate("bookmark.remove_item"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
}
}
}
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
hotkeys.put(
@@ -407,11 +488,22 @@ public abstract class ShortcutInputHandler {
}
if (recipeId != null) {
hotkeys.put(
NEIClientConfig
.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.add_item_with_recipe_and_count"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.add_item_with_recipe"));
}
if ((ItemPanels.itemPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) && FavoriteRecipes.contains(stack)) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.favorite"),
NEIClientUtils.translate("recipe.favorite.bookmark_recipe"));
}
}
hotkeys.put(NEIClientConfig.getKeyName("gui.recipe"), NEIClientUtils.translate("itempanel.open_crafting"));
@@ -508,17 +600,29 @@ public abstract class ShortcutInputHandler {
if (slot != null) {
return slot.isIngredient() ? null : slot.getRecipeId();
} else if (ItemPanels.itemPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) {
return FavoriteRecipes.getFavorite(stack);
} else
if (gui instanceof GuiRecipe guiRecipe) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
}
if (focusedRecipe != null) {
return focusedRecipe.getRecipeId();
}
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot != null) {
return itemSlot.getRecipeId();
}
if (gui instanceof GuiRecipe guiRecipe) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
if (focusedRecipe != null) {
return focusedRecipe.getRecipeId();
}
}
return null;
}
@@ -866,7 +866,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
onItemsChanged();
}
public void addRecipe(Recipe recipe, int groupId) {
public void addRecipe(Recipe recipe, int multiplier, int groupId) {
final RecipeId recipeId = recipe.getRecipeId();
final List<ItemStack> results = recipe.getResults().stream().map(res -> res.getItemStack())
.collect(Collectors.toList());
@@ -874,15 +874,19 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
.collect(Collectors.toList());
for (ItemStack stack : ItemStackAmount.of(results).values()) {
this.addItem(BookmarkItem.of(groupId, stack, StackInfo.getAmount(stack), recipeId, false), true);
long factor = StackInfo.getAmount(stack);
this.addItem(
BookmarkItem.of(groupId, StackInfo.withAmount(stack, factor * multiplier), factor, recipeId, false),
true);
}
for (ItemStack stack : ItemStackAmount.of(ingredients).values()) {
long factor = StackInfo.getAmount(stack);
this.addItem(
BookmarkItem.of(
groupId,
stack,
StackInfo.getAmount(stack),
StackInfo.withAmount(stack, factor * multiplier),
factor,
recipeId,
true,
BookmarkItem.generatePermutations(stack, recipe)),
@@ -904,7 +908,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (item.recipeId != null && removeFullRecipe) {
removeRecipe(item.recipeId, item.groupId);
} else {
} else if (itemIndex >= 0) {
this.bookmarkItems.remove(itemIndex);
onItemsChanged();
}
@@ -1116,16 +1120,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (item.factor > 0
&& outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) {
final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE);
final boolean isOutputRecipe = group.crafting != null
&& !group.crafting.recipeInMiddle.contains(item.recipeId);
final boolean recipeInMiddle = group.crafting != null
&& group.crafting.recipeInMiddle.contains(item.recipeId);
recipeMultipliers.put(
item.recipeId,
Math.min(
shiftMultiplier(
Math.max(isOutputRecipe ? 1 : 0, item.getMultiplier()),
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
shift,
isOutputRecipe ? 1 : 0),
recipeInMiddle ? 1 : 0),
multiplier));
items.add(item);
}
@@ -1148,16 +1152,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
} else if (targetItem.recipeId != null) {
final RecipeId recipeId = this.gridGenerator.itemToRecipe
.getOrDefault(targetItemIndex, targetItem.recipeId);
final boolean isOutputRecipe = group.crafting != null && !group.crafting.recipeInMiddle.contains(recipeId);
final boolean recipeInMiddle = group.crafting != null && group.crafting.recipeInMiddle.contains(recipeId);
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) {
multiplier = Math.min(
shiftMultiplier(
Math.max(isOutputRecipe ? 1 : 0, item.getMultiplier()),
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
shift,
isOutputRecipe ? 1 : 0),
recipeInMiddle ? 1 : 0),
multiplier);
}
}
@@ -117,7 +117,7 @@ public class BookmarkGridGenerator {
this.itemToSlot.put(collapsedItemIndex, absoluteSlotIndex);
this.caclulatedItems.put(collapsedItemIndex, getItem(group, collapsedItemIndex));
if (!(shadows = getShadows(group, item.recipeId)).isEmpty()) {
if (item.recipeId != null && !(shadows = getShadows(group, item.recipeId)).isEmpty()) {
borderGrid.put(absoluteSlotIndex, "recipe:" + shadows.get(0));
}
}
@@ -105,6 +105,7 @@ public class BookmarksGridSlot extends ItemsGridSlot {
return this.bookmarkItem;
}
@Override
public RecipeId getRecipeId() {
return this.bookmarkItem.recipeId;
}
@@ -179,9 +179,10 @@ public class RecipeChainDetails {
if (itemIndex >= 0 && this.calculatedItems.containsKey(itemIndex)) {
final BookmarkItem item = entry.getValue();
if (this.recipeInMiddle.contains(item.recipeId)) {
this.calculatedItems.get(itemIndex).setRealAmount(item.amount);
this.calculatedItems.get(itemIndex)
.setRealAmount(Math.max(0, item.factor * (item.getMultiplier() - 1)));
} else {
this.calculatedItems.get(itemIndex).setRealAmount(Math.max(item.factor, item.amount));
this.calculatedItems.get(itemIndex).setRealAmount(item.amount);
}
}
}
@@ -0,0 +1,78 @@
package codechicken.nei.commands;
import java.io.File;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.ItemList;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.util.ItemUntranslator;
public class CommandUntranslator extends CommandBase {
@Override
public String getCommandName() {
return "untranslator";
}
@Override
public String getCommandUsage(ICommandSender sender) {
return "/untranslator dump OR /untranslator clear";
}
@Override
public int getRequiredPermissionLevel() {
return 0;
}
@Override
public void processCommand(ICommandSender sender, String[] args) {
final String command = args.length == 0 ? null : args[0];
if ("dump".equals(command)) {
if (ItemList.items.isEmpty()) {
sendChatErrorMessage(sender, "nei.chat.untranslator.dump.error");
return;
} else {
sendChatInfoMessage(sender, "nei.chat.untranslator.dump.start");
ItemUntranslator.getInstance().generateUntranslatedNames();
sendChatInfoMessage(sender, "nei.chat.untranslator.dump.finish");
if (NEIClientConfig.enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
}
}
} else if ("clear".equals(command)) {
final File file = new File(NEIClientConfig.configDir, "untranslator.cfg");
if (file.exists() && file.delete()) {
sendChatInfoMessage(sender, "nei.chat.untranslator.clear");
if (NEIClientConfig.enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
}
} else {
sendChatErrorMessage(sender, "nei.chat.untranslator.clear.error");
}
} else {
sendChatErrorMessage(sender, getCommandUsage(sender));
}
}
private static void sendChatInfoMessage(ICommandSender sender, String translationKey, Object... args) {
sender.addChatMessage(
new ChatComponentTranslation(translationKey, args)
.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.AQUA)));
}
private static void sendChatErrorMessage(ICommandSender sender, String translationKey, Object... args) {
sender.addChatMessage(
new ChatComponentTranslation(translationKey, args)
.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED)));
}
}
@@ -160,8 +160,8 @@ public class GuiPanelSettings extends GuiScreenWidget {
}
protected void drawItems() {
final int columns = w / ItemsGrid.SLOT_SIZE;
final int rows = (h - (PANEL_SIZE + 2) * 2) / ItemsGrid.SLOT_SIZE;
final int columns = (w - 2) / ItemsGrid.SLOT_SIZE;
final int rows = (h - (PANEL_SIZE + 2) * 2 - 2) / ItemsGrid.SLOT_SIZE;
final int paddingLeft = (w % ItemsGrid.SLOT_SIZE) / 2;
final List<ItemStack> items = ItemPanels.itemPanel.getItems();
if (items.isEmpty()) return;
@@ -207,6 +207,10 @@ public class GuiPresetList extends GuiOptionPane {
modeName = EnumChatFormatting.GOLD + NEIClientUtils.translate("presets.mode.hide.char")
+ EnumChatFormatting.RESET;
break;
case SUBSET:
modeName = EnumChatFormatting.DARK_PURPLE + NEIClientUtils.translate("presets.mode.subset.char")
+ EnumChatFormatting.RESET;
break;
case REMOVE:
modeName = EnumChatFormatting.RED + NEIClientUtils.translate("presets.mode.remove.char")
+ EnumChatFormatting.RESET;
@@ -86,6 +86,7 @@ public class LeftPanel extends GuiWidget {
protected Label modeLabel = new Label(NEIClientUtils.translate("presets.mode"), false);
protected CheckboxButton modeHide;
protected CheckboxButton modeSubset;
protected CheckboxButton modeRemove;
protected CheckboxButton modeGroup;
@@ -114,6 +115,19 @@ public class LeftPanel extends GuiWidget {
}
};
modeSubset = new CheckboxButton(NEIClientUtils.translate("presets.mode.subset")) {
@Override
public boolean isChecked() {
return preset.mode == PresetMode.SUBSET;
}
@Override
protected void onChange() {
preset.mode = this.checked ? PresetMode.SUBSET : null;
}
};
modeRemove = new CheckboxButton(NEIClientUtils.translate("presets.mode.remove")) {
@Override
@@ -164,6 +178,10 @@ public class LeftPanel extends GuiWidget {
modeHide.handleClick(x, y, button);
}
if (modeSubset.contains(x, y)) {
modeSubset.handleClick(x, y, button);
}
if (modeRemove.contains(x, y)) {
modeRemove.handleClick(x, y, button);
}
@@ -248,7 +266,7 @@ public class LeftPanel extends GuiWidget {
@Override
public void update() {
int CHECKBOX_WIDTH = (this.width - 12) / 3;
int CHECKBOX_WIDTH = (this.width - 12) / 4;
nameLabel.x = this.x + 2;
nameLabel.y = this.y + 4;
nameLabel.w = this.width;
@@ -269,12 +287,13 @@ public class LeftPanel extends GuiWidget {
modeLabel.w = this.width;
modeLabel.h = 10;
modeHide.w = modeRemove.w = modeGroup.w = CHECKBOX_WIDTH;
modeHide.h = modeRemove.h = modeGroup.h = INPUT_HEIGHT;
modeHide.y = modeRemove.y = modeGroup.y = modeLabel.y + modeLabel.h;
modeHide.w = modeSubset.w = modeRemove.w = modeGroup.w = CHECKBOX_WIDTH;
modeHide.h = modeSubset.h = modeRemove.h = modeGroup.h = INPUT_HEIGHT;
modeHide.y = modeSubset.y = modeRemove.y = modeGroup.y = modeLabel.y + modeLabel.h;
modeHide.x = this.x + 1;
modeRemove.x = modeHide.x + modeHide.w + 6;
modeSubset.x = modeHide.x + modeHide.w + 6;
modeRemove.x = modeSubset.x + modeSubset.w + 6;
modeGroup.x = modeRemove.x + modeRemove.w + 6;
grid.setGridSize(
@@ -292,6 +311,7 @@ public class LeftPanel extends GuiWidget {
modeLabel.draw(mousex, mousey);
modeHide.draw(mousex, mousey);
modeSubset.draw(mousex, mousey);
modeRemove.draw(mousex, mousey);
modeGroup.draw(mousex, mousey);
@@ -326,6 +346,8 @@ public class LeftPanel extends GuiWidget {
} else if (modeHide.contains(mousex, mousey)) {
tooltip.add(NEIClientUtils.translate("presets.mode.hide.tip"));
} else if (modeSubset.contains(mousex, mousey)) {
tooltip.add(NEIClientUtils.translate("presets.mode.subset.tip"));
} else if (modeRemove.contains(mousex, mousey)) {
tooltip.add(NEIClientUtils.translate("presets.mode.remove.tip"));
} else if (modeGroup.contains(mousex, mousey)) {
@@ -0,0 +1,40 @@
package codechicken.nei.filter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class AllMultiRecipeFilter implements IRecipeFilter {
public final List<IRecipeFilter> filters;
public AllMultiRecipeFilter(List<IRecipeFilter> filters) {
this.filters = filters;
}
public AllMultiRecipeFilter(IRecipeFilter filters) {
this(Arrays.asList(filters));
}
public AllMultiRecipeFilter() {
this(new ArrayList<>());
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
for (IRecipeFilter filter : filters) {
try {
if (filter != null && !filter.matches(handler, recipeIndex)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
}
}
return true;
}
}
@@ -0,0 +1,44 @@
package codechicken.nei.filter;
import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class AllOthersRecipeFilter implements IRecipeFilter {
private final ItemFilter filter;
public AllOthersRecipeFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
if (!match(pStack)) {
return false;
}
}
return true;
}
protected boolean match(PositionedStack pStack) {
if (pStack == null) {
return false;
}
for (ItemStack stack : pStack.items) {
if (filter.matches(stack)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,40 @@
package codechicken.nei.filter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class AnyMultiRecipeFilter implements IRecipeFilter {
public final List<IRecipeFilter> filters;
public AnyMultiRecipeFilter(List<IRecipeFilter> filters) {
this.filters = filters;
}
public AnyMultiRecipeFilter(IRecipeFilter filters) {
this(Arrays.asList(filters));
}
public AnyMultiRecipeFilter() {
this(new ArrayList<>());
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
for (IRecipeFilter filter : filters) {
try {
if (filter != null && filter.matches(handler, recipeIndex)) return true;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
}
}
return false;
}
}
@@ -0,0 +1,44 @@
package codechicken.nei.filter;
import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class AnyOthersRecipeFilter implements IRecipeFilter {
private final ItemFilter filter;
public AnyOthersRecipeFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
if (match(pStack)) {
return true;
}
}
return false;
}
protected boolean match(PositionedStack pStack) {
if (pStack == null) {
return false;
}
for (ItemStack stack : pStack.items) {
if (filter.matches(stack)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,89 @@
package codechicken.nei.filter;
import java.util.List;
import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class RecipeFilter implements IRecipeFilter {
public enum FilterContext {
ANY,
INPUT,
OUTPUT;
public static FilterContext fromChar(char c) {
switch (c) {
case '<':
return INPUT;
case '>':
return OUTPUT;
default:
return ANY;
}
}
}
private final ItemFilter filter;
private final FilterContext context;
private final boolean anyMatch;
public RecipeFilter(FilterContext context, boolean anyMatch, ItemFilter filter) {
this.context = context;
this.anyMatch = anyMatch;
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
if ((this.context == FilterContext.ANY || this.context == FilterContext.INPUT)
&& matchPositionedStack(handler.getIngredientStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) {
final PositionedStack result = handler.getResultStack(recipeIndex);
if (result != null && matchPositionedStack(result) == this.anyMatch) {
return this.anyMatch;
}
if (matchPositionedStack(handler.getOtherStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
}
return !this.anyMatch;
}
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
for (PositionedStack pStack : items) {
if (matchPositionedStack(pStack) == dir) {
return true;
}
}
return false;
}
private boolean matchPositionedStack(PositionedStack pStack) {
if (pStack == null) return false;
for (ItemStack stack : pStack.items) {
if (this.filter.matches(stack)) {
return true;
}
}
return false;
}
}
@@ -49,6 +49,7 @@ import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.NEIModContainer;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ItemUntranslator;
import codechicken.nei.util.ReadableNumberConverter;
import codechicken.nei.util.RenderTooltipEventHelper;
@@ -60,6 +61,10 @@ public class GuiContainerManager {
public void onResourceManagerReload(IResourceManager p_110549_1_) {
renderingErrorItems.clear();
ItemList.loadItems.restart();
if (NEIClientConfig.enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
}
}
}
@@ -590,9 +595,7 @@ public class GuiContainerManager {
}
}
if (!tooltip.isEmpty()) tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); // add space after 'title'
if (showTooltip) {
if (showTooltip && NEIClientConfig.getBooleanSetting("inventory.showHotkeys")) {
List<String> hotkeystips = collectHotkeyTips(mousex, mousey);
if (!hotkeystips.isEmpty()) {
@@ -605,6 +608,19 @@ public class GuiContainerManager {
}
}
if (showTooltip && stack != null && !tooltip.isEmpty()) {
final String secondDisplayName = ItemUntranslator.getInstance().getItemStackDisplayName(stack);
if (!secondDisplayName.isEmpty()) {
tooltip.add(1, EnumChatFormatting.DARK_GRAY + secondDisplayName + GuiDraw.TOOLTIP_LINESPACE);
} else {
tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE);
}
} else if (!tooltip.isEmpty()) {
tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE);
}
if (NEIModContainer.isGTNHLibLoaded() && !tooltip.isEmpty()) {
if (RenderTooltipEventHelper.post(stack, this.window, mousex, mousey, font)) {
return;
@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
import net.minecraft.client.gui.inventory.GuiBrewingStand;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemPotion;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.Potion;
@@ -36,6 +37,13 @@ public class BrewingRecipeHandler extends TemplateRecipeHandler {
ingredient = new PositionedStack(ingred, 74, 6);
result = new PositionedStack(new ItemStack(potionitem, 1, resultDamage), 97, 35);
}
// Arbitrary precursor/result stacks (for Lingering)
public BrewingRecipe(ItemStack ingred, ItemStack precursor, ItemStack resultStack) {
precursorPotion = new PositionedStack(precursor, 51, 35);
ingredient = new PositionedStack(ingred, 74, 6);
result = new PositionedStack(resultStack, 97, 35);
}
}
public class CachedBrewingRecipe extends CachedRecipe {
@@ -63,6 +71,9 @@ public class BrewingRecipeHandler extends TemplateRecipeHandler {
public static final ItemStackSet ingredients = new ItemStackSet();
public static final HashSet<BrewingRecipe> apotions = new HashSet<>();
private static ItemStack DRAGONS_BREATH = null;
private static Item LINGERING_POTION_ITEM = null;
@Override
public void loadTransferRects() {
transferRects.add(new RecipeTransferRect(new Rectangle(58, 3, 14, 30), "brewing"));
@@ -90,23 +101,38 @@ public class BrewingRecipeHandler extends TemplateRecipeHandler {
@Override
public void loadCraftingRecipes(ItemStack result) {
if (result.getItem() != potionitem) return;
if (result == null) return;
// Allow lingering potion items as output
Item out = result.getItem();
boolean isPotion = out == potionitem;
boolean isLingering = (LINGERING_POTION_ITEM != null && out == LINGERING_POTION_ITEM);
if (!isPotion && !isLingering) return;
int damage = result.getItemDamage();
// Note: Not safe as written for parallelStream
apotions.stream().filter(recipe -> recipe.result.item.getItemDamage() == damage).map(CachedBrewingRecipe::new)
.collect(Collectors.toCollection(() -> arecipes));
apotions.stream()
.filter(recipe -> recipe.result.item.getItem() == out && recipe.result.item.getItemDamage() == damage)
.map(CachedBrewingRecipe::new).collect(Collectors.toCollection(() -> arecipes));
}
@Override
public void loadUsageRecipes(ItemStack ingredient) {
if (ingredient.getItem() != potionitem && !ingredients.contains(ingredient)) return;
if (ingredient == null) return;
// allow lingering potions to pass the guard & keeps og potion behavior
boolean passIt = ingredient.getItem() == potionitem
|| (LINGERING_POTION_ITEM != null && ingredient.getItem() == LINGERING_POTION_ITEM)
|| ingredients.contains(ingredient);
if (!passIt) return;
// Note: Not safe as written for parallelStream
apotions.stream()
.filter(
recipe -> NEIServerUtils.areStacksSameType(recipe.ingredient.item, ingredient)
|| NEIServerUtils.areStacksSameType(recipe.precursorPotion.item, ingredient))
|| NEIServerUtils.areStacksSameType(recipe.precursorPotion.item, ingredient)
|| NEIServerUtils.areStacksSameType(recipe.result.item, ingredient))
.map(CachedBrewingRecipe::new).collect(Collectors.toCollection(() -> arecipes));
}
@@ -141,7 +167,7 @@ public class BrewingRecipeHandler extends TemplateRecipeHandler {
List<?> baseMods = potionitem.getEffects(basePotion);
List<?> newMods = potionitem.getEffects(result); // compare ID's
if (basePotion > 0 && baseMods == newMods || // same modifers and not water->empty
if (basePotion > 0 && baseMods == newMods || // same modifiers and not water->empty
baseMods != null && (baseMods.equals(newMods) || newMods == null) || // modifiers different
// and doesn't lose
// modifiers
@@ -183,6 +209,42 @@ public class BrewingRecipeHandler extends TemplateRecipeHandler {
API.addSubset("Items.Potions.Positive", positivepots);
API.addSubset("Items.Potions.Negative", negativepots);
API.addSubset("Items.Potions.Neutral", neutralpots);
// EFR Potion Recipe Visibility - Will safely skip if EFR isn't loaded
tryResolveEFRItems();
if (DRAGONS_BREATH != null && LINGERING_POTION_ITEM != null) {
ingredients.add(DRAGONS_BREATH.copy());
// Iterate over snapshot to avoid concurrent modification when adding to apotions
ArrayList<BrewingRecipe> snapshot = new ArrayList<>(apotions);
for (BrewingRecipe bgr : snapshot) {
if (bgr == null || bgr.result == null || bgr.result.item == null) continue;
ItemStack splashOut = bgr.result.item;
if (splashOut.getItem() != potionitem) continue;
int meta = splashOut.getItemDamage();
if (!ItemPotion.isSplash(meta)) continue;
// Clear the splash bit (0x4000) and keep meta otherwise
int lingeringMeta = (meta & ~0x4000);
ItemStack precursorSplash = splashOut.copy();
ItemStack lingeringOut = new ItemStack(LINGERING_POTION_ITEM, 1, lingeringMeta);
apotions.add(new BrewingRecipe(DRAGONS_BREATH, precursorSplash, lingeringOut));
}
// Lingering items
final List<ItemStack> allLingering = apotions.stream()
.map(lpr -> lpr.result != null ? lpr.result.item : null)
.filter(lps -> lps != null && lps.getItem() == LINGERING_POTION_ITEM).collect(Collectors.toList());
if (!allLingering.isEmpty()) {
API.setItemListEntries(LINGERING_POTION_ITEM, allLingering);
API.addSubset("Items.Potions.Lingering", stack -> stack.getItem() == LINGERING_POTION_ITEM);
}
}
}
private static boolean levelModifierChanged(int basePotionID, int result) {
@@ -199,6 +261,16 @@ public class BrewingRecipeHandler extends TemplateRecipeHandler {
newPotions.add(result);
}
private static void tryResolveEFRItems() {
if (DRAGONS_BREATH == null) {
Item breath = (Item) Item.itemRegistry.getObject("etfuturum:dragon_breath");
DRAGONS_BREATH = (breath != null) ? new ItemStack(breath) : null;
}
if (LINGERING_POTION_ITEM == null) {
LINGERING_POTION_ITEM = (Item) Item.itemRegistry.getObject("etfuturum:lingering_potion");
}
}
@Override
public String getOverlayIdentifier() {
return "brewing";
@@ -15,6 +15,7 @@ import net.minecraft.item.ItemStack;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarksGridSlot;
@@ -52,6 +53,12 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
final GuiCraftingRecipe gui = new GuiCraftingRecipe(handlers, "recipeId".equals(outputId));
if (open) {
if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(outputId)
&& results[0] instanceof ItemStack stack) {
ItemPanels.itemPanel.historyPanel.addItem(stack);
}
mc.displayGuiScreen(gui);
}
@@ -117,17 +124,27 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
final Point mouseover = GuiDraw.getMousePosition();
final BookmarksGridSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
RecipeId recipeId = null;
if (panelSlot != null && !panelSlot.isIngredient()) {
recipeId = panelSlot.getRecipeId();
if (panelSlot != null) {
return panelSlot.isIngredient() || panelSlot.getRecipeId() == null ? FavoriteRecipes.getFavorite(stackover)
: panelSlot.getRecipeId();
}
if (recipeId == null) {
recipeId = FavoriteRecipes.getFavorite(stackover);
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseover.x, mouseover.y);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mouseover.x, mouseover.y);
}
return recipeId;
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mouseover.x, mouseover.y);
}
if (itemSlot != null) {
return itemSlot.getRecipeId();
}
return null;
}
private GuiCraftingRecipe(ArrayList<ICraftingHandler> handlers, boolean limitToOneRecipe) {
@@ -119,7 +119,7 @@ public class GuiFavoriteButton extends GuiRecipeButton {
public void lastKeyTyped(GuiRecipe<?> gui, char keyChar, int keyID) {
if (NEIClientConfig.isKeyHashDown("gui.bookmark") && NEIClientUtils.shiftKey()) {
ItemPanels.bookmarkPanel.addGroup(getRecipesTree(getRecipe()), BookmarkViewMode.TODO_LIST, true);
saveRecipeInBookmark();
}
}
@@ -180,6 +180,10 @@ public class GuiFavoriteButton extends GuiRecipeButton {
return true;
}
public void saveRecipeInBookmark() {
ItemPanels.bookmarkPanel.addGroup(getRecipesTree(getRecipe()), BookmarkViewMode.TODO_LIST, true);
}
private List<Recipe> getRecipesTree(Recipe mainRecipe) {
int depth = NEIClientConfig.getIntSetting("inventory.favorites.depth");
final ItemStackSet items = new ItemStackSet();
@@ -155,6 +155,10 @@ public class GuiOverlayButton extends GuiRecipeButton {
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("recipe.overlay.bookmarkRecipe"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("recipe.overlay.bookmarkRecipeAndCount"));
return hotkeys;
}
@@ -309,7 +313,8 @@ public class GuiOverlayButton extends GuiRecipeButton {
final Recipe recipe = getRecipe();
if (!ItemPanels.bookmarkPanel.removeRecipe(recipe.getRecipeId(), BookmarkGrid.DEFAULT_GROUP_ID)) {
ItemPanels.bookmarkPanel.addRecipe(recipe, BookmarkGrid.DEFAULT_GROUP_ID);
ItemPanels.bookmarkPanel
.addRecipe(recipe, NEIClientUtils.controlKey() ? 1 : 0, BookmarkGrid.DEFAULT_GROUP_ID);
}
}
@@ -43,6 +43,7 @@ import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.TaggedInventoryArea;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.filter.AllMultiRecipeFilter;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.guihook.IGuiClientSide;
@@ -118,86 +119,6 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private int yShift = 0;
public static class ItemRecipeFilter implements IRecipeFilter {
public ItemFilter filter;
public ItemRecipeFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
if (matchPositionedStack(ingredients)) {
return true;
}
if (matchPositionedStack(result)) {
return true;
}
if (matchPositionedStack(others)) {
return true;
}
return false;
}
private boolean matchPositionedStack(List<PositionedStack> items) {
for (PositionedStack pStack : items) {
if (matchPositionedStack(pStack)) {
return true;
}
}
return false;
}
private boolean matchPositionedStack(PositionedStack pStack) {
if (pStack == null) return false;
for (ItemStack stack : pStack.items) {
if (filter.matches(stack)) {
return true;
}
}
return false;
}
}
public static class AllMultiRecipeFilter implements IRecipeFilter {
public List<IRecipeFilter> filters;
public AllMultiRecipeFilter(List<IRecipeFilter> filters) {
this.filters = filters;
}
public AllMultiRecipeFilter(IRecipeFilter filters) {
this(Arrays.asList(filters));
}
public AllMultiRecipeFilter() {
this(new ArrayList<>());
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
for (IRecipeFilter filter : filters) {
try {
if (filter != null && !filter.matches(handler, ingredients, result, others)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
}
}
return true;
}
}
protected static final RestartableTask updateFilter = new RestartableTask("NEI Recipe Filtering") {
@Override
@@ -214,7 +135,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
searchHandler.setSearchIndices(null);
guiRecipe.changePage(0);
} else {
final IRecipeFilter filter = new ItemRecipeFilter(GuiRecipe.searchField.getFilter());
final IRecipeFilter filter = GuiRecipe.searchField.getRecipeFilter();
final List<Integer> filtered = searchHandler.getSearchResult(filter);
if (filtered == null) {
@@ -546,7 +467,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
synchronized (recipeFilterers) {
for (IRecipeFilterProvider p : recipeFilterers) {
IRecipeFilter filter = p.getFilter();
IRecipeFilter filter = p.getRecipeFilter();
if (filter != null) {
recipeFilter.filters.add(filter);
}
@@ -8,6 +8,7 @@ import java.util.Set;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import codechicken.nei.ItemPanels;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.recipe.Recipe.RecipeId;
@@ -34,6 +35,11 @@ public class GuiUsageRecipe extends GuiRecipe<IUsageHandler> {
final RecipeId recipeId = getCurrentRecipeId(mc.currentScreen);
final GuiUsageRecipe gui = new GuiUsageRecipe(handlers);
if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(inputId)
&& ingredients[0] instanceof ItemStack stack) {
ItemPanels.itemPanel.historyPanel.addItem(stack);
}
mc.displayGuiScreen(gui);
gui.openTargetRecipe(recipeId);
return true;
@@ -51,8 +57,8 @@ public class GuiUsageRecipe extends GuiRecipe<IUsageHandler> {
final RecipeHandlerQuery<IUsageHandler> recipeQuery = new RecipeHandlerQuery<>(
h -> getUsageOrCatalystHandler(h, inputId, ingredients),
usagehandlers,
serialUsageHandlers,
GuiUsageRecipe.usagehandlers,
GuiUsageRecipe.serialUsageHandlers,
"Error while looking up usage recipe",
"inputId: " + inputId,
"ingredients: " + Arrays.toString(ingredients));
@@ -1,119 +0,0 @@
package codechicken.nei.recipe;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Container;
import net.minecraft.item.ItemStack;
import codechicken.nei.ItemPanels;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IOverlayHandler;
import codechicken.nei.api.IRecipeOverlayRenderer;
class ItemsHistoryHandler implements ICraftingHandler, IUsageHandler {
@Override
public ICraftingHandler getRecipeHandler(String outputId, Object... results) {
if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(outputId)
&& results[0] instanceof ItemStack stack) {
ItemPanels.itemPanel.historyPanel.addItem(stack);
}
return null;
}
@Override
public IUsageHandler getUsageHandler(String inputId, Object... ingredients) {
if (NEIClientConfig.showHistoryPanelWidget() && "item".equals(inputId)
&& ingredients[0] instanceof ItemStack stack) {
ItemPanels.itemPanel.historyPanel.addItem(stack);
}
return null;
}
@Override
public void onUpdate() {
}
@Override
public boolean hasOverlay(GuiContainer gui, Container container, int recipe) {
return false;
}
@Override
public IRecipeOverlayRenderer getOverlayRenderer(GuiContainer gui, int recipe) {
return null;
}
@Override
public IOverlayHandler getOverlayHandler(GuiContainer gui, int recipe) {
return null;
}
@Override
public int recipiesPerPage() {
return 0;
}
@Override
public List<String> handleTooltip(GuiRecipe<?> gui, List<String> currenttip, int recipe) {
return null;
}
@Override
public List<String> handleItemTooltip(GuiRecipe<?> gui, ItemStack stack, List<String> currenttip, int recipe) {
return null;
}
@Override
public boolean keyTyped(GuiRecipe<?> gui, char keyChar, int keyCode, int recipe) {
return false;
}
@Override
public boolean mouseClicked(GuiRecipe<?> gui, int button, int recipe) {
return true;
}
@Override
public String getRecipeName() {
return "Items History";
}
@Override
public int numRecipes() {
return 0;
}
@Override
public void drawBackground(int recipe) {
}
@Override
public void drawForeground(int recipe) {
}
@Override
public List<PositionedStack> getIngredientStacks(int recipe) {
return null;
}
@Override
public List<PositionedStack> getOtherStacks(int recipeType) {
return null;
}
@Override
public PositionedStack getResultStack(int recipe) {
return null;
}
}
@@ -62,7 +62,13 @@ public class RecipeHandlerQuery<T extends IRecipeHandler> {
private ArrayList<T> getRecipeHandlersParallel() throws InterruptedException, ExecutionException {
// Pre-find the fuels so we're not fighting over it
FuelRecipeHandler.findFuelsOnceParallel();
if (NEIClientConfig.getBooleanSetting("findFuelsParallel")) {
FuelRecipeHandler.findFuelsOnceParallel();
} else {
FuelRecipeHandler.findFuelsOnce();
}
ArrayList<T> handlers = getSerialHandlersWithRecipes();
handlers.addAll(getHandlersWithRecipes());
handlers.sort(NEIClientConfig.HANDLER_COMPARATOR);
@@ -30,7 +30,7 @@ public class RecipeHandlerRef {
public static RecipeHandlerRef of(RecipeId recipeId) {
if (recipeId.getResult() != null && !recipeId.getIngredients().isEmpty()) {
if (recipeId != null && recipeId.getResult() != null && !recipeId.getIngredients().isEmpty()) {
return recipeRefCache.computeIfAbsent(recipeId, ri -> {
final List<ICraftingHandler> handlers = GuiCraftingRecipe
.getCraftingHandlers("recipeId", recipeId.getResult(), recipeId);
@@ -1,6 +1,8 @@
package codechicken.nei.recipe;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import net.minecraft.client.gui.inventory.GuiBrewingStand;
import net.minecraft.client.gui.inventory.GuiContainer;
@@ -79,6 +81,12 @@ public class RecipeInfo {
return overlayMap.get(new OverlayKey(gui.getClass(), ident));
}
public static List<String> getOverlayHandlerIdents(GuiContainer gui) {
final Class<? extends GuiContainer> classz = gui.getClass();
return overlayMap.keySet().stream().filter(key -> key.ident != null && key.guiClass.equals(classz))
.map(key -> key.ident).collect(Collectors.toList());
}
public static IStackPositioner getStackPositioner(GuiContainer gui, String ident) {
return positionerMap.get(new OverlayKey(gui.getClass(), ident));
}
@@ -116,7 +124,5 @@ public class RecipeInfo {
API.registerRecipeHandler(new ProfilerRecipeHandler(true));
API.registerUsageHandler(new ProfilerRecipeHandler(false));
API.registerRecipeHandler(new ItemsHistoryHandler());
API.registerUsageHandler(new ItemsHistoryHandler());
}
}
@@ -11,6 +11,7 @@ import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.api.ShortcutInputHandler;
import codechicken.nei.bookmark.BookmarksGridSlot;
@@ -100,9 +101,20 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
return null;
}
if (ItemPanels.itemPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) {
return NEIClientConfig.showRecipeTooltipInPanel() ? FavoriteRecipes.getFavorite(itemstack) : null;
if (NEIClientConfig.showRecipeTooltipInPanel()) {
ItemsGridSlot panelSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
if (panelSlot == null) {
panelSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
}
if (panelSlot == null) {
panelSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
}
if (panelSlot != null) {
return panelSlot.getRecipeId();
}
}
if (gui instanceof GuiRecipe<?>guiRecipe
@@ -37,12 +37,8 @@ class SearchRecipeHandler<H extends IRecipeHandler> {
}
}
protected static boolean mathRecipe(IRecipeHandler handler, int recipe, IRecipeFilter filter) {
return filter.matches(
handler,
handler.getIngredientStacks(recipe),
handler.getResultStack(recipe),
handler.getOtherStacks(recipe));
protected static boolean mathRecipe(IRecipeHandler handler, int recipeIndex, IRecipeFilter filter) {
return filter.matches(handler, recipeIndex);
}
public boolean searchingAvailable() {
@@ -86,13 +86,27 @@ public class ShapedRecipeHandler extends TemplateRecipeHandler {
if (outputId.equals("crafting") && getClass() == ShapedRecipeHandler.class) {
for (IRecipe irecipe : (List<IRecipe>) CraftingManager.getInstance().getRecipeList()) {
CachedShapedRecipe recipe = null;
if (irecipe instanceof ShapedRecipes) recipe = new CachedShapedRecipe((ShapedRecipes) irecipe);
else if (irecipe instanceof ShapedOreRecipe) recipe = forgeShapedRecipe((ShapedOreRecipe) irecipe);
if (recipe == null) continue;
if (irecipe instanceof ShapedRecipes shaped) {
recipe = new CachedShapedRecipe(shaped);
} else if (irecipe instanceof ShapedOreRecipe shaped) {
recipe = forgeShapedRecipe(shaped);
}
if (recipe != null) {
recipe.computeVisuals();
arecipes.add(recipe);
}
}
} else if (outputId.equals("crafting2x2") && getClass() == ShapedRecipeHandler.class) {
for (IRecipe irecipe : (List<IRecipe>) CraftingManager.getInstance().getRecipeList()) {
if (irecipe instanceof ShapedRecipes shaped && shaped.recipeWidth <= 2 && shaped.recipeHeight <= 2) {
CachedShapedRecipe recipe = new CachedShapedRecipe(shaped);
recipe.computeVisuals();
arecipes.add(recipe);
}
recipe.computeVisuals();
arecipes.add(recipe);
}
} else {
super.loadCraftingRecipes(outputId, results);
@@ -86,13 +86,31 @@ public class ShapelessRecipeHandler extends ShapedRecipeHandler {
List<IRecipe> allrecipes = CraftingManager.getInstance().getRecipeList();
for (IRecipe irecipe : allrecipes) {
CachedShapelessRecipe recipe = null;
if (irecipe instanceof ShapelessRecipes) recipe = shapelessRecipe((ShapelessRecipes) irecipe);
else if (irecipe instanceof ShapelessOreRecipe)
recipe = forgeShapelessRecipe((ShapelessOreRecipe) irecipe);
if (recipe == null) continue;
if (irecipe instanceof ShapelessRecipes shapeless) {
recipe = shapelessRecipe(shapeless);
} else if (irecipe instanceof ShapelessOreRecipe shapeless) {
recipe = forgeShapelessRecipe(shapeless);
}
arecipes.add(recipe);
if (recipe != null) {
arecipes.add(recipe);
}
}
} else if (outputId.equals("crafting2x2") && getClass() == ShapelessRecipeHandler.class) {
List<IRecipe> allrecipes = CraftingManager.getInstance().getRecipeList();
for (IRecipe irecipe : allrecipes) {
CachedShapelessRecipe recipe = null;
if (irecipe instanceof ShapelessRecipes shapeless && shapeless.getRecipeSize() <= 4) {
recipe = shapelessRecipe(shapeless);
} else if (irecipe instanceof ShapelessOreRecipe shapeless && shapeless.getRecipeSize() <= 4) {
recipe = forgeShapelessRecipe(shapeless);
}
if (recipe != null) {
arecipes.add(recipe);
}
}
} else {
super.loadCraftingRecipes(outputId, results);
@@ -15,6 +15,7 @@ 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;
@@ -44,6 +45,7 @@ import codechicken.nei.api.IStackPositioner;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerTooltipHandler;
import cpw.mods.fml.common.FMLLog;
/**
* A Template Recipe Handler! How about that. Because it was sooo hard, and more seriously required lots of copied code
@@ -107,10 +109,15 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
}
private static FurnaceRecipeHandler.FuelPair identifyFuel(final ItemStack itemStack) {
if (efuels.contains(itemStack.getItem())) return null;
final int burnTime = TileEntityFurnace.getItemBurnTime(itemStack);
if (burnTime <= 0) return null;
return new FurnaceRecipeHandler.FuelPair(itemStack.copy(), burnTime);
try {
if (efuels.contains(itemStack.getItem())) return null;
final int burnTime = TileEntityFurnace.getItemBurnTime(itemStack);
if (burnTime <= 0) return null;
return new FurnaceRecipeHandler.FuelPair(itemStack.copy(), burnTime);
} catch (Exception e) {
FMLLog.getLogger().error("NEI: Error identifying fuel for item " + itemStack.getItem());
throw e;
}
}
/**
@@ -711,11 +718,9 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
final ItemStack stack = items.get((items.size() - scroll + stackIndex) % items.size());
if (NEIClientConfig.useJEIStyledCycledIngredients()) {
for (PositionedStack pStack : getIngredientStacks(recipe)) {
if (pStack.containsWithNBT(stack)) {
pStack.setPermutationToRender(stack);
}
}
Stream.concat(getIngredientStacks(recipe).stream(), getOtherStacks(recipe).stream())
.filter(pStack -> pStack.containsWithNBT(stack))
.forEach(pStack -> pStack.setPermutationToRender(stack));
} else {
overStack.setPermutationToRender(stack);
}
@@ -738,14 +743,9 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
}
private PositionedStack getIngredientMouseOver(int mousex, int mousey, int recipe) {
for (PositionedStack pStack : getIngredientStacks(recipe)) {
if ((new Rectangle4i(pStack.relx, pStack.rely, 18, 18)).contains(mousex, mousey)) {
return pStack;
}
}
return null;
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) {
@@ -116,18 +116,18 @@ public class RecipeChainMath {
}
for (Map.Entry<RecipeId, Long> entry : multipliers.entrySet()) {
if (!this.outputRecipes.containsKey(entry.getKey()) && this.preferredItems.values().stream()
.noneMatch(resItem -> resItem.recipeId.equals(entry.getKey()))) {
this.outputRecipes.put(entry.getKey(), entry.getValue());
final RecipeId recipeId = entry.getKey();
final boolean isOutputRecipe = this.outputRecipes.containsKey(recipeId);
final boolean recipeInMiddle = this.preferredItems.values().stream()
.anyMatch(resItem -> resItem.recipeId.equals(recipeId));
if (!isOutputRecipe && !recipeInMiddle) {
this.outputRecipes.put(recipeId, entry.getValue());
} else if (isOutputRecipe && recipeInMiddle) {
this.outputRecipes.put(recipeId, Math.max(0, entry.getValue() - 1));
}
}
for (Map.Entry<RecipeId, Long> entry : this.outputRecipes.entrySet()) {
if (entry.getValue() == 0 && this.preferredItems.values().stream()
.noneMatch(prefItem -> prefItem.recipeId.equals(entry.getKey()))) {
entry.setValue(1L);
}
}
}
private void collectPreferredItems(RecipeId recipeId, Map<BookmarkItem, BookmarkItem> preferredItems,
@@ -25,7 +25,7 @@ public class DefaultStackStringifyHandler implements IStackStringifyHandler {
nbTag.setShort("Damage", (short) stack.getItemDamage());
if (stack.hasTagCompound() && !stack.getTagCompound().hasNoTags()) {
nbTag.setTag("tag", stack.getTagCompound());
nbTag.setTag("tag", stack.getTagCompound().copy());
}
return nbTag;
@@ -0,0 +1,21 @@
package codechicken.nei.search;
import codechicken.nei.SearchTokenParser;
public abstract class AbstractSearchExpressionVisitor<T> extends SearchExpressionParserBaseVisitor<T> {
protected final SearchTokenParser searchParser;
protected AbstractSearchExpressionVisitor(SearchTokenParser searchParser) {
this.searchParser = searchParser;
}
public SearchTokenParser getSearchParser() {
return searchParser;
}
public T fail() {
return defaultResult();
}
}
@@ -0,0 +1,194 @@
package codechicken.nei.search;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.SearchTokenParser;
import codechicken.nei.api.ItemFilter;
public class ItemFilterVisitor extends AbstractSearchExpressionVisitor<ItemFilter> {
private static final Pattern REGEX_ESCAPED_SPACE_PATTERN = Pattern.compile("([^\\\\](?:\\\\\\\\)+)?\\\\ ");
private static final Pattern PLAIN_TEXT_ESCAPED_PATTERN = Pattern.compile("\\\\(.)");
private static final Pattern ESCAPED_QUOTE_PATTERN = Pattern.compile("\\\\\"");
private static final Pattern ESCAPED_SPACE_PATTERN = Pattern.compile("\\\\ ");
public ItemFilterVisitor(SearchTokenParser searchParser) {
super(searchParser);
}
@Override
public ItemFilter visitChildren(RuleNode node) {
if (node instanceof ParserRuleContext) {
return visitChildren((ParserRuleContext) node, null);
} else {
return defaultResult();
}
}
// Should only be called for collecting ingredients permutations
@Override
public ItemFilter visitRecipeSearchExpression(SearchExpressionParser.RecipeSearchExpressionContext ctx) {
if (ctx.recipeClauseExpression() != null) {
final List<ItemFilter> anyFilters = new ArrayList<>();
final List<ItemFilter> allFilters = new ArrayList<>();
for (SearchExpressionParser.RecipeClauseExpressionContext clauseCtx : ctx.recipeClauseExpression()) {
addIngredientPermutationItemFilter(clauseCtx.searchExpression(), anyFilters, allFilters);
}
return constructFilter(anyFilters, allFilters);
}
return defaultResult();
}
@Override
public ItemFilter visitOrExpression(SearchExpressionParser.OrExpressionContext ctx) {
return visitChildren(ctx, AnyMultiItemFilter::new);
}
@Override
public ItemFilter visitSequenceExpression(SearchExpressionParser.SequenceExpressionContext ctx) {
return visitChildren(ctx, AllMultiItemFilter::new);
}
@Override
public ItemFilter visitNegateExpression(SearchExpressionParser.NegateExpressionContext ctx) {
return new NegatedItemFilter(visitChildren(ctx));
}
@Override
public ItemFilter visitPrefixedExpression(SearchExpressionParser.PrefixedExpressionContext ctx) {
if (ctx.token() != null) {
final String cleanText = getTokenCleanText(ctx.token());
final SearchTokenParser.ISearchParserProvider provider = searchParser.getProvider(ctx.token().prefix);
if (cleanText == null || provider == null) {
return defaultResult();
}
return provider.getFilter(cleanText);
}
return defaultResult();
}
@Override
public ItemFilter visitToken(SearchExpressionParser.TokenContext ctx) {
final String cleanText = getTokenCleanText(ctx);
if (cleanText != null) {
return getAlwaysProvidersFilter(cleanText);
}
return defaultResult();
}
@Override
protected ItemFilter defaultResult() {
return new NothingItemFilter();
}
private void addIngredientPermutationItemFilter(SearchExpressionParser.SearchExpressionContext ctx,
List<ItemFilter> anyFilters, List<ItemFilter> allFilters) {
// Only for ingredients or default
if (ctx.type == 0 || ctx.type == 3) {
if (ctx.allRecipe) {
allFilters.add(visit(ctx));
} else {
anyFilters.add(visit(ctx));
}
}
}
private ItemFilter constructFilter(List<ItemFilter> anyFilters, List<ItemFilter> allFilters) {
if (anyFilters.isEmpty()) {
return constructFilter(allFilters, AllMultiItemFilter::new);
}
if (allFilters.isEmpty()) {
return constructFilter(anyFilters, AnyMultiItemFilter::new);
}
final List<ItemFilter> filters = new ArrayList<>();
filters.add(constructFilter(allFilters, AllMultiItemFilter::new));
filters.add(constructFilter(anyFilters, AnyMultiItemFilter::new));
return constructFilter(filters, AllMultiItemFilter::new);
}
private String getTokenCleanText(SearchExpressionParser.TokenContext ctx) {
String cleanText = null;
if (ctx.PLAIN_TEXT() != null) {
cleanText = Pattern.quote(
PLAIN_TEXT_ESCAPED_PATTERN.matcher(ctx.PLAIN_TEXT().getSymbol().getText())
// Unescape everything in search expression
.replaceAll("$1"));
} else if (ctx.regex() != null) {
if (ctx.regex().REGEX_CONTENT() != null) {
cleanText = ctx.regex().REGEX_CONTENT().getSymbol().getText();
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
// Unescape spaces
if (spaceModeEnabled == 1) {
cleanText = REGEX_ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll("$1 ");
}
}
} else if (ctx.quoted() != null) {
if (ctx.quoted().QUOTED_CONTENT() != null) {
cleanText = ctx.quoted().QUOTED_CONTENT().getSymbol().getText();
// Unescape quotes
cleanText = ESCAPED_QUOTE_PATTERN.matcher(cleanText).replaceAll("\"");
cleanText = Pattern.quote(cleanText);
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
// Unescape spaces
if (spaceModeEnabled == 1) {
cleanText = ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll(" ");
}
}
}
return cleanText;
}
private ItemFilter visitChildren(ParserRuleContext node, Function<List<ItemFilter>, ItemFilter> filterConstructor) {
if (node.children != null && !node.children.isEmpty()) {
// By default return any found rule child filter (there should be only one)
if (filterConstructor == null) {
for (final ParseTree child : node.children) {
if (child instanceof RuleNode) {
return visit(child);
}
}
return defaultResult();
// Otherwise create a filter out of rule childrens' filters
} else {
final List<ItemFilter> filters = new ArrayList<>();
for (final ParseTree child : node.children) {
if (child instanceof RuleNode) {
filters.add(visit(child));
}
}
return constructFilter(filters, filterConstructor);
}
}
return defaultResult();
}
private ItemFilter getAlwaysProvidersFilter(String searchText) {
final List<ItemFilter> filters = searchParser.getAlwaysProvidersFilters(searchText);
return constructFilter(filters, AnyMultiItemFilter::new);
}
private ItemFilter constructFilter(List<ItemFilter> filters, Function<List<ItemFilter>, ItemFilter> constructor) {
if (!filters.isEmpty()) {
// Propagate the result up
if (filters.size() == 1) {
return filters.get(0);
}
return constructor.apply(filters);
} else {
return defaultResult();
}
}
}
@@ -0,0 +1,88 @@
package codechicken.nei.search;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.SearchTokenParser;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.filter.AllMultiRecipeFilter;
import codechicken.nei.filter.AllOthersRecipeFilter;
import codechicken.nei.filter.AnyOthersRecipeFilter;
import codechicken.nei.filter.RecipeFilter;
import codechicken.nei.filter.RecipeFilter.FilterContext;
public class RecipeFilterVisitor extends AbstractSearchExpressionVisitor<IRecipeFilter> {
private final ItemFilterVisitor itemFilterVisitor;
public RecipeFilterVisitor(SearchTokenParser searchParser) {
super(searchParser);
itemFilterVisitor = new ItemFilterVisitor(searchParser);
}
@Override
public IRecipeFilter visitRecipeSearchExpression(SearchExpressionParser.RecipeSearchExpressionContext ctx) {
if (ctx.recipeClauseExpression() != null) {
final List<IRecipeFilter> filters = new ArrayList<>();
for (SearchExpressionParser.RecipeClauseExpressionContext clauseCtx : ctx.recipeClauseExpression()) {
filters.add(createRecipeFilter(clauseCtx.searchExpression()));
}
return constructFilter(filters);
}
return defaultResult();
}
@Override
protected IRecipeFilter defaultResult() {
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
}
private IRecipeFilter createRecipeFilter(SearchExpressionParser.SearchExpressionContext ctx) {
if (ctx == null) {
return defaultResult();
}
final ItemFilter itemFilter = itemFilterVisitor.visitSearchExpression(ctx);
switch (ctx.type) {
case 0:
return new RecipeFilter(FilterContext.INPUT, !ctx.allRecipe, itemFilter);
case 1:
return new RecipeFilter(FilterContext.OUTPUT, !ctx.allRecipe, itemFilter);
case 2:
return getAllOrAnyFilter(
ctx.allRecipe,
itemFilter,
AnyOthersRecipeFilter::new,
AllOthersRecipeFilter::new);
// Doesn't support all by default
case 3:
return new RecipeFilter(FilterContext.ANY, !ctx.allRecipe, itemFilter);
default:
return defaultResult();
}
}
private IRecipeFilter getAllOrAnyFilter(boolean allRecipe, ItemFilter itemFilter,
Function<ItemFilter, IRecipeFilter> createAnyFilter, Function<ItemFilter, IRecipeFilter> createAllFilter) {
if (allRecipe) {
return createAllFilter.apply(itemFilter);
} else {
return createAnyFilter.apply(itemFilter);
}
}
private IRecipeFilter constructFilter(List<IRecipeFilter> filters) {
if (!filters.isEmpty()) {
// Propagate the result up
if (filters.size() == 1) {
return filters.get(0);
}
return new AllMultiRecipeFilter(filters);
} else {
return defaultResult();
}
}
}
@@ -0,0 +1,18 @@
package codechicken.nei.search;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import codechicken.nei.NEIClientConfig;
public class SearchExpressionErrorListener extends BaseErrorListener {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) {
NEIClientConfig.logger
.error("NEI Search grammar exception on line " + line + " char " + charPositionInLine + ": " + msg);
}
}
@@ -0,0 +1,124 @@
package codechicken.nei.search;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import net.minecraft.util.EnumChatFormatting;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.SearchTokenParser;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
public class SearchExpressionFormatVisitor extends AbstractSearchExpressionVisitor<String> {
private static final Pattern REGEX_ESCAPED_SPACE_PATTERN = Pattern.compile("([^\\\\](?:\\\\\\\\)+)?\\\\ ");
private static final Pattern ESCAPED_SPACE_PATTERN = Pattern.compile("\\\\ ");
public SearchExpressionFormatVisitor(SearchTokenParser searchParser) {
super(searchParser);
}
@Override
public String visitChildren(RuleNode node) {
if (node instanceof ParserRuleContext) {
return visitChildren((ParserRuleContext) node, null);
} else {
return defaultResult();
}
}
@Override
public String visitPrefixedExpression(SearchExpressionParser.PrefixedExpressionContext ctx) {
final EnumChatFormatting format = getFormatting(ctx.prefix, null);
return format + String.valueOf(ctx.prefix) + visitToken(ctx.token());
}
@Override
public String visitToken(SearchExpressionParser.TokenContext ctx) {
return getTokenCleanText(ctx, ctx.prefix);
}
@Override
public String visitRegex(SearchExpressionParser.RegexContext ctx) {
return visitChildren(ctx, ctx.prefix);
}
@Override
public String visitQuoted(SearchExpressionParser.QuotedContext ctx) {
return visitChildren(ctx, ctx.prefix);
}
@Override
protected String defaultResult() {
return "";
}
private String getTokenCleanText(SearchExpressionParser.TokenContext ctx, Character prefix) {
String cleanText = null;
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (ctx.PLAIN_TEXT() != null) {
cleanText = ctx.PLAIN_TEXT().getSymbol().getText();
// Unescape spaces
if (spaceModeEnabled == 1) {
cleanText = ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll(" ");
}
final EnumChatFormatting format = getFormatting(prefix, EnumChatFormatting.RESET);
return format + cleanText;
} else if (ctx.regex() != null) {
cleanText = visitRegex(ctx.regex());
if (spaceModeEnabled == 1) {
cleanText = REGEX_ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll("$1 ");
}
} else if (ctx.quoted() != null) {
cleanText = visitQuoted(ctx.quoted());
if (spaceModeEnabled == 1) {
cleanText = ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll(" ");
}
}
return cleanText;
}
private String formatChild(ParseTree child, Character prefix) {
if (child instanceof TerminalNode) {
final TerminalNode node = (TerminalNode) child;
final int type = node.getSymbol().getType();
final String format = Optional.ofNullable(
// check if highlight is defined for the token
Optional.ofNullable(SearchExpressionUtils.getHighlight(type))
// check if highlight is defined for the prefix
.orElse(getFormatting(prefix, null))
// use default highlight otherwise
).map(EnumChatFormatting::toString).orElse("");
return format + node.getSymbol().getText();
} else {
return visit(child);
}
}
private String visitChildren(ParserRuleContext node, Character prefix) {
if (node.children != null && !node.children.isEmpty()) {
return node.children.stream().map(child -> formatChild(child, prefix)).collect(Collectors.joining());
}
return defaultResult();
}
private EnumChatFormatting getFormatting(Character prefix, EnumChatFormatting defaultFormatting) {
if (prefix != null) {
if (prefix == '\0') {
return EnumChatFormatting.RESET;
}
final ISearchParserProvider provider = searchParser.getProvider(prefix);
if (provider != null) {
return provider.getHighlightedColor();
}
}
return defaultFormatting;
}
}
@@ -0,0 +1,89 @@
package codechicken.nei.search;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.util.EnumChatFormatting;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.SearchTokenParser;
public class SearchExpressionUtils {
private static final Map<Integer, EnumChatFormatting> HIGHLIGHT_MAP = new HashMap<>();
static {
for (HIGHLIGHTS elem : HIGHLIGHTS.values()) {
for (int parserType : elem.parserTypes) {
HIGHLIGHT_MAP.put(parserType, elem.f);
}
}
}
public static enum HIGHLIGHTS {
RECIPE(EnumChatFormatting.GREEN, SearchExpressionParser.RECIPE_INGREDIENTS,
SearchExpressionParser.RECIPE_RESULT, SearchExpressionParser.RECIPE_OTHERS),
OR(EnumChatFormatting.GRAY, SearchExpressionParser.OR),
BRACKETS(EnumChatFormatting.GRAY, SearchExpressionParser.LEFT_BRACKET, SearchExpressionParser.RIGHT_BRACKET),
NEGATE(EnumChatFormatting.BLUE, SearchExpressionParser.DASH),
REGEX(EnumChatFormatting.AQUA, SearchExpressionParser.REGEX_LEFT, SearchExpressionParser.REGEX_RIGHT),
QUOTED(EnumChatFormatting.GOLD, SearchExpressionParser.QUOTE_LEFT, SearchExpressionParser.QUOTE_RIGHT);
public final EnumChatFormatting f;
public final int[] parserTypes;
private HIGHLIGHTS(EnumChatFormatting format, int... parserTypes) {
this.f = format;
this.parserTypes = parserTypes;
}
@Override
public String toString() {
return this.f.toString();
}
}
public static EnumChatFormatting getHighlight(Integer parserType) {
return HIGHLIGHT_MAP.get(parserType);
}
public static <T> T visitSearchExpression(String text, AbstractSearchExpressionVisitor<T> visitor) {
SearchExpressionParser parser = createSearchExpressionParser(text, visitor.getSearchParser(), visitor);
SearchExpressionParser.RecipeSearchExpressionContext recipeCtx = parser.recipeSearchExpression();
// Manually check whether it's not a recipe expression, because parser would parse it either way
if (recipeCtx.recipeClauseExpression() != null && recipeCtx.recipeClauseExpression().size() == 1
&& recipeCtx.recipeClauseExpression(0).searchExpression().type == 3) {
return visitor.visitSearchExpression(recipeCtx.recipeClauseExpression(0).searchExpression());
}
return visitor.fail();
}
public static <T> T visitRecipeSearchExpression(String text, AbstractSearchExpressionVisitor<T> visitor) {
SearchExpressionParser parser = createSearchExpressionParser(text, visitor.getSearchParser(), visitor);
return visitor.visitRecipeSearchExpression(parser.recipeSearchExpression());
}
private static <T> SearchExpressionParser createSearchExpressionParser(String text, SearchTokenParser searchParser,
SearchExpressionParserBaseVisitor<T> visitor) {
final boolean doLogExceptions = NEIClientConfig.getBooleanSetting("inventory.search.logSearchExceptions");
final CharStream inputStream = CharStreams.fromString(text);
final SearchExpressionErrorListener errorListener = new SearchExpressionErrorListener();
final SearchExpressionLexer lexer = new SearchExpressionLexer(inputStream, searchParser);
lexer.removeErrorListeners();
if (doLogExceptions) {
lexer.addErrorListener(errorListener);
}
final CommonTokenStream tokenStream = new CommonTokenStream(lexer);
final SearchExpressionParser parser = new SearchExpressionParser(tokenStream);
parser.removeErrorListeners();
if (doLogExceptions) {
parser.addErrorListener(errorListener);
}
return parser;
}
}
@@ -0,0 +1,86 @@
package codechicken.nei.search;
import java.util.ArrayList;
import java.util.List;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.SearchTokenParser;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.api.ItemFilter;
public class SearchToken {
public Character ignore = null;
public boolean quotes = false;
public Character firstChar = null;
public String[] words;
public String rawText = "";
public int start = 0;
public int end = 0;
public ItemFilter getFilter(SearchTokenParser parser) {
if (this.words == null || this.words.length == 0) {
return null;
}
if (!this.rawText.isEmpty()) {
final ItemFilter result = parseToken(parser);
if (this.ignore != null) {
return new NegatedItemFilter(result);
} else {
return result;
}
} else if (this.ignore == null) {
return new NothingItemFilter();
}
return null;
}
public boolean isSimpleFilter() {
return this.firstChar == null && !this.quotes && this.ignore == null;
}
private ItemFilter parseToken(SearchTokenParser parser) {
final ISearchParserProvider provider = this.firstChar == null ? null : parser.getProvider(this.firstChar);
if (provider == null || provider.getSearchMode() == SearchMode.NEVER) {
final List<ItemFilter> filters = new ArrayList<>();
for (ISearchParserProvider _provider : parser.getProviders()) {
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
AllMultiItemFilter filter = generateFilters(_provider);
if (!filter.filters.isEmpty()) {
filters.add(filter);
}
}
}
return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters);
} else {
return generateFilters(provider);
}
}
private AllMultiItemFilter generateFilters(ISearchParserProvider provider) {
final AllMultiItemFilter filters = new AllMultiItemFilter();
for (String work : this.words) {
final ItemFilter filter = provider.getFilter(work);
if (filter != null) {
filters.filters.add(filter);
}
}
return filters;
}
}
@@ -0,0 +1,396 @@
package codechicken.nei.util;
import java.io.BufferedWriter;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Stream;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.StatCollector;
import net.minecraft.util.StringTranslate;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidContainerRegistry.FluidContainerData;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import codechicken.nei.ClientHandler;
import codechicken.nei.ItemList;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.SubsetWidget;
import codechicken.nei.api.API;
import codechicken.nei.recipe.StackInfo;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.relauncher.ReflectionHelper;
public class ItemUntranslator {
private class LanguageRegistryPatch implements AutoCloseable {
// RAII-style helper that temporarily swaps language data in
// LanguageRegistry / StringTranslate using reflection. This allows
// reading translation entries for a different language (eg. en_US)
// even when the game is using another language at runtime.
private String secondLanguage = "en_US";
private String language;
private static Map<String, Properties> modLanguageDataSymlink;
private Properties languageDataCache;
private static Map<String, String> secondLanguageListCache;
private static Map<String, String> languageListCache;
private static Field languageListField;
private static Field fieldStringTranslateInstance;
static {
try {
final Field modLanguageDataField = ReflectionHelper
.findField(LanguageRegistry.class, "modLanguageData");
languageListField = ReflectionHelper.findField(StringTranslate.class, "languageList", "field_74816_c");
fieldStringTranslateInstance = ReflectionHelper
.findField(StringTranslate.class, "instance", "field_74817_a");
modLanguageDataSymlink = (Map<String, Properties>) modLanguageDataField
.get(LanguageRegistry.instance());
languageListCache = (Map<String, String>) languageListField.get(fieldStringTranslateInstance.get(null));
} catch (Throwable e) {
languageListField = null;
fieldStringTranslateInstance = null;
}
}
private LanguageRegistryPatch(String secondLanguage) {
this.language = FMLCommonHandler.instance().getCurrentLanguage();
this.secondLanguage = secondLanguage;
patchLanguageRegistry();
patchStringTranslate();
}
@Override
public void close() {
restoreStringTranslate();
restoreLanguageRegistry();
}
private void patchLanguageRegistry() {
this.languageDataCache = modLanguageDataSymlink.get(this.language);
modLanguageDataSymlink.put(this.language, modLanguageDataSymlink.get(this.secondLanguage));
}
private void patchStringTranslate() {
if (languageListField != null) {
if (secondLanguageListCache == null) {
secondLanguageListCache = new HashMap<>(languageListCache);
modLanguageDataSymlink.get(secondLanguage)
.forEach((k, v) -> secondLanguageListCache.put((String) k, (String) v));
}
try {
languageListField.set(fieldStringTranslateInstance.get(null), secondLanguageListCache);
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to access StringTranslate language list", e);
}
}
}
private void restoreLanguageRegistry() {
final String language = FMLCommonHandler.instance().getCurrentLanguage();
modLanguageDataSymlink.put(language, languageDataCache);
languageDataCache = null;
}
private void restoreStringTranslate() {
if (languageListField != null && languageListCache != null) {
try {
languageListField.set(fieldStringTranslateInstance.get(null), languageListCache);
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to restore StringTranslate language list", e);
}
}
}
}
private static final String CONFIG_FILE = "untranslator.cfg";
private static final ItemUntranslator instance = new ItemUntranslator();
private final Map<String, String> secondNames = new HashMap<>();
private final Map<String, String> processedNames = new HashMap<>();
static {
API.addSubset("Untranslated", item -> instance.getItemStackDisplayName(item).isEmpty());
}
private ItemUntranslator() {}
public static ItemUntranslator getInstance() {
return instance;
}
public void generateUntranslatedNames() {
final Map<String, String> items = new HashMap<>();
items.put("language", FMLCommonHandler.instance().getCurrentLanguage());
for (Fluid fluid : FluidRegistry.getRegisteredFluids().values()) {
items.put(
fluid.getUnlocalizedName().toLowerCase(),
stripFormatting(StatCollector.translateToLocal(fluid.getUnlocalizedName())));
}
for (CreativeTabs tab : CreativeTabs.creativeTabArray) {
final List<ItemStack> tabItems = new ArrayList<>();
tab.displayAllReleventItems(tabItems);
for (ItemStack stack : tabItems) {
collectDisplayNameForStack(stack, items);
}
}
for (ItemStack stack : ItemList.items) {
collectDisplayNameForStack(stack, items);
}
for (FluidContainerData data : FluidContainerRegistry.getRegisteredFluidContainerData()) {
if (data.filledContainer != null) {
collectDisplayNameForStack(data.filledContainer, items);
}
}
for (Map.Entry<String, String> entry : LanguageRegistryPatch.languageListCache.entrySet()) {
final String key = entry.getKey().toLowerCase();
if (key.endsWith(".name") && !items.containsKey(key) && !entry.getValue().contains("%")) {
items.put(key, entry.getValue());
}
}
saveMappings(items);
}
private void collectDisplayNameForStack(ItemStack stack, Map<String, String> items) {
final String displayName = stripFormatting(stack.getDisplayName());
final String guidKey = determineGUIDForGeneration(stack, displayName, items);
if (!items.containsKey(guidKey)) {
items.put(guidKey, displayName);
}
}
private String determineGUIDForGeneration(ItemStack stack, String displayName, Map<String, String> items) {
final String unlocalizedName = stack.getUnlocalizedName().toLowerCase();
if (displayName.equals(stripFormatting(StatCollector.translateToLocal(unlocalizedName)))) {
return unlocalizedName;
}
if (!unlocalizedName.endsWith(".name")
&& displayName.equals(stripFormatting(StatCollector.translateToLocal(unlocalizedName + ".name")))) {
return unlocalizedName + ".name";
}
final FluidStack fluid = StackInfo.getFluid(stack);
if (fluid != null) {
final String fluidUnlocalizedName = fluid.getUnlocalizedName().toLowerCase();
if (displayName.equals(items.get(fluidUnlocalizedName))) {
return fluidUnlocalizedName;
}
if (!fluidUnlocalizedName.startsWith("fluid.")
&& displayName.equals(items.get("fluid." + fluidUnlocalizedName))) {
return "fluid." + fluidUnlocalizedName;
}
return (Item.itemRegistry.getNameForObject(stack.getItem()) + ":"
+ stack.getItemDamage()
+ "@"
+ fluid.getUnlocalizedName()).toLowerCase();
}
if (stack.hasTagCompound()) {
try {
final ItemStack stackCopy = new ItemStack(stack.getItem(), 1, stack.getItemDamage());
if (displayName.equals(stripFormatting(stackCopy.getDisplayName()))) {
return (Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + stack.getItemDamage())
.toLowerCase();
}
} catch (Exception e) {}
}
return StackInfo.getItemStackGUID(stack);
}
private String stripFormatting(String name) {
return EnumChatFormatting.getTextWithoutFormattingCodes(name).trim();
}
public synchronized String getItemStackDisplayName(ItemStack stack) {
if (stack == null || this.secondNames.isEmpty()) {
return "";
}
final String guidKey = StackInfo.getItemStackGUID(stack);
return this.processedNames.computeIfAbsent(guidKey, stackKey -> {
String displayName = this.secondNames.get(stackKey);
if (displayName == null) {
final String guid = findBestGUIDForLookup(stack);
displayName = this.secondNames
.computeIfAbsent(guid == null ? stackKey : guid, g -> getFallbackDisplayName(stack));
}
return displayName.equals(stripFormatting(stack.getDisplayName())) ? "" : displayName;
});
}
public String findBestGUIDForLookup(ItemStack stack) {
String guidKey = (Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + stack.getItemDamage())
.toLowerCase();
if (this.secondNames.containsKey(guidKey)) {
return guidKey;
}
final FluidStack fluid = StackInfo.getFluid(stack);
if (fluid != null) {
guidKey = (Item.itemRegistry.getNameForObject(stack.getItem()) + ":"
+ stack.getItemDamage()
+ "@"
+ fluid.getUnlocalizedName()).toLowerCase();
if (this.secondNames.containsKey(guidKey)) {
return guidKey;
}
final String fluidUnlocalizedName = fluid.getUnlocalizedName().toLowerCase();
if (!fluidUnlocalizedName.startsWith("fluid.")
&& this.secondNames.containsKey("fluid." + fluidUnlocalizedName)) {
return "fluid." + fluidUnlocalizedName;
}
if (this.secondNames.containsKey(fluidUnlocalizedName)) {
return fluidUnlocalizedName;
}
}
final String unlocalizedName = stack.getUnlocalizedName().toLowerCase();
if (!unlocalizedName.endsWith(".name") && this.secondNames.containsKey(unlocalizedName + ".name")) {
return unlocalizedName + ".name";
}
if (this.secondNames.containsKey(unlocalizedName)) {
return unlocalizedName;
}
return null;
}
private String getFallbackDisplayName(ItemStack stack) {
final String unlocalizedName = stack.getUnlocalizedName();
final String secondLanguage = this.secondNames.get("language");
String name = LanguageRegistry.instance().getStringLocalization(unlocalizedName, secondLanguage);
if (name == null && !unlocalizedName.endsWith(".name")) {
name = LanguageRegistry.instance().getStringLocalization(unlocalizedName + ".name", secondLanguage);
}
if (name == null || name.contains("%")) {
try (LanguageRegistryPatch lrPatch = new LanguageRegistryPatch(secondLanguage)) {
name = stack.getDisplayName();
}
}
final FluidStack fluid = StackInfo.getFluid(stack);
if (fluid != null) {
final String unlocalizedFluidName = fluid.getUnlocalizedName().toLowerCase();
String fluidName = this.secondNames.get(unlocalizedFluidName);
if (fluidName == null && !unlocalizedFluidName.startsWith("fluid.")) {
fluidName = this.secondNames.get("fluid." + unlocalizedFluidName);
}
if (fluidName != null && name != null && !name.isEmpty()) {
return name.replace(fluid.getLocalizedName(), fluidName);
} else {
name = fluidName;
}
}
return name == null ? "" : name;
}
protected void saveMappings(Map<String, String> items) {
try (BufferedWriter writer = Files.newBufferedWriter(
(new File(NEIClientConfig.configDir, CONFIG_FILE)).toPath(),
StandardCharsets.UTF_8)) {
for (Map.Entry<String, String> entry : items.entrySet()) {
if (entry.getValue().isEmpty()) continue;
writer.write(entry.getKey() + "=" + entry.getValue());
writer.newLine();
}
} catch (Exception e) {
NEIClientConfig.logger.error("Error saving untranslator", e);
}
NEIClientConfig.logger.info("Untranslator saved");
}
public void unload() {
this.secondNames.clear();
LanguageRegistryPatch.secondLanguageListCache = null;
this.processedNames.clear();
}
public void load() {
unload();
this.secondNames.clear();
if (ClientHandler.loadSettingsFile(CONFIG_FILE, null, this::parseStream)
|| ClientHandler.loadSettingsResource(CONFIG_FILE, this::parseStream)) {
NEIClientConfig.logger.info("Untranslator loaded");
}
if (!this.secondNames.containsKey("language")
|| FMLCommonHandler.instance().getCurrentLanguage().equals(this.secondNames.get("language"))) {
this.secondNames.clear();
}
SubsetWidget.updateHiddenItems();
}
private void parseStream(Stream<String> lines) {
lines.forEach(line -> {
final int sepIndex = line.indexOf('=');
if (sepIndex != -1) {
this.secondNames.put(line.substring(0, sepIndex).trim(), line.substring(sepIndex + 1).trim());
}
});
}
}
@@ -0,0 +1,8 @@
# 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.
appeng.client.gui.implementations.GuiPatternTerm
appeng.client.gui.implementations.GuiPatternTermEx
com.glodblock.github.client.gui.GuiFluidPatternExWireless
com.glodblock.github.client.gui.GuiFluidPatternTerminal
com.glodblock.github.client.gui.GuiFluidPatternTerminalEx
com.glodblock.github.client.gui.GuiFluidPatternWireless
@@ -557,7 +557,6 @@ connor135246.campfirebackport.client.compat.nei.NEICampfireStateChangerHandler,c
smelting,Natura:NetherFurnace,,Natura,TRUE,,,,,,
crafting,gregtech:gt.metaitem.01:32744,,gregtech,TRUE,gregapi_post,,,,,"GT5 start: for readability, keep all GTNH-related catalysts below"
gt.recipe.primitiveblastfurnace,gregtech:gt.blockmachines:140,,gregtech,TRUE,gregapi_post,,,,,
gt.recipe.primitiveblastfurnace,gregtech:gt.blockmachines:108,,gregtech,TRUE,gregapi_post,,,,,
smelting,gregtech:gt.blockmachines:103,,gregtech,TRUE,gregapi_post,,,,,
smelting,gregtech:gt.blockmachines:104,,gregtech,TRUE,gregapi_post,,,,,
gt.recipe.macerator,gregtech:gt.blockmachines:106,,gregtech,TRUE,gregapi_post,,,,,
@@ -823,6 +822,7 @@ gt.recipe.largeboilerfakefuels,gregtech:gt.blockmachines:1023,,gregtech,TRUE,gre
gt.recipe.vacuumfreezer,gregtech:gt.blockmachines:1002,,gregtech,TRUE,gregapi_post,,,,,
smelting,gregtech:gt.blockmachines:1003,,gregtech,TRUE,gregapi_post,,,,,
smelting,gregtech:gt.blockmachines:15412,,gregtech,TRUE,gregapi_post,,,,,
smelting,gregtech:gt.blockmachines:31087,,gregtech,TRUE,gregapi_post,,,,,
gt.recipe.dieselgeneratorfuel,gregtech:gt.blockmachines:1110,,gregtech,TRUE,gregapi_post,,,,,
gt.recipe.dieselgeneratorfuel,gregtech:gt.blockmachines:1111,,gregtech,TRUE,gregapi_post,,,,,
gt.recipe.dieselgeneratorfuel,gregtech:gt.blockmachines:1112,,gregtech,TRUE,gregapi_post,,,,,
@@ -1172,11 +1172,8 @@ gt.recipe.circuitassembler,gregtech:gt.blockmachines:12096,,dreamcraft,TRUE,,,,,
gt.recipe.alloysmelter,gregtech:gt.blockmachines:31023,,miscutils,TRUE,,,,,,
gt.recipe.assembler,gregtech:gt.blockmachines:876,,miscutils,TRUE,,,,,,
gt.recipe.metalbender,gregtech:gt.blockmachines:792,,miscutils,TRUE,,,,,,
gt.recipe.compressor,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.cuttingsaw,gregtech:gt.blockmachines:992,,miscutils,TRUE,,,,,,
gt.recipe.extractor,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.extruder,gregtech:gt.blockmachines:859,,miscutils,TRUE,,,,,,
gt.recipe.lathe,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.macerator,gregtech:gt.blockmachines:31041,,miscutils,TRUE,,,,,,
gt.recipe.macerator,gregtech:gt.blockmachines:797,,miscutils,TRUE,,,,,,
ic2.recycler,gregtech:gt.blockmachines:799,,miscutils,TRUE,,,,,,
@@ -1185,15 +1182,9 @@ gt.recipe.thermalcentrifuge,gregtech:gt.blockmachines:849,,miscutils,TRUE,,,,,,
gt.recipe.orewasher,gregtech:gt.blockmachines:850,,miscutils,TRUE,,,,,,
gt.recipe.packager,gregtech:gt.blockmachines:942,,miscutils,TRUE,,,,,,
gt.recipe.rockbreaker,gregtech:gt.blockmachines:31065,,miscutils,TRUE,,,,,,
gt.recipe.fermenter,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.fluidextractor,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.fluidsolidifier,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.distillery,gregtech:gt.blockmachines:31021,,miscutils,TRUE,,,,,,
gt.recipe.distillationtower,gregtech:gt.blockmachines:31021,,miscutils,TRUE,,,,,,
gt.recipe.chemicalbath,gregtech:gt.blockmachines:850,,miscutils,TRUE,,,,,,
gt.recipe.polarizer,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.autoclave,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.laserengraver,gregtech:gt.blockmachines:860,,miscutils,TRUE,,,,,,
gt.recipe.press,gregtech:gt.blockmachines:792,,miscutils,TRUE,,,,,,
gt.recipe.slicer,gregtech:gt.blockmachines:992,,miscutils,TRUE,,,,,,
gt.recipe.sifter,gregtech:gt.blockmachines:840,,miscutils,TRUE,,,,,,
@@ -1201,8 +1192,6 @@ gt.recipe.arcfurnace,gregtech:gt.blockmachines:862,,miscutils,TRUE,,,,,,
gt.recipe.plasmaarcfurnace,gregtech:gt.blockmachines:862,,miscutils,TRUE,,,,,,
gt.recipe.blastfurnace,gregtech:gt.blockmachines:963,,miscutils,TRUE,,,,,,
gt.recipe.implosioncompressor,gregtech:gt.blockmachines:964,,miscutils,TRUE,,,,,,
gt.recipe.dieselgeneratorfuel,gregtech:gt.blockmachines:960,,miscutils,TRUE,,10,,,,
gt.recipe.gasturbinefuel,gregtech:gt.blockmachines:961,,miscutils,TRUE,,10,,,,
gtpp.recipe.chemicaldehydrator,gregtech:gt.blockmachines:911,,miscutils,TRUE,,,,,,
gtpp.recipe.chemicaldehydrator,gregtech:gt.blockmachines:912,,miscutils,TRUE,,,,,,
gtpp.recipe.chemicaldehydrator,gregtech:gt.blockmachines:813,,miscutils,TRUE,,,,,,
@@ -1266,8 +1255,6 @@ bw.recipe.biolab,gregtech:gt.blockmachines:12705,,bartworks,TRUE,,,,,,
bw.recipe.biolab,gregtech:gt.blockmachines:12706,,bartworks,TRUE,,,,,,
bw.recipe.biolab,gregtech:gt.blockmachines:12707,,bartworks,TRUE,,,,,,
bw.recipe.biolab,gregtech:gt.blockmachines:12708,,bartworks,TRUE,,,,,,
bw.recipe.biolab,gregtech:gt.blockmachines:12709,,bartworks,TRUE,,,,,,
bw.recipe.biolab,gregtech:gt.blockmachines:12710,,bartworks,TRUE,,,,,,
bw.recipe.BacteriaVat,gregtech:gt.blockmachines:12712,,bartworks,TRUE,,,,,,
gt.recipe.electricimplosioncompressor,gregtech:gt.blockmachines:12734,,bartworks,TRUE,,,,,,
gt.recipe.dieselgeneratorfuel,gregtech:gt.blockmachines:1113,,GoodGenerator,TRUE,,,,,,
@@ -1496,4 +1483,4 @@ straywolfe.cookingwithtfc.client.nei.ClayOvenRecipeHandler,cookingwithtfc:item.c
factorization.nei.RecipeGrinder,factorization:diamond_cutting_head,,factorization,TRUE,,,,,,,,,,,
factorization.nei.RecipeMixer,factorization:FzBlock:18,,factorization,TRUE,,,,,,,,,,,
ua.pp.shurgent.tfctech.nei.WireDrawBenchRecipeHandler,tfctech:item.Wire Draw Bench,,tfctech,TRUE,,,,,,,,,,,
mekanism.client.nei.ElectrolyticSeparatorRecipeHandler,Mekanism:MachineBlock2:4,,Mekanism,TRUE,,,,,,,,,,,
mekanism.client.nei.ElectrolyticSeparatorRecipeHandler,Mekanism:MachineBlock2:4,,Mekanism,TRUE,,,,,,,,,,,
1 handler itemName nbtInfo modId modRequired excludedModId priority minVersion maxVersion forceClassName itemNotes
557 smelting Natura:NetherFurnace Natura TRUE
558 crafting gregtech:gt.metaitem.01:32744 gregtech TRUE gregapi_post GT5 start: for readability, keep all GTNH-related catalysts below
559 gt.recipe.primitiveblastfurnace gregtech:gt.blockmachines:140 gregtech TRUE gregapi_post
gt.recipe.primitiveblastfurnace gregtech:gt.blockmachines:108 gregtech TRUE gregapi_post
560 smelting gregtech:gt.blockmachines:103 gregtech TRUE gregapi_post
561 smelting gregtech:gt.blockmachines:104 gregtech TRUE gregapi_post
562 gt.recipe.macerator gregtech:gt.blockmachines:106 gregtech TRUE gregapi_post
822 gt.recipe.vacuumfreezer gregtech:gt.blockmachines:1002 gregtech TRUE gregapi_post
823 smelting gregtech:gt.blockmachines:1003 gregtech TRUE gregapi_post
824 smelting gregtech:gt.blockmachines:15412 gregtech TRUE gregapi_post
825 smelting gregtech:gt.blockmachines:31087 gregtech TRUE gregapi_post
826 gt.recipe.dieselgeneratorfuel gregtech:gt.blockmachines:1110 gregtech TRUE gregapi_post
827 gt.recipe.dieselgeneratorfuel gregtech:gt.blockmachines:1111 gregtech TRUE gregapi_post
828 gt.recipe.dieselgeneratorfuel gregtech:gt.blockmachines:1112 gregtech TRUE gregapi_post
1172 gt.recipe.alloysmelter gregtech:gt.blockmachines:31023 miscutils TRUE
1173 gt.recipe.assembler gregtech:gt.blockmachines:876 miscutils TRUE
1174 gt.recipe.metalbender gregtech:gt.blockmachines:792 miscutils TRUE
gt.recipe.compressor gregtech:gt.blockmachines:860 miscutils TRUE
1175 gt.recipe.cuttingsaw gregtech:gt.blockmachines:992 miscutils TRUE
gt.recipe.extractor gregtech:gt.blockmachines:860 miscutils TRUE
1176 gt.recipe.extruder gregtech:gt.blockmachines:859 miscutils TRUE
gt.recipe.lathe gregtech:gt.blockmachines:860 miscutils TRUE
1177 gt.recipe.macerator gregtech:gt.blockmachines:31041 miscutils TRUE
1178 gt.recipe.macerator gregtech:gt.blockmachines:797 miscutils TRUE
1179 ic2.recycler gregtech:gt.blockmachines:799 miscutils TRUE
1182 gt.recipe.orewasher gregtech:gt.blockmachines:850 miscutils TRUE
1183 gt.recipe.packager gregtech:gt.blockmachines:942 miscutils TRUE
1184 gt.recipe.rockbreaker gregtech:gt.blockmachines:31065 miscutils TRUE
gt.recipe.fermenter gregtech:gt.blockmachines:860 miscutils TRUE
gt.recipe.fluidextractor gregtech:gt.blockmachines:860 miscutils TRUE
gt.recipe.fluidsolidifier gregtech:gt.blockmachines:860 miscutils TRUE
1185 gt.recipe.distillery gregtech:gt.blockmachines:31021 miscutils TRUE
1186 gt.recipe.distillationtower gregtech:gt.blockmachines:31021 miscutils TRUE
1187 gt.recipe.chemicalbath gregtech:gt.blockmachines:850 miscutils TRUE
gt.recipe.polarizer gregtech:gt.blockmachines:860 miscutils TRUE
gt.recipe.autoclave gregtech:gt.blockmachines:860 miscutils TRUE
gt.recipe.laserengraver gregtech:gt.blockmachines:860 miscutils TRUE
1188 gt.recipe.press gregtech:gt.blockmachines:792 miscutils TRUE
1189 gt.recipe.slicer gregtech:gt.blockmachines:992 miscutils TRUE
1190 gt.recipe.sifter gregtech:gt.blockmachines:840 miscutils TRUE
1192 gt.recipe.plasmaarcfurnace gregtech:gt.blockmachines:862 miscutils TRUE
1193 gt.recipe.blastfurnace gregtech:gt.blockmachines:963 miscutils TRUE
1194 gt.recipe.implosioncompressor gregtech:gt.blockmachines:964 miscutils TRUE
gt.recipe.dieselgeneratorfuel gregtech:gt.blockmachines:960 miscutils TRUE 10
gt.recipe.gasturbinefuel gregtech:gt.blockmachines:961 miscutils TRUE 10
1195 gtpp.recipe.chemicaldehydrator gregtech:gt.blockmachines:911 miscutils TRUE
1196 gtpp.recipe.chemicaldehydrator gregtech:gt.blockmachines:912 miscutils TRUE
1197 gtpp.recipe.chemicaldehydrator gregtech:gt.blockmachines:813 miscutils TRUE
1255 bw.recipe.biolab gregtech:gt.blockmachines:12706 bartworks TRUE
1256 bw.recipe.biolab gregtech:gt.blockmachines:12707 bartworks TRUE
1257 bw.recipe.biolab gregtech:gt.blockmachines:12708 bartworks TRUE
bw.recipe.biolab gregtech:gt.blockmachines:12709 bartworks TRUE
bw.recipe.biolab gregtech:gt.blockmachines:12710 bartworks TRUE
1258 bw.recipe.BacteriaVat gregtech:gt.blockmachines:12712 bartworks TRUE
1259 gt.recipe.electricimplosioncompressor gregtech:gt.blockmachines:12734 bartworks TRUE
1260 gt.recipe.dieselgeneratorfuel gregtech:gt.blockmachines:1113 GoodGenerator TRUE
1483 factorization.nei.RecipeGrinder factorization:diamond_cutting_head factorization TRUE
1484 factorization.nei.RecipeMixer factorization:FzBlock:18 factorization TRUE
1485 ua.pp.shurgent.tfctech.nei.WireDrawBenchRecipeHandler tfctech:item.Wire Draw Bench tfctech TRUE
1486 mekanism.client.nei.ElectrolyticSeparatorRecipeHandler Mekanism:MachineBlock2:4 Mekanism TRUE
+29 -6
View File
@@ -37,6 +37,7 @@ nei.bookmark.remove_recipe=Remove Recipe
nei.bookmark.remove_item=Remove Item
nei.bookmark.add_item=Add Bookmark
nei.bookmark.add_item_with_recipe=Add Bookmark with Recipe
nei.bookmark.add_item_with_recipe_and_count=Add Bookmark with Recipe & Count
nei.bookmark.add_item_with_count=Add Bookmark with Count
nei.bookmark.change_quantity=Change Quantity
nei.bookmark.change_quantity_step=Change Quantity with Multiplier
@@ -51,6 +52,7 @@ nei.inventory.search.placeholder=Search...
nei.itempanel.quantity.default=One stack
nei.itempanel.loading=Loading Item List...
nei.itempanel.deleteItem=Delete Item
nei.itempanel.collapsed.button=G
nei.itempanel.collapsed.button.tip=Collapse/Expand All Collapsible Items
nei.itempanel.collapsed.hint.expand=Expand Group (%d items)
nei.itempanel.collapsed.hint.collapse=Collapse Group (%d items)
@@ -135,6 +137,8 @@ nei.chat.chunkoverlay.0=Chunk Overlay: Off
nei.chat.chunkoverlay.1=Chunk Overlay: Chunk Corners
nei.chat.chunkoverlay.2=Chunk Overlay: Chunks
nei.chat.chunkoverlay.3=Chunk Overlay: GT Ore Veins
nei.chat.moboverlay.0=Mob Spawning Overlay: §cOff
nei.chat.moboverlay.1=Mob Spawning Overlay: §2On
nei.chat.item_link.text=<%s> §6Click to add bookmark: %s
nei.chat.bookmark_added.text=Bookmark added!
@@ -145,6 +149,12 @@ nei.chat.recipeid.diff.finish=Finished processing recipe diff!
nei.chat.recipeid.dump.start=Start processing recipe handlers!
nei.chat.recipeid.dump.finish=Finished processing recipe handlers!
nei.chat.untranslator.dump.start=Start processing untranslator dump!
nei.chat.untranslator.dump.finish=Finished processing untranslator dump!
nei.chat.untranslator.dump.error=First, wait for the items to load!
nei.chat.untranslator.clear=File successfully deleted!
nei.chat.untranslator.clear.error=Could not delete file!
nei.options=NEI Options
nei.options.back=Back
nei.options.save=Save
@@ -173,8 +183,6 @@ nei.options.keys.gui.prev_machine=Prev Machine
nei.options.keys.gui.search=Focus Search Bar
nei.options.keys.gui.bookmark=Add Bookmark
nei.options.keys.gui.remove_recipe=Remove Group from Bookmarks
nei.options.keys.gui.bookmark_recipe=Add Bookmark with Recipe
nei.options.keys.gui.bookmark_count=Add Bookmark with Count
nei.options.keys.gui.bookmark_pull_items=Pull all Bookmarked Items
nei.options.keys.gui.hide_bookmarks=Toggle Bookmark Panel Visibility
nei.options.keys.gui.overlay=Fill/Overlay Crafting Grid
@@ -189,6 +197,7 @@ nei.options.keys.gui.itemzoom_toggle=Toggle Item Zoom
nei.options.keys.gui.itemzoom_hold=Hold to Display Item Zoom
nei.options.keys.gui.itemzoom_zoom_in=Item Zoom In
nei.options.keys.gui.itemzoom_zoom_out=Item Zoom Out
nei.options.keys.gui.favorite=Add Favorite
nei.options.keys.world=World
nei.options.keys.world.chunkoverlay=Chunk Boundary Overlay
nei.options.keys.world.moboverlay=Mob Spawn Overlay
@@ -249,6 +258,9 @@ nei.options.inventory.favorites.depth=Depth
nei.options.inventory.hotkeysHelpText=Show Hotkeys Help Text
nei.options.inventory.hotkeysHelpText.true=Yes
nei.options.inventory.hotkeysHelpText.false=No
nei.options.inventory.showHotkeys=Show Hotkeys
nei.options.inventory.showHotkeys.true=Yes
nei.options.inventory.showHotkeys.false=No
nei.options.inventory.guirecipe=Recipes GUI
nei.options.inventory.guirecipe.jeiStyleTabs=JEI Style Tabs
nei.options.inventory.guirecipe.jeiStyleTabs.true=Yes
@@ -284,6 +296,7 @@ nei.options.inventory.search.patternMode=Search Mode
nei.options.inventory.search.patternMode.0=Plain
nei.options.inventory.search.patternMode.1=Extended
nei.options.inventory.search.patternMode.2=Regex
nei.options.inventory.search.patternMode.3=Extended+
nei.options.inventory.search.spaceMode=Space Works like
nei.options.inventory.search.spaceMode.0=AND
nei.options.inventory.search.spaceMode.0.tip=Space between non-prefixed tokens works like "AND"
@@ -386,11 +399,14 @@ nei.options.inventory.history=History Panel
nei.options.inventory.history.enabled=History Panel Visibility
nei.options.inventory.history.enabled.true=Enabled
nei.options.inventory.history.enabled.false=Disabled
nei.options.inventory.history.historyColor=Color
nei.options.inventory.history.color=Color
nei.options.inventory.history.useRows=Rows
nei.options.inventory.history.splittingMode=Splitting Mode
nei.options.inventory.history.splittingMode.0=Background
nei.options.inventory.history.splittingMode.1=Dotted Line
nei.options.inventory.craftables=Craftables Panel
nei.options.inventory.craftables.enabled=Craftables Panel Visibility
nei.options.inventory.craftables.enabled.true=Enabled
nei.options.inventory.craftables.enabled.false=Disabled
nei.options.inventory.craftables.color=Color
nei.options.inventory.craftables.useRows=Rows
nei.options.inventory.subsets=Subsets Widget
nei.options.inventory.subsets.enabled=Subsets Visibility
nei.options.inventory.subsets.enabled.true=Enabled
@@ -419,6 +435,9 @@ nei.options.inventory.itemzoom.showName=Display Name
nei.options.inventory.itemzoom.showName.true=Show
nei.options.inventory.itemzoom.showName.false=Hide
nei.options.inventory.itemzoom.nameColor=Display Name Color
nei.options.inventory.itemUntranslator=Item Untranslator
nei.options.inventory.itemUntranslator.true=Enabled
nei.options.inventory.itemUntranslator.false=Disabled
nei.options.command=Commands
nei.options.command.creative=Gamemode
@@ -515,6 +534,7 @@ nei.recipe.overlay.mismatch=Mismatch Crafting Grid
nei.recipe.overlay.overlayRecipe=Overlay Recipe
nei.recipe.overlay.fillCraftingGrid=Fill Crafting Grid
nei.recipe.overlay.bookmarkRecipe=Save with Recipe
nei.recipe.overlay.bookmarkRecipeAndCount=Save with Recipe & Count
nei.recipe.favorite=Favorite
nei.recipe.favorite.bookmark_recipe=Save Recipe Tree
@@ -581,6 +601,9 @@ nei.presets.mode=Mode
nei.presets.mode.hide=Hide
nei.presets.mode.hide.char=H
nei.presets.mode.hide.tip=Hide items in panel
nei.presets.mode.subset=Subset
nei.presets.mode.subset.char=S
nei.presets.mode.subset.tip=Create custom subsets
nei.presets.mode.remove=Remove
nei.presets.mode.remove.tip=Hide items in panel and their recipes
nei.presets.mode.remove.char=R
@@ -28,6 +28,7 @@ nei.bookmark.crafting_chain.remainder=Остатки
nei.itempanel.quantity.default=Одна стопка
nei.itempanel.loading=Загрузка списка предметов...
nei.itempanel.deleteItem=Удалить предмет
nei.itempanel.collapsed.button=Г
nei.itempanel.collapsed.button.tip=Свернуть/Развернуть все группы предметов
nei.itempanel.collapsed.hint.expand=Развернуть группу (%d предметов)
nei.itempanel.collapsed.hint.collapse=Свернуть группу (%d предметов)
@@ -301,11 +302,14 @@ nei.options.inventory.history=Панель истории
nei.options.inventory.history.enabled=Видимость панели истории
nei.options.inventory.history.enabled.true=Включено
nei.options.inventory.history.enabled.false=Отключено
nei.options.inventory.history.historyColor=Цвет
nei.options.inventory.history.color=Цвет
nei.options.inventory.history.useRows=Строки
nei.options.inventory.history.splittingMode=Режим разделения
nei.options.inventory.history.splittingMode.0=Фон
nei.options.inventory.history.splittingMode.1=Пунктирная линия
nei.options.inventory.craftables=Панель создания
nei.options.inventory.craftables.enabled=Видимость панели создания
nei.options.inventory.craftables.enabled.true=Включено
nei.options.inventory.craftables.enabled.false=Отключено
nei.options.inventory.craftables.color=Цвет
nei.options.inventory.craftables.useRows=Строки
nei.options.inventory.itemzoom=Панель увеличения предметов
nei.options.inventory.itemzoom.enabled=Видимость панели увеличения предметов
nei.options.inventory.itemzoom.enabled.true=Включено
Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

@@ -0,0 +1,410 @@
package codechicken.nei;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Named.named;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.Language;
import net.minecraft.client.resources.LanguageManager;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.FieldSource;
import org.mockito.MockedStatic;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.PatternItemFilter;
import codechicken.nei.SearchField.SearchParserProvider;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.search.IdentifierFilter;
@DisplayName("Search expression")
public class SearchTokenParserTest {
private static class ModeValue {
private final String name;
private final int value;
public ModeValue(int value, String name) {
this.value = value;
this.name = name;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
private static final ModeValue[] spaceModes = { new ModeValue(0, "space works like AND"),
new ModeValue(1, "space works like SPACE"), new ModeValue(2, "space works like AND-X") };
private static final ModeValue[] spaceIsAndMode = { spaceModes[0] };
private static final ModeValue[] spaceIsSpaceMode = { spaceModes[1] };
private static final ModeValue[] patternModes = { new ModeValue(0, "Plain Text mode"),
new ModeValue(1, "Extended mode"), new ModeValue(2, "Regex mode"), new ModeValue(3, "Extended+ mode") };
private static final ModeValue[] extendedPlusPatternMode = { patternModes[3] };
private static final ModeValue[] extendedPatternModes = { patternModes[1], patternModes[3] };
private static final SearchParserProvider defaultParserProvider = parserProvider(
'\0',
"default",
SearchTokenParser.SearchMode.ALWAYS,
PatternItemFilter::new);
private static final Function<Pattern, ItemFilter> customCreateFilter = (pattern) -> new IdentifierFilter(pattern) {
@Override
public boolean matches(ItemStack stack) {
return pattern.matcher(getStringIdentifier(stack)).find();
}
@Override
protected String getStringIdentifier(ItemStack stack) {
if (stack == itemA) {
return "A";
}
return "F";
}
@Override
protected String getIdentifier(ItemStack stack) {
return getStringIdentifier(stack);
}
};
private static MockedStatic<NEIClientConfig> config;
private static Language language;
private static ItemStack itemA;
private static ItemStack itemB;
private static ItemStack itemC;
private static ItemStack itemAB;
private static ItemStack itemABC;
private static ItemStack itemQuestionMark;
private static List<Arguments> combinationsProvider(ModeValue[] spaceModes, ModeValue[] patternModes) {
List<Arguments> combinations = new ArrayList<>();
for (ModeValue s : spaceModes) {
for (ModeValue p : patternModes) {
combinations
.add(Arguments.arguments(named(s.getName(), s.getValue()), named(p.getName(), p.getValue())));
}
}
return combinations;
}
private static final List<Arguments> allModesTestProvider = combinationsProvider(spaceModes, patternModes);
private static final List<Arguments> extendedPlusAllSpaceModesTestProvider = combinationsProvider(
spaceModes,
extendedPlusPatternMode);
private static final List<Arguments> spaceIsSpaceAllModesTestProvider = combinationsProvider(
spaceIsSpaceMode,
patternModes);
private static final List<Arguments> spaceIsAndAllModesTestProvider = combinationsProvider(
spaceIsAndMode,
patternModes);
private static final List<Arguments> extendedAllSpaceModesTestProvider = combinationsProvider(
spaceModes,
extendedPatternModes);
private static final List<Arguments> spaceIsAndExtendedPlusModeTestProvider = combinationsProvider(
spaceIsAndMode,
extendedPlusPatternMode);
@BeforeAll
public static void initItems() {
itemA = mock(ItemStack.class);
when(itemA.getDisplayName()).thenReturn("a");
itemB = mock(ItemStack.class);
when(itemB.getDisplayName()).thenReturn("b");
itemC = mock(ItemStack.class);
when(itemC.getDisplayName()).thenReturn("c");
itemAB = mock(ItemStack.class);
when(itemAB.getDisplayName()).thenReturn("a b");
itemABC = mock(ItemStack.class);
when(itemABC.getDisplayName()).thenReturn("a b c");
itemQuestionMark = mock(ItemStack.class);
when(itemQuestionMark.getDisplayName()).thenReturn("?");
MockedStatic<Minecraft> staticMinecraft = mockStatic(Minecraft.class);
Minecraft minecraft = mock(Minecraft.class);
staticMinecraft.when(() -> Minecraft.getMinecraft()).thenReturn(minecraft);
LanguageManager languageManager = mock(LanguageManager.class);
when(minecraft.getLanguageManager()).thenReturn(languageManager);
language = mock(Language.class);
when(languageManager.getCurrentLanguage()).thenReturn(language);
when(language.getLanguageCode()).thenReturn("en");
config = mockStatic(NEIClientConfig.class);
config.when(() -> NEIClientConfig.getBooleanSetting(eq("inventory.search.logSearchExceptions")))
.thenReturn(false);
}
// All modes tests
@DisplayName("No providers")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("allModesTestProvider")
public void testWithoutProviders(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
Set<ItemStack> matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, "test");
assertEquals(0, matchedItems.size());
}
@DisplayName("Or")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("allModesTestProvider")
public void testOr(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
itemA.getDisplayName() + "|" + itemC.getDisplayName());
assertEquals(4, matchedItems.size());
assertTrue(matchedItems.contains(itemA));
assertTrue(matchedItems.contains(itemC));
assertTrue(matchedItems.contains(itemAB));
assertTrue(matchedItems.contains(itemABC));
}
@DisplayName("Negate")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("allModesTestProvider")
public void testNegate(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
"-" + itemA.getDisplayName());
assertEquals(3, matchedItems.size());
assertTrue(matchedItems.contains(itemB));
assertTrue(matchedItems.contains(itemC));
assertTrue(matchedItems.contains(itemQuestionMark));
}
@DisplayName("Custom prefix provider")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("allModesTestProvider")
public void testCustomPrefixProvider(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
searchParser
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
Set<ItemStack> matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, "?A");
assertEquals(1, matchedItems.size());
assertTrue(matchedItems.contains(itemA));
}
@DisplayName("Space mode")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("spaceIsSpaceAllModesTestProvider")
public void testSpaceMode(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
itemAB.getDisplayName());
assertEquals(2, matchedItems.size());
assertTrue(matchedItems.contains(itemAB));
assertTrue(matchedItems.contains(itemABC));
}
@DisplayName("Spaced expression")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("spaceIsAndAllModesTestProvider")
public void testSpacedExpression(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
itemA.getDisplayName() + " " + itemC.getDisplayName());
assertEquals(1, matchedItems.size());
assertTrue(matchedItems.contains(itemABC));
}
// Extended modes tests
@DisplayName("Regex")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("extendedAllSpaceModesTestProvider")
public void testRegex(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
"r/z?\\s" + itemB.getDisplayName() + "/");
assertEquals(2, matchedItems.size());
assertTrue(matchedItems.contains(itemAB));
assertTrue(matchedItems.contains(itemABC));
}
// Extended+ specific tests
@DisplayName("Sequence")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("extendedPlusAllSpaceModesTestProvider")
public void testSequence(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
searchParser
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
"\"" + itemA.getDisplayName() + "\"?A");
assertEquals(1, matchedItems.size());
assertTrue(matchedItems.contains(itemA));
}
@DisplayName("Brackets")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("extendedPlusAllSpaceModesTestProvider")
public void testBrackets(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
"{" + itemA.getDisplayName() + "|" + itemC.getDisplayName() + "}" + itemB.getDisplayName());
assertEquals(2, matchedItems.size());
assertTrue(matchedItems.contains(itemAB));
assertTrue(matchedItems.contains(itemABC));
}
@DisplayName("Quoted")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("extendedPlusAllSpaceModesTestProvider")
public void testQuoted(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
"\"" + itemB.getDisplayName() + " " + itemC.getDisplayName());
assertEquals(1, matchedItems.size());
assertTrue(matchedItems.contains(itemABC));
}
@DisplayName("Escaped symbols")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("spaceIsAndExtendedPlusModeTestProvider")
public void testEscapedSymbols(int spaceMode, int patternMode) {
SearchTokenParser searchParser = new SearchTokenParser();
searchParser.addProvider(defaultParserProvider);
searchParser
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
spaceMode,
patternMode,
searchParser,
'\\' + itemQuestionMark.getDisplayName());
assertEquals(1, matchedItems.size());
assertTrue(matchedItems.contains(itemQuestionMark));
matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, '\\' + " ");
assertEquals(2, matchedItems.size());
assertTrue(matchedItems.contains(itemAB));
assertTrue(matchedItems.contains(itemABC));
}
private static Set<ItemStack> testFilterAgainstAllItems(int spaceMode, int patternMode,
SearchTokenParser searchParser, String search) {
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.patternMode"))).thenReturn(patternMode);
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.spaceMode"))).thenReturn(spaceMode);
ItemFilter filter = searchParser.getFilter(search);
if (filter instanceof SearchTokenParser.IsRegisteredItemFilter) {
filter = ((SearchTokenParser.IsRegisteredItemFilter) filter).filter;
}
Set<ItemStack> matchedItems = Stream.of(itemA, itemB, itemC, itemAB, itemABC, itemQuestionMark)
.filter(filter::matches).collect(Collectors.toSet());
return matchedItems;
}
private static SearchParserProvider parserProvider(char prefix, String name,
SearchTokenParser.SearchMode searchMode, Function<Pattern, ItemFilter> createFilter) {
return new SearchParserProvider(prefix, name, EnumChatFormatting.RESET, createFilter) {
@Override
public SearchTokenParser.SearchMode getSearchMode() {
return searchMode;
}
@Override
public List<Language> getMatchingLanguages() {
List<Language> languages = new ArrayList<>();
languages.add(language);
return languages;
}
};
}
private static String printResult(Set<ItemStack> items) {
return items.stream().map(ItemStack::getDisplayName).collect(Collectors.joining(","));
}
private static String printFilterContents(ItemFilter filter) {
if (filter instanceof AnyMultiItemFilter) {
return "ANY: ("
+ ((AnyMultiItemFilter) filter).filters.stream().map(SearchTokenParserTest::printFilterContents)
.collect(Collectors.joining(","))
+ ")";
}
if (filter instanceof AllMultiItemFilter) {
return "ALL: ("
+ ((AllMultiItemFilter) filter).filters.stream().map(SearchTokenParserTest::printFilterContents)
.collect(Collectors.joining(","))
+ ")";
}
if (filter instanceof PatternItemFilter) {
return "pattern(" + ((PatternItemFilter) filter).pattern + ")";
}
return filter.toString();
}
}