mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-15 17:54:50 +00:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99fb5a656e | ||
|
|
4b663222b1 | ||
|
|
3e5d0088bc | ||
|
|
948be399d2 | ||
|
|
5f6091924d | ||
|
|
c5bd1f897a | ||
|
|
61d4b6ab62 | ||
|
|
791d05d5f5 | ||
|
|
66001275c4 | ||
|
|
db6b83c906 | ||
|
|
58ef6c85e0 | ||
|
|
ab0aeef872 | ||
|
|
23b7c69e2f | ||
|
|
bb89384493 | ||
|
|
e96e4dfdde | ||
|
|
0a6a6771d8 | ||
|
|
c96199a727 | ||
|
|
ab4ea2cfa1 | ||
|
|
b679883d02 | ||
|
|
0048ef80d0 | ||
|
|
84576aa7a2 | ||
|
|
c5cd5b9e61 | ||
|
|
b1bedcf403 | ||
|
|
bba0a11466 | ||
|
|
b80948f2f2 | ||
|
|
28539bbacc | ||
|
|
ec28bdf116 | ||
|
|
e5d55a9bd8 | ||
|
|
c1f7489de1 | ||
|
|
fa6fd01a3a | ||
|
|
453ba6667f | ||
|
|
4e11a45852 | ||
|
|
95274339a0 | ||
|
|
31fcd6af51 | ||
|
|
860bc059ca |
No files matched your search
@@ -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,5 +1,6 @@
|
||||
//version: 1707058017
|
||||
|
||||
plugins {
|
||||
id 'antlr'
|
||||
id 'com.gtnewhorizons.gtnhconvention'
|
||||
}
|
||||
+12
-2
@@ -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
@@ -1,4 +1,4 @@
|
||||
// Add any additional repositiroes for your dependencies here
|
||||
// Add any additional repositories for your dependencies here
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.43'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,,,,,,,,,,,
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user