Compare commits

..
49 Commits
Author SHA1 Message Date
Ruling-0 da55c3b12c Merge remote-tracking branch 'origin/master' into mu 2026-10-24 09:01:08 -05:00
slprime ebe83f970f Fix group drag between bookmark namespaces (#1090) 2026-10-24 01:09:00 +03:00
slprime ad9beb7f90 Fill crafting grid with one recipe when Ctrl is held (#1089) 2026-10-23 23:44:16 +03:00
slprime 68bdb51c6b Update tree camera and scrollbar dragging every frame (#1087) 2026-10-22 23:58:41 +03:00
slprime e4e1964dec Fix NPE in BookmarksGridSlot when dragging a group across namespaces (#1086) 2026-10-22 16:22:23 +03:00
slprime 560618024f Autocrafting Optimization (#1084) 2026-10-21 01:24:40 +03:00
slprime e2a2d2a9df Batch 9-slice quads in DrawableResource (#1082) 2026-10-19 15:47:52 +03:00
slprime 7163630cb1 Move 9-slice setup into DrawableBuilder (#1081) 2026-10-16 23:18:28 +03:00
RessedandMartin Robertz ab168ad6ba Make StrictIdentifierFilter (<modid:name:meta>) resolve item on first match (#1080)
Co-authored-by: Martin Robertz <[email protected]>
2026-10-16 20:42:59 +03:00
Martin Robertz e13e3483f0 update+deps+bs 2026-10-16 10:20:51 +02:00
slprime de26547b64 Treat item aliases as translation keys (#1079) 2026-10-15 20:19:58 +03:00
slprime c49f8808fd Fix NPE in item panel getMask (#1078) 2026-10-15 18:59:55 +03:00
slprime 1c6cc8b693 Unify hovered recipe lookup into HoveredRecipe (#1076) 2026-10-12 22:03:07 +03:00
Ruling-0 9f74331116 Merge remote-tracking branch 'origin/master' into mu 2026-10-12 11:27:47 -05:00
slprime 43ae1b613a Add recipe origins to handler debug widget (#1072) 2026-10-12 13:43:22 +03:00
Ruling-0 cdac4cc4a8 Bump MaterialLib to 0.24.0-mu 2026-10-11 18:25:32 -05:00
Wilhelm Schuster 402c4325a6 Update java version in GitHub issue template (#1074) 2026-10-11 23:33:54 +02:00
slprime 550e85c456 Fix -@mod and !@mod search syntax in Extended mode (#1071) 2026-10-10 14:58:17 +03:00
slprime 1673079cbb ItemZoom: Add side priority option (#1070) 2026-10-10 14:36:28 +03:00
Ruling-0 9958dd45ff Bump MaterialLib to 0.21.0-mu 2026-10-09 21:16:39 -05:00
Ruling-0 a25879aaad Bump MaterialLib to 0.20.0-mu 2026-10-09 19:03:07 -05:00
Ruling-0 d8c5870a91 Merge remote-tracking branch 'origin/master' into mu 2026-10-09 18:56:18 -05:00
slprime e6174c446a Add options for vertical line count in chain tooltips (#1068) 2026-10-09 21:14:11 +03:00
slprime 869b4571b0 Add recipe cycle freeze option (#1067) 2026-10-09 19:47:38 +03:00
Ruling-0 96260fe68b Bump MaterialLib to 0.19.0-mu 2026-10-06 15:02:21 -05:00
slprime 35f5400ecb Fix Crafting Tree NPE (#1064) 2026-10-06 19:57:10 +03:00
micvog 1e2801c891 Fix crash when returning to the crafting tree after its bookmark group was removed (#1063) 2026-10-06 19:46:37 +03:00
Ruling-0 f44536d2e8 Merge remote-tracking branch 'origin/master' into mu 2026-10-05 13:22:17 -05:00
Ressedandslprime 1b3b495fec Fix crash while dragging bookmark group to another grid (#1062)
Co-authored-by: slprime <[email protected]>
2026-10-05 20:07:52 +03:00
Ruling-0 fa8342cfed Bump MaterialLib to 0.18.0-mu 2026-10-04 12:35:02 -05:00
Ruling-0 82ddc4da41 Bump MaterialLib to 0.17.0-mu 2026-10-04 12:16:02 -05:00
Ruling-0 3d1d2f79e5 Bump MaterialLib to 0.16.0-mu 2026-10-04 02:48:31 -05:00
Ruling-0 867651ecab Bump MaterialLib to 0.15.0-mu 2026-09-30 13:01:52 -05:00
Ruling-0 0674aae0ec Merge remote-tracking branch 'origin/master' into mu 2026-09-30 11:33:15 -05:00
Ruling-0 5277fd36f5 Bump MaterialLib to 0.14.0-mu 2026-09-30 11:03:30 -05:00
Ruling-0 b6b31eca33 Bump MaterialLib to 0.13.0-mu 2026-09-29 16:57:41 -05:00
Ruling-0 0dbeae9f68 Bump MaterialLib to 0.12.0-mu 2026-09-29 07:22:41 -05:00
Ruling-0 f0547b5ca6 Merge remote-tracking branch 'origin/master' into mu 2026-09-28 07:38:24 -05:00
Ruling-0 e7a252a0de Bump MaterialLib to 0.11.0-mu 2026-09-22 09:54:05 -05:00
Ruling-0 442118d2e4 Merge remote-tracking branch 'origin/master' into mu 2026-09-20 11:21:25 -05:00
Ruling-0 972d0e4f3c Merge remote-tracking branch 'origin/master' into mu 2026-09-17 16:25:45 -05:00
Ruling-0 bffef55cb8 Merge remote-tracking branch 'origin/master' into mu 2026-09-15 16:10:19 -05:00
Ruling-0 e1d19e0bfc Merge remote-tracking branch 'origin/master' into mu 2026-09-09 12:51:25 -05:00
Ruling-0 b0c5f6fcda Bump MaterialLib to 0.10.0-mu 2026-08-29 22:33:14 -05:00
Ruling-0 953638be05 Merge remote-tracking branch 'origin/master' into mu 2026-08-29 22:19:17 -05:00
Ruling-0 e53d728baf Doc and code style pass 2026-08-20 17:53:20 -05:00
Ruling-0 58b63671ce Clean up ml resolver docs 2026-08-17 21:27:08 -05:00
Ruling-0 a1631b0f58 Resolve MaterialLib ml references in stack config entries 2026-08-17 20:18:14 -05:00
Ruling-0 eafd9ff4a3 Add MaterialLib compileOnly dependency 2026-08-17 20:18:14 -05:00
60 changed files with 1395 additions and 455 deletions

No files matched your search

+2 -4
View File
@@ -33,12 +33,10 @@ body:
description: What Java version are you using? It's worth mentioning that if you play on Java9+ you should try update to latest minor release (e.g. prefer Java 17.0.6 over 17.0.2) of that version.
options:
- Java 8
- Java 9
- Java 11
- Java 17
- Java 19
- Java 20
- Java 21
- java 25
- java 26
- Other (Please Specify)
validations:
required: true
+5 -4
View File
@@ -5,13 +5,14 @@ configurations.create('gtnhlibEmbed') {
}
dependencies {
api('com.github.GTNewHorizons:CodeChickenCore:1.4.21:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.49:dev')
api('com.github.GTNewHorizons:CodeChickenCore:1.4.22:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.54:dev')
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
compileOnly('maven.modrinth:forge-essentials:7.5.1')
compileOnly('com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.14-alpha:dev')
compileOnly('com.ruling_0:MaterialLib:0.24.0-mu:dev')
antlr('org.antlr:antlr4:4.13.2')
shadowImplementation('org.antlr:antlr4-runtime:4.13.2')
@@ -23,8 +24,8 @@ dependencies {
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.49") { transitive = false }
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.49") { transitive = false }
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.54") { transitive = false }
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.54") { transitive = false }
}
def embedArchiveOperations = gtnhGradle.archiveOperations
+1
View File
@@ -1,6 +1,7 @@
// Add any additional repositories for your dependencies here
repositories {
mavenLocal()
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.31'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.34'
}
@@ -572,9 +572,10 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
return;
} else if (this.sortableGroup != null) {
final int overRowIndex = grid.getRowIndex(mousey);
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
if (overRowIndex >= 0 && this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
if (overRowIndex >= 0 && !isOverNamespaceControls(mousex, mousey)
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
this.grid.moveGroup(this.sortableGroup, overRowIndex);
}
@@ -1022,14 +1023,18 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
this.mouseDownSlot = -1;
}
@Override
public boolean onMouseWheel(int shift, int mousex, int mousey) {
if ((!inEditingState() || this.sortableGroup != null) && new Rectangle4i(
private boolean isOverNamespaceControls(int mousex, int mousey) {
return new Rectangle4i(
namespacePrev.x,
namespacePrev.y,
namespaceNext.x + namespaceNext.w - namespacePrev.x,
namespacePrev.h).contains(mousex, mousey)) {
namespacePrev.h).contains(mousex, mousey);
}
@Override
public boolean onMouseWheel(int shift, int mousex, int mousey) {
if ((!inEditingState() || this.sortableGroup != null) && isOverNamespaceControls(mousex, mousey)) {
if (shift > 0) {
this.storage.prevNamespace();
@@ -1105,10 +1110,10 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
if (super.onMouseWheel(shift, mousex, mousey)) {
final int overRowIndex = grid.getRowIndex(mousey);
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
if (this.sortableGroup != null && overRowIndex >= 0
&& this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
this.grid.moveGroup(this.sortableGroup, overRowIndex);
}
+18 -22
View File
@@ -7,40 +7,36 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
public class GuiNEIButton extends GuiButton {
protected static final ResourceLocation guiTex = new ResourceLocation("textures/gui/widgets.png");
private static final DrawableResource[] BACKGROUNDS = new DrawableResource[3];
static {
for (int state = 0; state < BACKGROUNDS.length; state++) {
BACKGROUNDS[state] = new DrawableBuilder(guiTex, 0, 46 + state * 20, 200, 20).setSlices(2, 2, 2, 2).build();
}
}
public GuiNEIButton(int i, int j, int k, int l, int i1, String s) {
super(i, j, k, l, i1, s);
}
protected DrawableResource getBackground(int mouseX, int mouseY, boolean mouseOver) {
return BACKGROUNDS[getHoverState(mouseOver)];
}
public void drawButton(Minecraft minecraft, int x, int y) {
if (!visible) return;
minecraft.renderEngine.bindTexture(guiTex);
GL11.glColor4f(1, 1, 1, 1);
boolean mouseOver = x >= xPosition && y >= yPosition && x < xPosition + width && y < yPosition + height;
int k = getHoverState(mouseOver);
drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height / 2); // top left
drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height / 2); // top
// right
drawTexturedModalRect(
xPosition,
yPosition + height / 2,
0,
46 + k * 20 + 20 - height / 2,
width / 2,
height / 2); // bottom left
drawTexturedModalRect(
xPosition + width / 2,
yPosition + height / 2,
200 - width / 2,
46 + k * 20 + 20 - height / 2,
width / 2,
height / 2); // bottom right
mouseDragged(minecraft, x, y);
GL11.glColor4f(1, 1, 1, 1);
getBackground(x, y, mouseOver).draw(xPosition, yPosition, width, height);
mouseDragged(minecraft, x, y);
drawContent(minecraft, x, y, mouseOver);
}
@@ -55,7 +51,7 @@ public class GuiNEIButton extends GuiButton {
return ColorUtils.buttonLabelNormal.getColor();
}
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
protected void drawContent(Minecraft minecraft, int x, int y, boolean mouseOver) {
FontRenderer fontrenderer = minecraft.fontRenderer;
int color = getTextColour(mouseOver);
+2 -1
View File
@@ -11,6 +11,7 @@ import java.util.regex.Pattern;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.StatCollector;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
@@ -103,7 +104,7 @@ public class ItemList {
}
for (String alias : ItemInfo.getAliases(item)) {
if (this.pattern.matcher(alias).find()) {
if (this.pattern.matcher(StatCollector.translateToLocal(alias)).find()) {
return true;
}
}
+1 -7
View File
@@ -75,13 +75,7 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
}
public static void updateItemList(ArrayList<ItemStack> newItems) {
final ItemsPanelGrid grid = ItemPanels.itemPanel.getGrid();
if (grid.rawItems.size() != newItems.size()) {
grid.setPage(0);
}
grid.setItems(newItems);
ItemPanels.itemPanel.getGrid().setItems(newItems);
ItemPanels.itemPanel.realItems = newItems;
}
+24 -2
View File
@@ -111,8 +111,14 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
this.xPosition = (availableAreaWidth - this.scale * SLOT_SIZE) / 2;
this.availableAreaWidth = (int) (availableAreaWidth / screenScale);
if (availableAreaWidth / screenScale >= mouse.x) {
this.xPosition += gui.width * screenScale - availableAreaWidth;
final float rightSideShift = gui.width * screenScale - availableAreaWidth;
final boolean leftSideFree = availableAreaWidth / screenScale < mouse.x;
final boolean rightSideFree = mouse.x < gui.width - availableAreaWidth / screenScale;
final boolean showOnRight = leftSideFree && rightSideFree ? !preferLeftSide() : !leftSideFree;
if (showOnRight) {
this.xPosition += rightSideShift;
}
try {
@@ -128,6 +134,22 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
}
}
private boolean preferLeftSide() {
switch (NEIClientConfig.getIntSetting("inventory.itemzoom.preferredSide")) {
case 1:
return true;
case 2:
return false;
}
final int leftCount = NEIClientConfig.isBookmarkPanelHidden()
|| ItemPanels.bookmarkPanel.getGrid().getPerPage() == 0 ? 0 : ItemPanels.bookmarkPanel.getGrid().size();
final int rightCount = ItemPanels.itemPanel.getGrid().getPerPage() == 0 ? 0
: ItemPanels.itemPanel.getGrid().size();
return leftCount <= rightCount;
}
private float getPointSize(GuiContainer gui) {
final float screenScale = 1.0f / getScreenScale();
final float availableAreaWidth = (gui.width - (gui.xSize + gui.width) / 2) * screenScale;
@@ -89,6 +89,10 @@ public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot,
public void refresh(GuiContainer gui) {
if (this.newItems != null) {
if (this.rawItems.size() != this.newItems.size()) {
this.page = 0;
}
this.groupItems.clear();
this.itemToGroup.clear();
this.forceExpand = false;
@@ -167,7 +167,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
protected int activeStackIndex = -1;
protected Dimension size = new Dimension();
protected final boolean showNames;
protected int maxNamedRows = Integer.MAX_VALUE;
protected int length = 0;
protected int rows = 0;
@@ -175,29 +175,25 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
protected int gridColumns = 0;
protected int gridCount = 0;
public static ItemsTooltipLineHandler grid(String label, List<ItemStack> items, int maxRows) {
return new ItemsTooltipLineHandler(label, items, true, maxRows, false);
}
public static ItemsTooltipLineHandler list(String label, List<ItemStack> items, int maxRows) {
return new ItemsTooltipLineHandler(label, items, true, maxRows, true);
public static ItemsTooltipLineHandler of(String label, List<ItemStack> items, int maxRows, int maxNamedRows) {
return new ItemsTooltipLineHandler(label, items, true, maxRows, maxNamedRows);
}
public ItemsTooltipLineHandler(String label, List<ItemStack> items) {
this(label, items, true, DEFAULT_MAX_ROWS, false);
this(label, items, true, DEFAULT_MAX_ROWS, 0);
}
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
this(label, items, saveStackSize, maxRows, false);
this(label, items, saveStackSize, maxRows, 0);
}
protected ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows,
boolean showNames) {
int maxNamedRows) {
this.label = label;
this.items = groupingItemStacks(items);
this.amountRenderer = saveStackSize ? new TotalAmountRenderer() : new NoAmountRenderer();
this.length = this.items.size();
this.showNames = showNames;
this.maxNamedRows = maxNamedRows;
setMaxRows(maxRows);
}
@@ -214,7 +210,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
this.names.clear();
this.nameRows = 0;
if (!this.showNames || !layoutWithNames(maxRows)) {
if (!layoutWithNames(maxRows)) {
layoutAsGrid(maxRows);
}
@@ -228,7 +224,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
}
protected boolean layoutWithNames(int maxRows) {
final int maxNames = this.length <= maxRows ? this.length : maxRows - 1;
final int maxNames = Math.min(this.maxNamedRows, this.length <= maxRows ? this.length : maxRows - 1);
final int[] lineWidth = new int[maxNames + 1];
lineWidth[0] = labelWidth();
@@ -61,6 +61,9 @@ public class NEICPH implements IClientPacketHandler {
case S2C.CACHE_BOOKMARK_DATA:
handleCacheBookmarkData(packet);
break;
case S2C.SYNC:
ServerSync.onReply(packet.readInt());
break;
}
}
@@ -200,6 +200,11 @@ public class NEIClientConfig {
tag.getTag("inventory.bookmarks.showRecipeMarkerMode").setComment("Show Recipe Marker").getIntValue(0);
API.addOption(new OptionCycled("inventory.bookmarks.showRecipeMarkerMode", 3, true));
tag.getTag("inventory.bookmarks.maxVerticalTooltipRows")
.setComment("Max rows in vertical crafting chain tooltip lists (0 - disable, always use grid)")
.getIntValue(12);
API.addOption(new OptionIntegerField("inventory.bookmarks.maxVerticalTooltipRows", 0, 50));
tag.getTag("inventory.bookmarks.recipeMarkerColor").setComment("Color of the icon marker")
.getHexValue(0xADADAD);
API.addOption(
@@ -267,6 +272,11 @@ public class NEIClientConfig {
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.scrollPages", true));
tag.getTag("inventory.guirecipe.freezeCycleOnHover")
.setComment("Stop cycling the recipe while hovering over an ingredient or a button")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.freezeCycleOnHover", true));
tag.getTag("inventory.guirecipe.profile").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
@@ -372,6 +382,11 @@ public class NEIClientConfig {
tag.getTag("inventory.itemzoom.onlySolid").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.itemzoom.onlySolid", true));
tag.getTag("inventory.itemzoom.preferredSide")
.setComment("Which side to prefer showing the zoomed item on, when both sides are free.")
.getIntValue(0);
API.addOption(new OptionCycled("inventory.itemzoom.preferredSide", 3, true));
tag.getTag("inventory.itemzoom.helpText")
.setComment("Display name \"Item Zoom\" and the hotkey to toggle this mod below the zoomed item.")
.getBooleanValue(true);
@@ -1106,6 +1121,10 @@ public class NEIClientConfig {
return getIntSetting("inventory.bookmarks.showRecipeMarkerMode");
}
public static int getMaxVerticalTooltipRows() {
return getIntSetting("inventory.bookmarks.maxVerticalTooltipRows");
}
public static boolean showItemQuantityWidget() {
return getBooleanSetting("inventory.showItemQuantityWidget");
}
@@ -1166,6 +1185,10 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
}
public static boolean freezeCycleOnHover() {
return getBooleanSetting("inventory.guirecipe.freezeCycleOnHover");
}
public static boolean requireShiftForOverlayRecipe() {
return getBooleanSetting("inventory.guirecipe.shiftOverlayRecipe");
}
@@ -14,11 +14,13 @@ import com.google.common.eventbus.Subscribe;
import codechicken.core.CommonUtils;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.nei.api.API;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.asm.NEICorePlugin;
import codechicken.nei.config.IMCHandler;
import codechicken.nei.guihook.HideousLinkedList;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.stackinfo.MaterialLibStackStringifyHandler;
import cpw.mods.fml.client.FMLFileResourcePack;
import cpw.mods.fml.client.FMLFolderResourcePack;
import cpw.mods.fml.common.DummyModContainer;
@@ -114,6 +116,10 @@ public class NEIModContainer extends DummyModContainer {
IMCForNEI.IMCSender();
}
ServerHandler.load();
if (Loader.isModLoaded("materiallib")) {
API.registerStackStringifyHandler(new MaterialLibStackStringifyHandler());
}
}
@Subscribe
@@ -92,9 +92,17 @@ public class NEISPH implements IServerPacketHandler {
case C2S.SEND_CHAT_ITEM_LINK:
handleSendChatItemLink(sender, packet);
break;
case C2S.SYNC:
handleSync(sender, packet.readInt());
break;
}
}
private void handleSync(EntityPlayerMP sender, int id) {
sender.sendContainerAndContentsToPlayer(sender.openContainer, sender.openContainer.getInventory());
new PacketCustom(channel, S2C.SYNC).writeInt(id).sendToPlayer(sender);
}
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
NEIServerUtils.sendChatItemLink(sender, payloadTag);
@@ -24,6 +24,7 @@ public final class PacketIDs {
public static final int REQUEST_POTION_GUI = 24;
public static final int SET_DUMMY_SLOT = 25;
public static final int SEND_CHAT_ITEM_LINK = 26;
public static final int SYNC = 27;
}
public static final class S2C {
@@ -38,5 +39,6 @@ public final class PacketIDs {
public static final int SET_CREATIVE_PLUS_MODE = 23;
public static final int OPEN_POTION_GUI = 24;
public static final int CACHE_BOOKMARK_DATA = 25;
public static final int SYNC = 26;
}
}
@@ -445,7 +445,7 @@ public class SearchTokenParser {
final char ch = input.charAt(index);
if (!insideQuotes && delimiters.indexOf(ch) >= 0
&& (!space || index == 0 || input.charAt(index - 1) == ' ')) {
&& (!space || index == 0 || input.charAt(index - 1) == ' ' || lastEnd == index)) {
if (lastEnd == index) {
token += String.valueOf(ch);
@@ -0,0 +1,36 @@
package codechicken.nei;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.PacketIDs.C2S;
/**
* Waits until the server has processed everything the client sent before. The server handles slot clicks and NEI
* packets in the order they came and answers after it has sent the slot updates, so once the answer is here the client
* container holds what the server computed.
*/
public final class ServerSync {
private static int lastId;
private static volatile int awaiting;
private ServerSync() {}
public static void request() {
if (!NEIClientConfig.hasSMPCounterPart()) {
awaiting = 0;
return;
}
awaiting = ++lastId;
new PacketCustom(NEICPH.channel, C2S.SYNC).writeInt(awaiting).sendToServer();
}
public static boolean pending() {
return awaiting != 0;
}
static void onReply(int id) {
if (id == awaiting) awaiting = 0;
}
}
@@ -9,6 +9,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -541,14 +542,22 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
return;
}
final AtomicInteger resolved = new AtomicInteger();
final AtomicInteger invalid = new AtomicInteger();
Stream.of(dir.listFiles()).filter(file -> !file.isDirectory()).forEach(file -> {
ClientHandler.loadSettingsFile(
"subsets/" + file.getName(),
lines -> parseFile(file.getName(), lines.collect(Collectors.toList())));
lines -> parseFile(file.getName(), lines.collect(Collectors.toList()), resolved, invalid));
});
if (resolved.get() + invalid.get() > 0) {
NEIClientConfig.logger.info("Resolved {} MaterialLib entries ({} invalid)", resolved.get(), invalid.get());
}
}
private static void parseFile(String resource, List<String> itemStrings) {
private static void parseFile(String resource, List<String> itemStrings, AtomicInteger resolved,
AtomicInteger invalid) {
final JsonParser parser = new JsonParser();
final int dotIndex = resource.lastIndexOf('.');
final String subsetNamespace = dotIndex > 0 ? resource.substring(0, dotIndex) : resource;
@@ -578,7 +587,13 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
final NBTBase nbt = NBTJson.toNbt(parser.parse(itemStr));
if (nbt instanceof NBTTagCompound tag) {
((ItemStackSet) processedTag.filter).add(StackInfo.loadFromNBT(tag));
final ItemStack stack = StackInfo.loadFromNBT(tag);
if (tag.getString("strId").startsWith(StackInfo.MATERIALLIB_PREFIX)) {
(stack != null ? resolved : invalid).incrementAndGet();
}
((ItemStackSet) processedTag.filter).add(stack);
} else {
throw new IllegalArgumentException(
"Expected NBTTagCompound but got " + nbt.getClass().getSimpleName());
@@ -180,6 +180,8 @@ public class API {
/**
* Add an alias name that this item can be found by in the search field, in addition to its display name.
*
* @param aliases translation keys, localized at search time
*/
public static void setAliases(ItemStack item, String... aliases) {
final List<String> list = Arrays.asList(aliases).stream().filter(Objects::nonNull).map(String::trim)
@@ -19,7 +19,6 @@ import codechicken.nei.BookmarkContainerInfo;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemQuantityField;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.KeyManager;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
@@ -38,6 +37,7 @@ import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiFavoriteButton;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.HoveredRecipe;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeHandlerRef;
@@ -415,41 +415,37 @@ public abstract class ShortcutInputHandler {
private static Recipe getFocusedRecipe(ItemStack stackover, int mousex, int mousey, boolean useFavorites) {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
Recipe recipe = null;
if (useFavorites) {
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) {
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) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
if (focusedRecipe != null) {
return focusedRecipe;
}
}
if (gui instanceof GuiRecipe<?>guiRecipe) {
recipe = guiRecipe.getFocusedRecipe();
if (!useFavorites) {
return null;
}
final HoveredRecipe hovered = HoveredRecipe.of(gui, stackover);
if (hovered == null || hovered.source != HoveredRecipe.Source.ITEM_PANEL) {
return null;
}
final Recipe recipe = Recipe.of(hovered.recipeId);
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;
}
}
});
}
return recipe;
@@ -529,7 +525,9 @@ public abstract class ShortcutInputHandler {
}
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
final RecipeId recipeId = getHotkeyRecipeId(gui, mousex, mousey, stack, slot);
final HoveredRecipe hovered = HoveredRecipe.of(gui, stack);
final RecipeId recipeId = hovered == null || hovered.source == HoveredRecipe.Source.OTHER
|| hovered.source == HoveredRecipe.Source.BOOKMARK && hovered.favorite ? null : hovered.recipeId;
if (slot != null) {
final BookmarkGroup group = slot.getGroup();
@@ -729,36 +727,4 @@ public abstract class ShortcutInputHandler {
}
}
private static RecipeId getHotkeyRecipeId(GuiContainer gui, int mousex, int mousey, ItemStack stack,
BookmarksGridSlot slot) {
if (slot != null) {
return slot.getType() == BookmarkItemType.INGREDIENT ? null : slot.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;
}
}
@@ -14,6 +14,7 @@ import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import java.util.Map;
@@ -22,11 +23,14 @@ import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import codechicken.lib.asm.ASMBlock;
@@ -98,6 +102,48 @@ public class NEITransformer implements IClassTransformer {
}
});
if (Boolean.getBoolean("nei.recipeOrigins")) {
// Replace the plain ArrayList CraftingManager's constructor builds its recipe list with, so every recipe
// added anywhere (including vanilla's own, added in that same constructor) is observed for
// codechicken.nei.recipe.debug.RecipeOrigins. Must be done at classload time, since by the time any mod
// code runs vanilla has already populated the list. Capturing a stack trace per recipe is slow, so this is
// opt-in via -Dnei.recipeOrigins=true.
transformer.add(
new MethodTransformer(
new ObfMapping("net/minecraft/item/crafting/CraftingManager", "<init>", "()V")) {
@Override
public void transform(MethodNode mv) {
ASMHelper.logger.debug("NEI: Hooking CraftingManager recipe list for origin tracking");
TypeInsnNode newInsn = null;
MethodInsnNode initInsn = null;
// only the first ArrayList construction, the recipe list field
for (AbstractInsnNode insn : mv.instructions.toArray()) {
if (newInsn == null) {
if (insn.getOpcode() == NEW
&& "java/util/ArrayList".equals(((TypeInsnNode) insn).desc)) {
newInsn = (TypeInsnNode) insn;
}
} else if (insn.getOpcode() == INVOKESPECIAL
&& "java/util/ArrayList".equals(((MethodInsnNode) insn).owner)
&& "<init>".equals(((MethodInsnNode) insn).name)) {
initInsn = (MethodInsnNode) insn;
break;
}
}
if (newInsn == null || initInsn == null) {
ASMHelper.logger.warn("NEI: CraftingManager recipe list not found, origins disabled");
return;
}
newInsn.desc = initInsn.owner = "codechicken/nei/recipe/debug/RecipeOrigins$TrackingList";
}
});
}
// put glint alpha into the buffer correctly for exporting
transformer.add(
new MethodReplacer(
@@ -452,8 +452,9 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (!this.isEmpty() && this.gridGenerator.maxAbsoluteSlotIndex > 0) {
final GroupingItem groupingItem = ItemPanels.bookmarkPanel.groupingItem;
final int sortableGroupId = ItemPanels.bookmarkPanel.sortableGroup != null
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
&& ItemPanels.bookmarkPanel.sortableGroup.grid == this
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
final int rowShift = this.page * this.rows;
Map<Integer, Integer> groups = this.gridGenerator.rowToGroupId;
@@ -795,10 +796,10 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
this.bookmarkItems.add(item);
}
sortableGroup.grid = this;
sortableGroup.groupId = groupId;
sortableGroup.update(this, groupId);
this.onItemsChanged();
this.getMask();// force generate grid
overGroupId = getRowGroupId(overRowIndex);
}
if (sortableGroup.groupId == overGroupId) {
@@ -1284,7 +1285,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarksGridSlot slot : getMask()) {
Rectangle4i rect = getSlotRect(slot.slotIndex);
if (sortableGroup != null && slot.getGroupId() == sortableGroup.groupId) {
if (sortableGroup != null && sortableGroup.matches(this, slot.getGroupId())) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
} else if (sortableItem != null && sortableItem.items.contains(slot.itemIndex)) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
@@ -1313,7 +1314,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
slot.afterDraw(getSlotRect(slot.slotIndex), mouseContext);
}
@@ -1330,7 +1331,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
final BookmarkItem item = slot.getBookmarkItem();
@@ -340,7 +340,8 @@ public class BookmarkGridGenerator {
0,
chainItem.getItem(),
null,
group);
group,
this.grid);
} else {
return new BookmarksGridSlot(
slotIndex,
@@ -350,7 +351,8 @@ public class BookmarkGridGenerator {
chainItem.getCalculatedMultiplier(),
chainItem.getItem(),
chainItem.calculatedType,
group);
group,
this.grid);
}
}
@@ -23,6 +23,7 @@ import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.util.NBTJson;
@@ -79,10 +80,21 @@ public class BookmarkStorage {
public int getNamespaceSize() {
if (this.namespaces.get(this.namespaces.size() - 1).size() > 0) {
this.namespaces.add(new BookmarkGrid());
} else if (this.activeNamespaceIndex == this.namespaces.size() - 2 && getActiveGrid().size() == 0) {
this.namespaces.remove(this.namespaces.size() - 1);
final int lastIndex = this.namespaces.size() - 1;
final BookmarkGrid lastGrid = this.namespaces.get(lastIndex);
final SortableGroup sortableGroup = ItemPanels.bookmarkPanel.sortableGroup;
if (lastGrid.size() > 0) {
if (sortableGroup == null || sortableGroup.grid != lastGrid || !sortableGroup.aloneInGrid) {
this.namespaces.add(new BookmarkGrid());
}
} else if (lastIndex > 0 && this.activeNamespaceIndex != lastIndex) {
final BookmarkGrid prevGrid = this.namespaces.get(lastIndex - 1);
if (sortableGroup != null && sortableGroup.grid == prevGrid && sortableGroup.aloneInGrid
|| prevGrid == getActiveGrid() && prevGrid.size() == 0) {
this.namespaces.remove(lastIndex);
}
}
return this.namespaces.size();
@@ -198,6 +210,11 @@ public class BookmarkStorage {
final BookmarkItem item = BookmarkItem.of(parser.parse(itemStr).getAsJsonObject());
if (item != null) {
if (!grid.groups.containsKey(item.groupId)) {
item.groupId = BookmarkGrid.DEFAULT_GROUP_ID;
}
grid.addItem(item, false);
} else {
wasError = true;
@@ -8,7 +8,6 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.NEIClientConfig;
@@ -65,7 +64,8 @@ public class BookmarksGridSlot extends ItemsGridSlot {
protected final int rowIndex;
public BookmarksGridSlot(int slotIndex, int itemIndex, long realMultiplier, long shiftAmount,
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) {
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group,
BookmarkGrid grid) {
super(
slotIndex,
itemIndex,
@@ -96,7 +96,6 @@ public class BookmarksGridSlot extends ItemsGridSlot {
: CalculatedType.RESULT;
}
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
final BookmarkItem prevItem = grid.getCalculatedItem(
grid.gridGenerator.slotToItem.getOrDefault(grid.gridGenerator.itemToSlot.get(this.itemIndex) - 1, -1));
this.isFirstOutput = this.itemIndex >= 0 && this.bookmarkItem.type == BookmarkItemType.RESULT
@@ -13,13 +13,23 @@ public class SortableGroup {
public int groupId;
public BookmarkGrid grid;
public boolean aloneInGrid;
public int shiftX = 0;
public int shiftY = 0;
public SortableGroup(BookmarkGrid grid, int groupId) {
update(grid, groupId);
}
public void update(BookmarkGrid grid, int groupId) {
this.grid = grid;
this.groupId = groupId;
this.aloneInGrid = grid.bookmarkItems.stream().allMatch(item -> item.groupId == groupId);
}
public boolean matches(BookmarkGrid grid, int groupId) {
return this.grid == grid && this.groupId == groupId;
}
public void postDraw(int mousex, int mousey) {
@@ -2,6 +2,8 @@ package codechicken.nei.bookmark.tree;
import java.awt.geom.Point2D;
import org.lwjgl.input.Mouse;
class Camera {
private static final float ZOOM_MIN = 0.1f;
@@ -39,23 +41,34 @@ class Camera {
&& y < this.widgetY + this.widgetH;
}
public void startDrag(int mouseX, int mouseY) {
public void startDrag(Point2D mouse) {
this.dragging = true;
this.lastDragX = mouseX;
this.lastDragY = mouseY;
this.lastDragX = (float) mouse.getX();
this.lastDragY = (float) mouse.getY();
}
public void drag(int mouseX, int mouseY) {
public void drag(Point2D mouse) {
if (!this.dragging) {
return;
}
final float mouseX = (float) mouse.getX();
final float mouseY = (float) mouse.getY();
this.scrollX -= (mouseX - this.lastDragX) / this.zoomLevel;
this.scrollY -= (mouseY - this.lastDragY) / this.zoomLevel;
this.lastDragX = mouseX;
this.lastDragY = mouseY;
}
public void update(Point2D mouse, int treeWidth, int treeHeight) {
if (this.dragging && Mouse.isButtonDown(0)) {
drag(mouse);
}
clampToBounds(treeWidth, treeHeight);
}
public void endDrag() {
this.dragging = false;
}
@@ -79,7 +92,6 @@ class Camera {
} else {
this.scrollY -= direction * this.widgetH / (SCROLL_STEP_DIVISOR * this.zoomLevel);
}
}
public void centerOn(float worldX, float worldY) {
@@ -169,12 +169,12 @@ class CraftingTreeCanvas {
: -FIT_MARGIN;
}
public void startDrag(int mouseX, int mouseY) {
this.camera.startDrag(mouseX, mouseY);
public void startDrag(Point2D mouse) {
this.camera.startDrag(mouse);
}
public void drag(int mouseX, int mouseY) {
this.camera.drag(mouseX, mouseY);
public void drag(Point2D mouse) {
this.camera.drag(mouse);
}
public void endDrag() {
@@ -259,7 +259,7 @@ class CraftingTreeCanvas {
public void draw(Point2D mouse, Set<BookmarkItem> collapsedItems, SearchResult searchResult,
boolean useInventorySnapshot, ItemTreeSlot hoveredNode, ItemTreeSlot deleteHoverNode) {
this.camera.clampToBounds(this.treeWidth, this.treeHeight);
this.camera.update(mouse, this.treeWidth, this.treeHeight);
final float zScrollX = this.camera.scrollX * this.camera.zoomLevel;
final float zScrollY = this.camera.scrollY * this.camera.zoomLevel;
@@ -102,20 +102,20 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
}
}
private static final int MARGIN = 4;
private static final int PADDING = 9;
private static final int PADDING_TOP = 22;
private static final DrawableResource BG_TEXTURE = new DrawableBuilder(
"nei:textures/gui/craftingtree.png",
0,
0,
64,
64).build();
64).setSlices(PADDING, PADDING, PADDING_TOP, PADDING).build();
private static final DateTimeFormatter SCREENSHOT_DATE_FORMAT = DateTimeFormatter
.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
private static final int MARGIN = 4;
private static final int PADDING = 9;
private static final int PADDING_TOP = 22;
private final LRUCache<String, String> handlerTitleCache = new LRUCache<>(100);
private GuiScreen prevGui;
@@ -162,7 +162,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
final ItemTreeSlot replaceNode = this.uiState.replaceRecipeNode;
this.uiState.replaceRecipeNode = null;
if (recipe != null) {
if (recipe != null && this.grid.getGroup(this.groupId) != null) {
final RecipeId recipeId = recipe.getRecipeId();
boolean changed = false;
@@ -190,6 +190,13 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
@Override
public void initGui() {
// the group can be removed from the bookmark panel while a recipe screen opened from this tree is shown
if (this.grid.getGroup(this.groupId) == null) {
this.mc.displayGuiScreen(this.prevGui);
return;
}
this.xSize = this.width;
this.ySize = this.height;
@@ -331,15 +338,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
this.hoveredNode = canHover && hoveredBadgeHandler == null ? this.canvas.getNodeMouseOver(getMousePosition())
: null;
BG_TEXTURE.draw(
MARGIN,
MARGIN,
this.width - MARGIN * 2,
this.height - MARGIN * 2,
PADDING,
PADDING,
PADDING_TOP,
PADDING);
BG_TEXTURE.draw(MARGIN, MARGIN, this.width - MARGIN * 2, this.height - MARGIN * 2);
if (this.math.outputRecipes.isEmpty()) {
drawCenteredString(
@@ -758,7 +757,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
return true;
}
this.canvas.startDrag(mouseX, mouseY);
this.canvas.startDrag(getMousePosition());
}
return false;
@@ -779,7 +778,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
}
this.canvas.drag(mouseX, mouseY);
this.canvas.drag(getMousePosition());
}
@Override
@@ -242,7 +242,7 @@ class StatsPanel extends ScrollContainer {
26,
84,
46,
16).build();
16).setSlices(0, 0, 2, 0).build();
private static final DrawableResource SLOT_DEFAULT = new DrawableBuilder(
"nei:textures/gui/craftingtree.png",
@@ -290,8 +290,8 @@ class StatsPanel extends ScrollContainer {
public StatsPanel() {
final ScrollBar scrollbar = new ScrollBar().setTrackWidth(4)
.setThumbTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 100, 24, 4).build(), 0, 0)
.setTrackTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 104, 24, 4).build(), 0, 0)
.setThumbTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 100, 24, 4).build())
.setTrackTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 104, 24, 4).build())
.setTrackPadding(1, -5, 1, 0).setOverflowType(ScrollBar.OverflowType.AUTO);
setHorizontalScroll(scrollbar);
@@ -589,7 +589,7 @@ class StatsPanel extends ScrollContainer {
RenderHelper.enableGUIStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
BG_TEXTURE.draw(this.x, this.y, this.w, this.h, 0, 0, 2, 0);
BG_TEXTURE.draw(this.x, this.y, this.w, this.h);
super.draw(mouseX, mouseY);
@@ -74,7 +74,11 @@ public class IMCHandler {
tag.getInteger("imageX"),
tag.getInteger("imageY"),
tag.getInteger("imageWidth"),
tag.getInteger("imageHeight"));
tag.getInteger("imageHeight"),
tag.hasKey("imageTextureWidth") ? tag.getInteger("imageTextureWidth")
: HandlerInfo.DEFAULT_IMAGE_TEXTURE_SIZE,
tag.hasKey("imageTextureHeight") ? tag.getInteger("imageTextureHeight")
: HandlerInfo.DEFAULT_IMAGE_TEXTURE_SIZE);
}
if (!info.hasImageOrItem()) {
final String itemName = tag.getString("itemName");
@@ -15,13 +15,21 @@ public class DrawableBuilder {
private int paddingBottom = 0;
private int paddingLeft = 0;
private int paddingRight = 0;
private int sliceLeft = 0;
private int sliceRight = 0;
private int sliceTop = 0;
private int sliceBottom = 0;
public DrawableBuilder(String resourceLocation, int u, int v, int width, int height) {
this(new ResourceLocation(resourceLocation), u, v, width, height);
}
public DrawableBuilder(ResourceLocation resource, int u, int v, int width, int height) {
this.u = u;
this.v = v;
this.width = width;
this.height = height;
this.resourceLocation = new ResourceLocation(resourceLocation);
this.resourceLocation = resource;
}
public DrawableBuilder setTextureSize(int width, int height) {
@@ -38,6 +46,14 @@ public class DrawableBuilder {
return this;
}
public DrawableBuilder setSlices(int sliceLeft, int sliceRight, int sliceTop, int sliceBottom) {
this.sliceLeft = sliceLeft;
this.sliceRight = sliceRight;
this.sliceTop = sliceTop;
this.sliceBottom = sliceBottom;
return this;
}
public DrawableBuilder trim(int trimTop, int trimBottom, int trimLeft, int trimRight) {
this.u += trimLeft;
this.v += trimTop;
@@ -58,6 +74,10 @@ public class DrawableBuilder {
paddingLeft,
paddingRight,
textureWidth,
textureHeight);
textureHeight,
sliceLeft,
sliceRight,
sliceTop,
sliceBottom);
}
}
@@ -1,7 +1,7 @@
package codechicken.nei.drawable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
@@ -19,9 +19,42 @@ public class DrawableResource extends Image {
private final int paddingLeft;
private final int paddingRight;
private final int sliceLeft;
private final int sliceRight;
private final int sliceTop;
private final int sliceBottom;
public DrawableResource(ResourceLocation resourceLocation, int u, int v, int width, int height, int paddingTop,
int paddingBottom, int paddingLeft, int paddingRight, int textureWidth, int textureHeight) {
this(
resourceLocation,
u,
v,
width,
height,
paddingTop,
paddingBottom,
paddingLeft,
paddingRight,
textureWidth,
textureHeight,
0,
0,
0,
0);
}
// negative padding lets the texture overflow the box it is drawn into
public DrawableResource(ResourceLocation resourceLocation, int u, int v, int width, int height, int paddingTop,
int paddingBottom, int paddingLeft, int paddingRight, int textureWidth, int textureHeight, int sliceLeft,
int sliceRight, int sliceTop, int sliceBottom) {
super(u, v, width, height);
this.sliceLeft = sliceLeft;
this.sliceRight = sliceRight;
this.sliceTop = sliceTop;
this.sliceBottom = sliceBottom;
this.resourceLocation = resourceLocation;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
@@ -40,6 +73,14 @@ public class DrawableResource extends Image {
return height + paddingTop + paddingBottom;
}
public int getTextureWidth() {
return this.textureWidth;
}
public int getTextureHeight() {
return this.textureHeight;
}
public void bindTexture() {
Minecraft.getMinecraft().getTextureManager().bindTexture(this.resourceLocation);
}
@@ -55,18 +96,28 @@ public class DrawableResource extends Image {
GL11.glEnable(GL11.GL_TEXTURE_2D);
bindTexture();
drawModalRectWithCustomSizedTexture(xOffset + maskLeft, yOffset + maskTop, maskLeft, maskTop, width, height);
final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
addQuad(tessellator, xOffset + maskLeft, yOffset + maskTop, maskLeft, maskTop, width, height);
tessellator.draw();
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
}
public void draw(int xOffset, int yOffset, int width, int height, int sliceLeft, int sliceRight, int sliceTop,
int sliceBottom) {
final int textureWidth = getWidth();
final int textureHeight = getHeight();
final int middleWidth = textureWidth - sliceLeft - sliceRight;
final int middleHeight = textureHeight - sliceTop - sliceBottom;
final int tileWidth = width - sliceLeft - sliceRight;
final int tileHeight = height - sliceTop - sliceBottom;
public void draw(int xOffset, int yOffset, int width, int height) {
if (width == getWidth() && height == getHeight()) {
draw(xOffset, yOffset);
return;
}
final int sliceLeft = this.sliceLeft;
final int sliceRight = this.sliceRight;
final int sliceTop = this.sliceTop;
final int sliceBottom = this.sliceBottom;
final int middleWidth = this.width - sliceLeft - sliceRight;
final int middleHeight = this.height - sliceTop - sliceBottom;
final int tileWidth = width - this.paddingLeft - this.paddingRight - sliceLeft - sliceRight;
final int tileHeight = height - this.paddingTop - this.paddingBottom - sliceTop - sliceBottom;
if (middleWidth > 0 && middleHeight > 0 && tileWidth > 0 && tileHeight > 0) {
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
@@ -78,14 +129,30 @@ public class DrawableResource extends Image {
GL11.glEnable(GL11.GL_TEXTURE_2D);
bindTexture();
// middle parts
final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
final int x = xOffset + sliceLeft + tileW * middleWidth;
// border top
addQuad(tessellator, x, yOffset, sliceLeft, 0, tileWidthSize, sliceTop);
// border bottom
addQuad(
tessellator,
x,
yOffset + sliceTop + tileHeight,
sliceLeft,
sliceTop + middleHeight,
tileWidthSize,
sliceBottom);
// middle parts
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
drawModalRectWithCustomSizedTexture(
addQuad(
tessellator,
x,
yOffset + sliceTop + tileH * middleHeight,
sliceLeft,
@@ -95,114 +162,75 @@ public class DrawableResource extends Image {
}
}
// border top
if (sliceTop > 0) {
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileW * middleWidth,
yOffset,
sliceLeft,
0,
tileWidthSize,
sliceTop);
}
}
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
final int y = yOffset + sliceTop + tileH * middleHeight;
// border bottom
if (sliceBottom > 0) {
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileW * middleWidth,
yOffset + sliceTop + tileHeight,
sliceLeft,
sliceTop + middleHeight,
tileWidthSize,
sliceBottom);
}
}
// border left
if (sliceLeft > 0) {
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
drawModalRectWithCustomSizedTexture(
xOffset,
yOffset + sliceTop + tileH * middleHeight,
0,
sliceTop,
sliceLeft,
tileHeightSize);
}
}
// border right
if (sliceRight > 0) {
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileWidth,
yOffset + sliceTop + tileH * middleHeight,
sliceLeft + middleWidth,
sliceTop,
sliceRight,
tileHeightSize);
}
// border left
addQuad(tessellator, xOffset, y, 0, sliceTop, sliceLeft, tileHeightSize);
// border right
addQuad(
tessellator,
xOffset + sliceLeft + tileWidth,
y,
sliceLeft + middleWidth,
sliceTop,
sliceRight,
tileHeightSize);
}
// angle left-top
if (sliceLeft > 0 && sliceTop > 0) {
drawModalRectWithCustomSizedTexture(xOffset, yOffset, 0, 0, sliceLeft, sliceTop);
}
addQuad(tessellator, xOffset, yOffset, 0, 0, sliceLeft, sliceTop);
// angle right-top
if (sliceRight > 0 && sliceTop > 0) {
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileWidth,
yOffset,
sliceLeft + middleWidth,
0,
sliceRight,
sliceTop);
}
addQuad(
tessellator,
xOffset + sliceLeft + tileWidth,
yOffset,
sliceLeft + middleWidth,
0,
sliceRight,
sliceTop);
// angle left-bottom
if (sliceLeft > 0 && sliceBottom > 0) {
drawModalRectWithCustomSizedTexture(
xOffset,
yOffset + sliceTop + tileHeight,
0,
sliceTop + middleHeight,
sliceLeft,
sliceBottom);
}
addQuad(
tessellator,
xOffset,
yOffset + sliceTop + tileHeight,
0,
sliceTop + middleHeight,
sliceLeft,
sliceBottom);
// angle right-bottom
if (sliceRight > 0 && sliceBottom > 0) {
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileWidth,
yOffset + sliceTop + tileHeight,
sliceLeft + middleWidth,
sliceTop + middleHeight,
sliceRight,
sliceBottom);
}
addQuad(
tessellator,
xOffset + sliceLeft + tileWidth,
yOffset + sliceTop + tileHeight,
sliceLeft + middleWidth,
sliceTop + middleHeight,
sliceRight,
sliceBottom);
tessellator.draw();
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
}
}
private void drawModalRectWithCustomSizedTexture(int xOffset, int yOffset, int u, int v, int width, int height) {
Gui.func_146110_a(
xOffset + this.paddingLeft,
yOffset + this.paddingTop,
this.x + u,
this.y + v,
width,
height,
this.textureWidth,
this.textureHeight);
private void addQuad(Tessellator tessellator, int xOffset, int yOffset, int u, int v, int width, int height) {
if (width <= 0 || height <= 0) return;
final float scaleU = 1.0F / this.textureWidth;
final float scaleV = 1.0F / this.textureHeight;
final int x = xOffset + this.paddingLeft;
final int y = yOffset + this.paddingTop;
final float u0 = (this.x + u) * scaleU;
final float u1 = (this.x + u + width) * scaleU;
final float v0 = (this.y + v) * scaleV;
final float v1 = (this.y + v + height) * scaleV;
tessellator.addVertexWithUV(x, y + height, 0.0D, u0, v1);
tessellator.addVertexWithUV(x + width, y + height, 0.0D, u1, v1);
tessellator.addVertexWithUV(x + width, y, 0.0D, u1, v0);
tessellator.addVertexWithUV(x, y, 0.0D, u0, v0);
}
}
@@ -5,13 +5,18 @@ import java.util.List;
import java.util.Map;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.inventory.SlotCrafting;
import net.minecraft.item.ItemStack;
import codechicken.nei.FastTransferManager;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemStackAmount;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.NEIServerUtils;
import codechicken.nei.RestartableTask;
import codechicken.nei.ServerSync;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.chain.RecipeChainIterator;
@@ -26,6 +31,7 @@ public class AutoCraftingManager {
@Override
public void execute() {
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
final ItemStack[] layout = snapshotLayout(guiContainer);
final List<BookmarkItem> initialItems = prepareInitialItems(math, getInventoryItems(guiContainer));
boolean processed = false;
boolean changed = false;
@@ -69,6 +75,11 @@ public class AutoCraftingManager {
} while (changed && !interrupted(guiContainer));
if (!interrupted(guiContainer)) {
restoreLayout(guiContainer, layout);
ServerSync.request();
}
} finally {
StackInfo.pauseItemDamageSound(false);
}
@@ -134,4 +145,70 @@ public class AutoCraftingManager {
return inventory;
}
private static ItemStack[] snapshotLayout(GuiContainer guiContainer) {
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final ItemStack[] layout = new ItemStack[slots.size()];
for (int index = 0; index < slots.size(); index++) {
final Slot slot = slots.get(index);
if (slot.getHasStack() && !(slot instanceof SlotCrafting)) {
layout[index] = slot.getStack().copy();
}
}
return layout;
}
private static void restoreLayout(GuiContainer guiContainer, ItemStack[] layout) {
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final EntityPlayer player = guiContainer.mc.thePlayer;
for (int index = 0; index < slots.size() && player.inventory.getItemStack() == null
&& guiContainer.mc.currentScreen == guiContainer; index++) {
final ItemStack expected = layout[index];
final Slot target = slots.get(index);
if (expected == null || inPlace(slots, layout, index)
|| !target.isItemValid(expected)
|| target.getHasStack() && !target.canTakeStack(player)) {
continue;
}
final Slot source = findMisplaced(slots, layout, expected, player);
if (source == null) continue;
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
FastTransferManager.clickSlot(guiContainer, target.slotNumber);
if (player.inventory.getItemStack() != null) {
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
}
}
FastTransferManager.dropHeldItem(guiContainer);
}
private static boolean inPlace(List<Slot> slots, ItemStack[] layout, int index) {
final ItemStack stack = slots.get(index).getStack();
return stack != null && layout[index] != null
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(layout[index], stack);
}
private static Slot findMisplaced(List<Slot> slots, ItemStack[] layout, ItemStack expected, EntityPlayer player) {
for (int index = 0; index < slots.size(); index++) {
final Slot slot = slots.get(index);
if (slot.getHasStack() && !(slot instanceof SlotCrafting)
&& !inPlace(slots, layout, index)
&& slot.canTakeStack(player)
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(expected, slot.getStack())) {
return slot;
}
}
return null;
}
}
@@ -1,6 +1,7 @@
package codechicken.nei.recipe;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -20,10 +21,13 @@ import codechicken.lib.inventory.InventoryUtils;
import codechicken.nei.FastTransferManager;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.ServerSync;
import codechicken.nei.api.IOverlayHandler;
public class DefaultOverlayHandler implements IOverlayHandler {
private static final long SYNC_TIMEOUT_MILLIS = 5000;
public static class DistributedIngred {
public DistributedIngred(ItemStack item) {
@@ -113,7 +117,8 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
boolean craft = false;
while (multiplier > 0) {
while (multiplier > 0 && firstGui.mc.currentScreen == firstGui
&& presenceOverlay(firstGui, handler, recipeIndex).stream().allMatch(s -> s.isPresent())) {
final int transfer = transferRecipe(firstGui, handler, recipeIndex, multiplier);
if (transfer <= 0) {
@@ -122,6 +127,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
multiplier -= transfer;
if (!craftingSlot.getHasStack() && !awaitServer(firstGui)) {
break;
}
if (craftingSlot.getHasStack() && craftingSlot.canTakeStack(firstGui.mc.thePlayer)) {
FastTransferManager.clickSlot(firstGui, craftingSlot.slotNumber, 0, 1);
@@ -139,9 +148,30 @@ public class DefaultOverlayHandler implements IOverlayHandler {
return craft;
}
private boolean awaitServer(GuiContainer gui) {
final long deadline = System.currentTimeMillis() + SYNC_TIMEOUT_MILLIS;
ServerSync.request();
while (ServerSync.pending()) {
if (gui.mc.currentScreen != gui || System.currentTimeMillis() > deadline) {
return false;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
return false;
}
}
return true;
}
protected boolean clearIngredients(GuiContainer gui, IRecipeHandler handler) {
for (Slot slot : getCraftMatrixSlots(gui, handler)) {
if (gui.mc.currentScreen != gui) return false;
if (slot.canTakeStack(gui.mc.thePlayer)) {
FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1);
if (slot.getHasStack()) return false;
@@ -206,7 +236,9 @@ public class DefaultOverlayHandler implements IOverlayHandler {
protected void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
final Slot craftingSlot = getCraftingResultSlot(gui);
for (Slot slot : gui.inventorySlots.inventorySlots) {
for (Slot slot : getSourceSlots(gui)) {
if (gui.mc.currentScreen != gui) return;
if (slot == craftingSlot || !slot.getHasStack()
|| !canMoveFrom(slot, gui)
|| !slot.canTakeStack(gui.mc.thePlayer))
@@ -240,6 +272,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
FastTransferManager.clickSlot(gui, slot.slotNumber);
}
// A right click with an empty or foreign cursor would take half of the dest stack
final ItemStack held = gui.mc.thePlayer.inventory.getItemStack();
if (held == null || !canStack(distrib.permutation, held)) break;
FastTransferManager.clickSlot(gui, dest.slotNumber, 1);
stackSize--;
}
@@ -370,7 +406,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
protected void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
for (Slot slot : gui.inventorySlots.inventorySlots) /* work out how much we have to go round */ {
for (Slot slot : getSourceSlots(gui)) /* work out how much we have to go round */ {
if (slot.getHasStack() && canMoveFrom(slot, gui)) {
final ItemStack pstack = slot.getStack();
final DistributedIngred istack = findIngred(ingredStacks, pstack);
@@ -408,6 +444,25 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
}
protected List<Slot> getSourceSlots(GuiContainer gui) {
final List<Slot> slots = new ArrayList<>(gui.inventorySlots.inventorySlots);
slots.sort(Comparator.comparingLong(slot -> getRemainingDurability(slot.getStack())));
return slots;
}
private static long getRemainingDurability(ItemStack stack) {
if (stack == null || stack.getMaxStackSize() != 1) return Long.MAX_VALUE;
final NBTTagCompound tagCompound = stack.getTagCompound();
if (tagCompound != null && tagCompound.hasKey("GT.ToolStats")) {
final NBTTagCompound toolStats = tagCompound.getCompoundTag("GT.ToolStats");
return toolStats.getLong("MaxDamage") - toolStats.getLong("Damage");
}
return stack.isItemStackDamageable() ? stack.getMaxDamage() - stack.getItemDamage() : Long.MAX_VALUE;
}
private int getGTToolDamagePerContainerCraft(ItemStack aStack) {
try {
@@ -1,6 +1,5 @@
package codechicken.nei.recipe;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -13,14 +12,9 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
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.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.recipe.Recipe.RecipeId;
public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
@@ -138,39 +132,8 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
}
public static RecipeId getRecipeId(GuiScreen gui, ItemStack stackover) {
if (gui instanceof GuiRecipe<?>guiRecipe) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
if (focusedRecipe != null) {
return focusedRecipe.getRecipeId();
}
}
final Point mouseover = GuiDraw.getMousePosition();
final BookmarksGridSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
if (panelSlot != null) {
return panelSlot.getType() == BookmarkItemType.INGREDIENT || panelSlot.getRecipeId() == null
? FavoriteRecipes.getFavorite(stackover)
: panelSlot.getRecipeId();
}
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseover.x, mouseover.y);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mouseover.x, mouseover.y);
}
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mouseover.x, mouseover.y);
}
if (itemSlot != null) {
return itemSlot.getRecipeId();
}
return null;
final HoveredRecipe hovered = HoveredRecipe.of(gui, stackover);
return hovered != null ? hovered.recipeId : null;
}
private GuiCraftingRecipe(ArrayList<ICraftingHandler> handlers) {
@@ -177,12 +177,20 @@ public class GuiOverlayButton extends GuiRecipeButton {
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGrid"));
hotkeys.put(
NEIClientUtils.getKeyName(
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH,
NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGridOnce"));
}
} else if (canFillCraftingGrid()) {
hotkeys.put(
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGrid"));
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGridOnce"));
}
hotkeys.put(
@@ -263,7 +271,7 @@ public class GuiOverlayButton extends GuiRecipeButton {
if (!items.isEmpty()) {
this.missedMaterialsTooltipLineHandler = ItemsTooltipLineHandler
.grid(NEIClientUtils.translate("recipe.overlay.missing"), items, Integer.MAX_VALUE);
.of(NEIClientUtils.translate("recipe.overlay.missing"), items, Integer.MAX_VALUE, 0);
} else {
this.missedMaterialsTooltipLineHandler = null;
}
@@ -329,7 +337,7 @@ public class GuiOverlayButton extends GuiRecipeButton {
if (!requireShiftForOverlayRecipe() || shift) {
if (this.canFillCraftingGrid) {
this.handlerRef.fillCraftingGrid(this.firstGui, 0);
this.handlerRef.fillCraftingGrid(this.firstGui, NEIClientUtils.controlKey() ? 1 : 0);
}
} else if (hasOverlay() && useOverlayRenderer()) {
this.handlerRef.useOverlayRenderer(this.firstGui);
@@ -71,15 +71,23 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
0,
0,
176 + TRANSPARENCY_BORDER * 2,
166 + TRANSPARENCY_BORDER * 2).build();
166 + TRANSPARENCY_BORDER * 2)
.setSlices(
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER)
.build();
private static final int BUTTON_WIDTH = 13;
private static final int BUTTON_HEIGHT = 12;
protected static final ScrollBar VERTICAL_SCROLLBAR = new ScrollBar().setTrackWidth(14)
.setOverflowType(OverflowType.AUTO).setScrollPlace(ScrollPlace.END)
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).build(), 9, 9)
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).build(), 3, 2)
.setTrackTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).setSlices(0, 0, 9, 9).build())
.setThumbTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).setSlices(0, 0, 3, 2).build())
.setThumbPadding(1, 6, 0, 6);
public static final List<IRecipeFilterProvider> recipeFilterers = new LinkedList<>();
@@ -958,11 +966,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.guiLeft - TRANSPARENCY_BORDER,
this.guiTop - TRANSPARENCY_BORDER,
this.xSize + TRANSPARENCY_BORDER * 2,
this.ySize + TRANSPARENCY_BORDER * 2,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER);
this.ySize + TRANSPARENCY_BORDER * 2);
drawJEITabs(mouseX, mouseY);
}
@@ -55,11 +55,17 @@ public class GuiRecipeCatalyst extends ScrollContainer {
0,
0,
28 + TRANSPARENCY_BORDER * 2,
28 + TRANSPARENCY_BORDER * 2).setTextureSize(28 + TRANSPARENCY_BORDER * 2, 28 + TRANSPARENCY_BORDER * 2)
28 + TRANSPARENCY_BORDER * 2)
.setTextureSize(28 + TRANSPARENCY_BORDER * 2, 28 + TRANSPARENCY_BORDER * 2)
.setSlices(
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER)
.build();
private static final DrawableResource FG_TEXTURE = new DrawableBuilder("nei:textures/slot.png", 0, 0, 18, 18)
.setTextureSize(18, 18).build();
.setTextureSize(18, 18).setSlices(1, 1, 1, 1).build();
private static final ScrollBar VERTICAL_SCROLLBAR = ScrollBar.defaultVerticalBar()
.setOverflowType(ScrollBar.OverflowType.AUTO).setTrackPadding(0, BORDER_PADDING, -13, BORDER_PADDING)
@@ -155,20 +161,12 @@ public class GuiRecipeCatalyst extends ScrollContainer {
this.x - TRANSPARENCY_BORDER,
this.y - TRANSPARENCY_BORDER,
this.w + TRANSPARENCY_BORDER * 2,
this.h + TRANSPARENCY_BORDER * 2,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER);
this.h + TRANSPARENCY_BORDER * 2);
FG_TEXTURE.draw(
this.x + this.paddingInlineStart - 1,
this.y + this.paddingBlockStart - 1,
this.w - this.paddingInlineStart - this.paddingInlineEnd + 2,
this.h - this.paddingBlockStart - this.paddingBlockEnd + 2,
1,
1,
1,
1);
this.h - this.paddingBlockStart - this.paddingBlockEnd + 2);
super.draw(mx, my);
}
@@ -12,6 +12,7 @@ public class HandlerInfo {
public static int DEFAULT_HEIGHT = 65;
public static int DEFAULT_WIDTH = 166;
public static final int DEFAULT_IMAGE_TEXTURE_SIZE = 256;
@Deprecated
public static int DEFAULT_MAX_PER_PAGE = 1;
@@ -70,9 +71,22 @@ public class HandlerInfo {
}
public boolean setImage(String resourceLocation, int imageX, int imageY, int imageWidth, int imageHeight) {
return setImage(
resourceLocation,
imageX,
imageY,
imageWidth,
imageHeight,
DEFAULT_IMAGE_TEXTURE_SIZE,
DEFAULT_IMAGE_TEXTURE_SIZE);
}
public boolean setImage(String resourceLocation, int imageX, int imageY, int imageWidth, int imageHeight,
int textureWidth, int textureHeight) {
if (hasImageOrItem()) return false;
this.image = new DrawableBuilder(resourceLocation, imageX, imageY, imageWidth, imageHeight).build();
this.image = new DrawableBuilder(resourceLocation, imageX, imageY, imageWidth, imageHeight)
.setTextureSize(textureWidth, textureHeight).build();
return true;
}
@@ -211,6 +225,20 @@ public class HandlerInfo {
return this;
}
public Builder setDisplayImage(ResourceLocation location, int imageX, int imageY, int imageWidth,
int imageHeight, int textureWidth, int textureHeight) {
this.info.itemStack = null;
this.info.setImage(
location.toString(),
imageX,
imageY,
imageWidth,
imageHeight,
textureWidth,
textureHeight);
return this;
}
@Deprecated
public Builder setUseCustomScroll(boolean useCustomScroll) {
this.info.setUseCustomScroll(useCustomScroll);
@@ -0,0 +1,87 @@
package codechicken.nei.recipe;
import java.awt.Point;
import net.minecraft.client.gui.GuiScreen;
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.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.recipe.Recipe.RecipeId;
public final class HoveredRecipe {
public enum Source {
RECIPE_GUI,
BOOKMARK,
ITEM_PANEL,
OTHER
}
public final RecipeId recipeId;
public final Source source;
public final boolean favorite;
private HoveredRecipe(RecipeId recipeId, Source source, boolean favorite) {
this.recipeId = recipeId;
this.source = source;
this.favorite = favorite;
}
public static HoveredRecipe of(GuiScreen gui, ItemStack stackover) {
if (gui instanceof GuiRecipe<?>guiRecipe) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
if (focusedRecipe != null) {
return new HoveredRecipe(focusedRecipe.getRecipeId(), Source.RECIPE_GUI, false);
}
}
final Point mouseover = GuiDraw.getMousePosition();
final BookmarksGridSlot bookmarkSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
if (bookmarkSlot != null) {
if (bookmarkSlot.getType() != BookmarkItemType.INGREDIENT && bookmarkSlot.getRecipeId() != null) {
return new HoveredRecipe(bookmarkSlot.getRecipeId(), Source.BOOKMARK, false);
}
return favorite(stackover, Source.BOOKMARK);
}
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseover.x, mouseover.y);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mouseover.x, mouseover.y);
}
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mouseover.x, mouseover.y);
}
if (itemSlot != null) {
if (itemSlot.getRecipeId() != null) {
return new HoveredRecipe(itemSlot.getRecipeId(), Source.ITEM_PANEL, false);
}
return favorite(stackover, Source.ITEM_PANEL);
}
if (gui instanceof GuiRecipe<?>guiRecipe && guiRecipe.getSlotAtPosition(mouseover.x, mouseover.y) != null) {
return favorite(stackover, Source.RECIPE_GUI);
}
return favorite(stackover, Source.OTHER);
}
private static HoveredRecipe favorite(ItemStack stackover, Source source) {
final RecipeId recipeId = FavoriteRecipes.getFavorite(stackover);
return recipeId != null ? new HoveredRecipe(recipeId, source, true) : null;
}
}
@@ -1,5 +1,6 @@
package codechicken.nei.recipe;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
@@ -167,6 +168,8 @@ public class RecipeHandlerRef {
return transferAlghoritmCache.computeIfAbsent(overlayHandler.getClass(), clazz -> {
try {
final Method transferRecipe = overlayHandler.getClass()
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class);
if (overlayHandler instanceof DefaultOverlayHandler) {
final Class<?> overlayRecipeClazz = overlayHandler.getClass()
@@ -178,14 +181,11 @@ public class RecipeHandlerRef {
boolean.class)
.getDeclaringClass();
if (overlayRecipeClazz.equals(DefaultOverlayHandler.class)) {
return true;
}
return overlayRecipeClazz.equals(DefaultOverlayHandler.class)
|| !transferRecipe.getDeclaringClass().equals(DefaultOverlayHandler.class);
}
return !overlayHandler.getClass()
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class)
.isDefault();
return !transferRecipe.isDefault();
} catch (NoSuchMethodException e) {}
return false;
@@ -7,14 +7,10 @@ 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.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.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
@@ -58,7 +54,9 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
List<String> currenttip) {
if (itemstack != null) {
final RecipeId recipeId = getHoveredRecipeId(gui, itemstack, mousex, mousey);
final HoveredRecipe hovered = HoveredRecipe.of(gui, itemstack);
final RecipeId recipeId = hovered != null && showRecipeTooltip(hovered, mousex, mousey) ? hovered.recipeId
: null;
if (recipeId == null) {
recipeTooltipLineHandler = null;
@@ -76,49 +74,21 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
return currenttip;
}
private RecipeId getHoveredRecipeId(GuiContainer gui, ItemStack itemstack, int mousex, int mousey) {
private boolean showRecipeTooltip(HoveredRecipe hovered, int mousex, int mousey) {
return switch (hovered.source) {
case BOOKMARK -> !hovered.favorite && showRecipeTooltipInBookmarks(mousex, mousey);
case ITEM_PANEL -> NEIClientConfig.showRecipeTooltipInPanel();
case RECIPE_GUI -> hovered.favorite && NEIClientConfig.showRecipeTooltipInGui();
default -> false;
};
}
if (ItemPanels.bookmarkPanel.contains(mousex, mousey)) {
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
if (slot != null && slot.getRecipeId() != null
&& slot.getType() != BookmarkItemType.INGREDIENT
&& NEIClientConfig.getRecipeTooltipsMode() != 0) {
final int tooltipMode = NEIClientConfig.getRecipeTooltipsMode();
final BookmarkViewMode viewMode = slot.getGroup().viewMode;
if (tooltipMode == 3 || viewMode == BookmarkViewMode.DEFAULT && tooltipMode == 1
|| viewMode == BookmarkViewMode.TODO_LIST && tooltipMode == 2) {
return slot.getRecipeId();
}
}
return 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
&& new Rectangle4i(gui.guiLeft, gui.guiTop, gui.xSize, gui.ySize).contains(mousex, mousey)
&& NEIClientConfig.showRecipeTooltipInGui()
&& guiRecipe.getFocusedRecipe() == null) {
return FavoriteRecipes.getFavorite(itemstack);
}
return null;
private boolean showRecipeTooltipInBookmarks(int mousex, int mousey) {
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
final int tooltipMode = NEIClientConfig.getRecipeTooltipsMode();
final BookmarkViewMode viewMode = slot.getGroup().viewMode;
return tooltipMode == 3 || viewMode == BookmarkViewMode.DEFAULT && tooltipMode == 1
|| viewMode == BookmarkViewMode.TODO_LIST && tooltipMode == 2;
}
@Override
@@ -21,6 +21,7 @@ import codechicken.nei.ItemStackAmount;
import codechicken.nei.ItemsTooltipLineHandler;
import codechicken.nei.ItemsTooltipLineHandler.StacksAmountRenderer;
import codechicken.nei.ItemsTooltipLineHandler.TotalAmountRenderer;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IGuiContainerOverlay;
@@ -163,9 +164,9 @@ public class RecipeItemsTooltipLineHandler extends CompositeTooltipLineHandler {
protected void addResultLine(List<ItemStack> items, Set<NBTTagCompound> approximate) {
final String label = NEIClientUtils.translate("recipe.items.results");
final ItemsTooltipLineHandler line = items.size() == 1
? ItemsTooltipLineHandler.list(label, items, maxLineRows())
: ItemsTooltipLineHandler.grid(label, items, maxLineRows());
final int maxRows = maxLineRows();
final int maxNamedRows = items.size() == 1 ? NEIClientConfig.getMaxVerticalTooltipRows() : 0;
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxRows, maxNamedRows);
if (line.isEmpty()) {
return;
@@ -188,7 +189,9 @@ public class RecipeItemsTooltipLineHandler extends CompositeTooltipLineHandler {
protected void addRequiredLine(List<ItemStack> items, Set<NBTTagCompound> approximate) {
final String label = NEIClientUtils.translate("recipe.items.ingredients");
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.list(label, items, maxLineRows());
final int maxRows = maxLineRows();
final int maxNamedRows = NEIClientConfig.getMaxVerticalTooltipRows();
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxRows, maxNamedRows);
if (line.isEmpty()) {
return;
@@ -211,8 +214,9 @@ public class RecipeItemsTooltipLineHandler extends CompositeTooltipLineHandler {
protected void addNeededLine(List<ItemStack> items, Set<NBTTagCompound> approximate, boolean vertical) {
final String label = NEIClientUtils.translate("recipe.items.ingredients_needed");
final ItemsTooltipLineHandler line = vertical ? ItemsTooltipLineHandler.list(label, items, maxLineRows())
: ItemsTooltipLineHandler.grid(label, items, maxLineRows());
final int maxRows = maxLineRows();
final int maxNamedRows = vertical ? NEIClientConfig.getMaxVerticalTooltipRows() : 0;
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxRows, maxNamedRows);
if (line.isEmpty()) {
return;
@@ -25,7 +25,13 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
0,
0,
176 + TRANSPARENCY_BORDER * 2,
166 + TRANSPARENCY_BORDER * 2).build();
166 + TRANSPARENCY_BORDER * 2)
.setSlices(
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER)
.build();
protected long lastUpdate = System.currentTimeMillis();
protected String recipeName = "";
@@ -105,11 +111,7 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
-TRANSPARENCY_BORDER,
-TRANSPARENCY_BORDER,
size.width + TRANSPARENCY_BORDER * 2,
size.height + TRANSPARENCY_BORDER * 2,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER);
size.height + TRANSPARENCY_BORDER * 2);
GL11.glEnable(GL11.GL_ALPHA_TEST);
@@ -28,6 +28,10 @@ import codechicken.nei.util.NBTHelper;
public class StackInfo {
/// Prefix of the MaterialLib reference form `ml:<MaterialName>:<shapeToken>`, which config entries carry in
/// place of an id and metadata.
public static final String MATERIALLIB_PREFIX = "ml:";
private static final FluidStack NULL_FLUID = new FluidStack(FluidRegistry.WATER, 0);
public static final List<IStackStringifyHandler> stackStringifyHandlers = new ArrayList<>();
private static final Map<String, HashMap<String, String[]>> guidfilters = new HashMap<>();
@@ -25,6 +25,7 @@ import codechicken.nei.ItemsTooltipLineHandler;
import codechicken.nei.ItemsTooltipLineHandler.AmountRenderer;
import codechicken.nei.ItemsTooltipLineHandler.StacksAmountRenderer;
import codechicken.nei.ItemsTooltipLineHandler.TotalAmountRenderer;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.drawable.DrawableResource;
@@ -354,8 +355,8 @@ public class RecipeChainTooltipLineHandler extends CompositeTooltipLineHandler {
protected void addLine(String label, List<ItemStack> items, boolean vertical, EnumChatFormatting labelColor,
AmountRenderer amountRenderer) {
final ItemsTooltipLineHandler line = vertical ? ItemsTooltipLineHandler.list(label, items, maxLineRows())
: ItemsTooltipLineHandler.grid(label, items, maxLineRows());
final int maxNamedRows = vertical ? NEIClientConfig.getMaxVerticalTooltipRows() : 0;
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxLineRows(), maxNamedRows);
if (line.isEmpty()) {
return;
@@ -25,6 +25,7 @@ public class CheckboxWidget extends Button implements IUpdatableWidget {
@Override
public boolean onButtonPress(boolean rightclick) {
if (this.state == 2) return false;
this.value = !this.value;
onChange.accept(this);
return true;
@@ -30,19 +30,24 @@ import codechicken.nei.ClientHandler;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.Widget;
import codechicken.nei.api.API;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiRecipeButton;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.HandlerInfo;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.ShapedRecipeHandler;
import codechicken.nei.recipe.TemplateRecipeHandler;
import codechicken.nei.recipe.TemplateRecipeHandler.RecipeTransferRect;
import codechicken.nei.recipe.widget.RecipeWidget;
import codechicken.nei.scroll.ScrollContainer;
public class DebugHandlerWidget extends Widget {
public class DebugHandlerWidget extends Widget implements IContainerTooltipHandler {
private static class HandlerInfoRecord {
@@ -152,6 +157,9 @@ public class DebugHandlerWidget extends Widget {
private Map<String, HandlerInfoRecord> patches = new HashMap<>();
private HandlerInfoRecord record;
private RecipeWidget hoveredRecipe;
private boolean duplicatesOnly = false;
private String duplicatesHandlerId;
private Point dragPoint = null;
private boolean showWidget = false;
@@ -162,11 +170,19 @@ public class DebugHandlerWidget extends Widget {
0,
0,
176 + TRANSPARENCY_BORDER * 2,
166 + TRANSPARENCY_BORDER * 2).build();
166 + TRANSPARENCY_BORDER * 2)
.setSlices(
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER)
.build();
private static final int BORDER_PADDING = 6;
private static final int TRANSPARENCY_BORDER = 4;
public DebugHandlerWidget() {
protected DebugHandlerWidget() {
GuiContainerManager.addTooltipHandler(this);
this.w = WINDOW_WIDTH;
this.x = 10;
this.y = 50;
@@ -242,6 +258,20 @@ public class DebugHandlerWidget extends Widget {
saveHandlerInfoPatch();
}, y);
if (Boolean.getBoolean("nei.recipeOrigins")) {
y = addCheckboxWidget("DuplicatesOnly", button -> {
this.duplicatesOnly = button.value;
if (NEIClientUtils.getGuiContainer() instanceof GuiRecipe<?>recipe) {
this.duplicatesHandlerId = getHandlerID(recipe.getHandler());
recipe.forceRefreshPage();
}
}, y + LINE_HEIGHT / 2);
// only the handler the checkbox was toggled on is filtered, so switching handlers does not pay for a rescan
API.addRecipeFilter(() -> this.duplicatesOnly ? this::isDuplicate : null);
}
this.h = 10 + LINE_HEIGHT + y;
this.container.h = this.h - INLINE_PADDING * 2 - LINE_HEIGHT;
}
@@ -355,11 +385,7 @@ public class DebugHandlerWidget extends Widget {
this.x - TRANSPARENCY_BORDER,
this.y - TRANSPARENCY_BORDER,
this.w + TRANSPARENCY_BORDER * 2,
this.h + TRANSPARENCY_BORDER * 2,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER);
this.h + TRANSPARENCY_BORDER * 2);
GuiDraw.drawRect(
this.x + INLINE_PADDING,
this.y + BORDER_PADDING,
@@ -417,6 +443,11 @@ public class DebugHandlerWidget extends Widget {
updateOverride();
if (this.values.get("duplicatesonly") instanceof CheckboxWidget duplicates) {
duplicates.state = handler instanceof ShapedRecipeHandler ? 0 : 2;
resetDuplicatesOnly();
}
this.container.removeIf(
widget -> this.values.get("height") == widget || this.values.get("height_readonly") == widget);
if (handler.getRecipeHeight(0) > 0) {
@@ -430,11 +461,26 @@ public class DebugHandlerWidget extends Widget {
} else {
this.record = null;
this.showWidget = false;
resetDuplicatesOnly();
}
this.container.update();
}
private void resetDuplicatesOnly() {
if (!this.duplicatesOnly) return;
this.duplicatesOnly = false;
if (this.values.get("duplicatesonly") instanceof CheckboxWidget duplicates) {
duplicates.updateValue("false");
}
if (NEIClientUtils.getGuiContainer() instanceof GuiRecipe<?>recipe) {
recipe.forceRefreshPage();
}
}
@Override
public List<String> handleTooltip(int mx, int my, List<String> tooltip) {
return this.record != null ? this.container.handleTooltip(mx, my, tooltip) : tooltip;
@@ -532,9 +578,51 @@ public class DebugHandlerWidget extends Widget {
}
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> tooltip) {
final RecipeWidget widget = this.hoveredRecipe;
this.hoveredRecipe = null;
if (widget == null || !this.showWidget || !tooltip.isEmpty()) {
return tooltip;
}
final RecipeHandlerRef ref = widget.getRecipeHandlerRef();
for (List<String> origin : RecipeOrigins.findOrigins(ref.handler, ref.recipeIndex)) {
addOriginLines(tooltip, origin);
}
return tooltip;
}
private static void addOriginLines(List<String> tooltip, List<String> lines) {
tooltip.add(
EnumChatFormatting.WHITE + NEIClientUtils.translate("debug.RecipeHandler.RegisteredBy", lines.get(0))
+ GuiDraw.TOOLTIP_LINESPACE);
for (int i = 1; i < lines.size(); i++) {
tooltip.add(EnumChatFormatting.GRAY + lines.get(i));
}
tooltip.add("");
}
// only the handler the checkbox was toggled on is filtered
private boolean isDuplicate(IRecipeHandler handler, int recipeIndex) {
return !getHandlerID(handler).equals(this.duplicatesHandlerId)
|| RecipeOrigins.hasDuplicates(handler, recipeIndex);
}
public void drawGuiPlaceholder(RecipeWidget widget) {
if (!this.showWidget) return;
final Point mouse = GuiDraw.getMousePosition();
if (widget.getRecipeHandlerRef().handler instanceof ShapedRecipeHandler && widget.contains(mouse.x, mouse.y)
&& widget.getStackMouseOver(mouse.x, mouse.y) == null) {
this.hoveredRecipe = widget;
}
NEIClientUtils.gl2DRenderContext(() -> {
// background
@@ -0,0 +1,317 @@
package codechicken.nei.recipe.debug;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.item.crafting.ShapelessRecipes;
import net.minecraftforge.oredict.ShapedOreRecipe;
import net.minecraftforge.oredict.ShapelessOreRecipe;
import codechicken.nei.NEIServerUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.ShapedRecipeHandler;
import codechicken.nei.recipe.ShapedRecipeHandler.CachedShapedRecipe;
import codechicken.nei.recipe.TemplateRecipeHandler.CachedRecipe;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
/**
* Tracks which mod registered each crafting recipe, so the recipe handler debug widget can show it in the recipe
* tooltip. {@link TrackingList} is spliced in as the type of {@link CraftingManager}'s internal recipe list by
* {@link codechicken.nei.asm.NEITransformer} (see the {@code <init>} transform there) in place of the plain
* {@code ArrayList} vanilla constructs it with. Patching the constructor itself, rather than swapping the field later
* on, means every single recipe is observed, including vanilla's own, which are added by that same constructor before
* any mod code ever runs. Opt-in via {@code -Dnei.recipeOrigins=true}, since capturing a stack trace per recipe slows
* down loading.
*/
public class RecipeOrigins {
private static final int MAX_FRAMES = 10;
private static final Map<IRecipe, List<String>> origins = Collections.synchronizedMap(new IdentityHashMap<>());
private static class OutputIndex {
final int size;
final Map<Item, List<IRecipe>> byItem;
OutputIndex(int size, Map<Item, List<IRecipe>> byItem) {
this.size = size;
this.byItem = byItem;
}
}
private static final Map<CachedRecipe, List<List<String>>> cache = new WeakHashMap<>();
private static volatile OutputIndex outputIndex = new OutputIndex(-1, Collections.emptyMap());
private RecipeOrigins() {}
public static List<List<String>> findOrigins(IRecipeHandler handler, int recipeIndex) {
if (!(handler instanceof ShapedRecipeHandler shaped)) return Collections.emptyList();
final CachedRecipe recipe = shaped.arecipes.get(recipeIndex);
List<List<String>> found;
synchronized (cache) {
found = cache.get(recipe);
}
if (found == null) {
found = findOrigins(shaped, recipe.getResult(), recipe.getIngredients());
synchronized (cache) {
cache.put(recipe, found);
}
}
return found;
}
public static boolean hasDuplicates(IRecipeHandler handler, int recipeIndex) {
return handler instanceof ShapedRecipeHandler shaped
&& hasDuplicateCandidates(shaped.arecipes.get(recipeIndex).getResult())
&& findOrigins(handler, recipeIndex).size() > 1;
}
private static boolean hasDuplicateCandidates(PositionedStack result) {
if (origins.isEmpty() || result == null || result.item == null) return false;
int count = 0;
for (IRecipe recipe : getRecipesByOutput(result.item.getItem())) {
if (origins.containsKey(recipe)
&& NEIServerUtils.areStacksSameTypeCrafting(recipe.getRecipeOutput(), result.item)
&& ++count > 1) {
return true;
}
}
return false;
}
private static List<List<String>> findOrigins(ShapedRecipeHandler handler, PositionedStack result,
List<PositionedStack> ingredients) {
if (origins.isEmpty() || result == null || result.item == null) return Collections.emptyList();
final List<List<String>> found = new ArrayList<>();
for (IRecipe recipe : getRecipesByOutput(result.item.getItem())) {
final List<String> origin = origins.get(recipe);
if (origin != null && NEIServerUtils.areStacksSameTypeCrafting(recipe.getRecipeOutput(), result.item)
&& matchesIngredients(handler, recipe, ingredients)) {
final List<String> entry = new ArrayList<>(origin);
entry.add(recipe.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(recipe)));
found.add(entry);
}
}
return found;
}
private static List<IRecipe> getRecipesByOutput(Item item) {
final List<IRecipe> recipes = CraftingManager.getInstance().getRecipeList();
OutputIndex index = outputIndex;
if (recipes.size() != index.size) {
synchronized (RecipeOrigins.class) {
index = outputIndex;
if (recipes.size() != index.size) {
final Map<Item, List<IRecipe>> byItem = new HashMap<>();
for (IRecipe recipe : recipes) {
final ItemStack output = recipe.getRecipeOutput();
if (output != null)
byItem.computeIfAbsent(output.getItem(), k -> new ArrayList<>()).add(recipe);
}
outputIndex = index = new OutputIndex(recipes.size(), byItem);
}
}
}
return index.byItem.getOrDefault(item, Collections.emptyList());
}
private static boolean matchesIngredients(ShapedRecipeHandler handler, IRecipe recipe,
List<PositionedStack> ingredients) {
if (recipe instanceof ShapedRecipes shaped) {
return matchesGrid(handler.new CachedShapedRecipe(shaped), ingredients);
}
if (recipe instanceof ShapedOreRecipe shaped)
return matchesGrid(handler.forgeShapedRecipe(shaped), ingredients);
if (recipe instanceof ShapelessRecipes shapeless) return matchesUnordered(shapeless.recipeItems, ingredients);
if (recipe instanceof ShapelessOreRecipe shapeless) return matchesUnordered(shapeless.getInput(), ingredients);
return false;
}
private static boolean matchesGrid(CachedShapedRecipe reference, List<PositionedStack> ingredients) {
if (reference == null || reference.ingredients.size() != ingredients.size()) return false;
for (PositionedStack expected : reference.ingredients) {
final PositionedStack actual = ingredients.stream()
.filter(stack -> stack.relx == expected.relx && stack.rely == expected.rely).findFirst()
.orElse(null);
if (actual == null || !matchesItems(expected.items, actual.items)) return false;
}
return true;
}
private static boolean matchesItems(ItemStack[] expected, ItemStack[] actual) {
for (ItemStack candidate : expected) {
for (ItemStack item : actual) {
if (NEIServerUtils.areStacksSameTypeCrafting(candidate, item)) return true;
}
}
return false;
}
private static boolean matchesUnordered(List<?> inputs, List<PositionedStack> ingredients) {
final List<PositionedStack> remaining = new ArrayList<>(ingredients);
for (Object input : inputs) {
if (input == null) continue;
final PositionedStack match = remaining.stream().filter(stack -> matchesInput(input, stack)).findFirst()
.orElse(null);
if (match == null) return false;
remaining.remove(match);
}
return remaining.isEmpty();
}
private static boolean matchesInput(Object input, PositionedStack stack) {
final List<?> candidates = input instanceof List<?>list ? list : Collections.singletonList(input);
for (Object candidate : candidates) {
if (!(candidate instanceof ItemStack candidateStack)) continue;
for (ItemStack item : stack.items) {
if (NEIServerUtils.areStacksSameTypeCrafting(candidateStack, item)) return true;
}
}
return false;
}
public static class TrackingList extends ArrayList<IRecipe> {
@Override
public boolean add(IRecipe recipe) {
origins.put(recipe, captureOrigin());
return super.add(recipe);
}
@Override
public void add(int index, IRecipe recipe) {
origins.put(recipe, captureOrigin());
super.add(index, recipe);
}
@Override
public IRecipe set(int index, IRecipe recipe) {
origins.put(recipe, captureOrigin());
return super.set(index, recipe);
}
@Override
public boolean addAll(Collection<? extends IRecipe> recipes) {
final List<String> origin = captureOrigin();
for (IRecipe recipe : recipes) origins.put(recipe, origin);
return super.addAll(recipes);
}
@Override
public boolean addAll(int index, Collection<? extends IRecipe> recipes) {
final List<String> origin = captureOrigin();
for (IRecipe recipe : recipes) origins.put(recipe, origin);
return super.addAll(index, recipes);
}
@Override
public IRecipe remove(int index) {
final IRecipe removed = super.remove(index);
origins.remove(removed);
return removed;
}
@Override
public boolean remove(Object recipe) {
final boolean removed = super.remove(recipe);
if (removed) origins.remove(recipe);
return removed;
}
@Override
public void clear() {
origins.clear();
super.clear();
}
}
private static List<String> captureOrigin() {
final List<StackTraceElement> frames = findCallerFrames();
if (frames.isEmpty()) return Collections.singletonList("Unknown");
final ModContainer container = Loader.instance().activeModContainer();
final List<String> lines = new ArrayList<>(frames.size() + 1);
lines.add(container != null ? container.getName() : "Unknown");
for (int i = 0; i < frames.size(); i++) {
lines.add(formatFrame(frames.get(i)));
}
return lines;
}
private static String formatFrame(StackTraceElement frame) {
return frame.getFileName() != null ? frame.getClassName() + "."
+ frame.getMethodName()
+ "("
+ frame.getFileName()
+ ":"
+ frame.getLineNumber()
+ ")" : frame.getClassName() + "." + frame.getMethodName();
}
private static List<StackTraceElement> findCallerFrames() {
final List<StackTraceElement> frames = new ArrayList<>(MAX_FRAMES);
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
final String className = element.getClassName();
if (className.startsWith("java.") || className.startsWith("net.minecraft.")
|| className.startsWith("net.minecraftforge.")
|| className.startsWith("cpw.mods.fml.")
|| className.startsWith("sun.")
|| className.startsWith("jdk.")
|| className.startsWith("org.spongepowered.")
|| className.startsWith("codechicken.nei.recipe.debug.RecipeOrigins")) {
continue;
}
frames.add(element);
if (frames.size() >= MAX_FRAMES) break;
}
return frames;
}
}
@@ -21,7 +21,6 @@ public class GTFluidStackStringifyHandler implements IStackStringifyHandler {
protected static Method getFluidFromDisplayStack = null;
protected static Class<?> gtMetaGeneratedTool = null;
protected static Field playSound = null;
private static boolean loggedNoNbtDisplay = false;
static {
try {
@@ -0,0 +1,43 @@
package codechicken.nei.recipe.stackinfo;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import com.ruling_0.materiallib.api.StackResolver;
import codechicken.nei.api.IStackStringifyHandler;
import codechicken.nei.recipe.StackInfo;
/// Resolves config entries written in the MaterialLib reference form `ml:<MaterialName>:<shapeToken>` (see
/// [StackInfo#MATERIALLIB_PREFIX]) through [StackResolver], so an entry keeps naming the same item across sessions
/// that renumber item metadata. The resolved stack carries the metadata MaterialLib currently assigns, ignoring any
/// `Damage` on the entry. Entries in any other form are left to the other handlers.
///
/// Registered only behind a `Loader.isModLoaded("materiallib")` gate, so this class never classloads without
/// MaterialLib present.
public class MaterialLibStackStringifyHandler implements IStackStringifyHandler {
@Override
public ItemStack convertNBTToItemStack(NBTTagCompound nbtTag) {
final String strId = nbtTag.getString("strId");
if (!strId.startsWith(StackInfo.MATERIALLIB_PREFIX)) {
return null;
}
final String[] parts = strId.split(":");
if (parts.length != 3) {
return null;
}
final ItemStack stack = StackResolver.getStack(parts[1], parts[2], nbtTag.getInteger("Count"));
if (stack != null && nbtTag.hasKey("tag")) {
stack.setTagCompound((NBTTagCompound) nbtTag.getCompoundTag("tag").copy());
}
return stack;
}
}
@@ -745,7 +745,8 @@ public class RecipeWidget extends Widget {
return true;
}
return !this.showAsWidget && contains(mouseX, mouseY)
return NEIClientConfig.freezeCycleOnHover() && !this.showAsWidget
&& contains(mouseX, mouseY)
&& (getPositionedStackMouseOver(mouseX, mouseY) != null
|| forEachButtons(button -> button.contains(mouseX, mouseY) ? true : null, false));
}
@@ -53,28 +53,26 @@ public class ScrollBar {
private int trackPaddingBottom = 0;
private DrawableResource thumbTexture;
private int thumbSliceStart;
private int thumbSliceEnd;
private DrawableResource trackTexture;
private int trackSliceStart;
private int trackSliceEnd;
public static ScrollBar defaultScrollBar() {
return new ScrollBar();
}
public static ScrollBar defaultVerticalBar() {
return new ScrollBar().setTrackWidth(8)
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 29, 95, 8, 11).build(), 3, 4)
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).build(), 3, 2)
return new ScrollBar().setTrackWidth(8).setTrackTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 29, 95, 8, 11).setSlices(0, 0, 3, 4).build())
.setThumbTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).setSlices(0, 0, 3, 2).build())
.setThumbPadding(1, 1, 1, 1);
}
public static ScrollBar defaultHorizontalBar() {
return new ScrollBar().setTrackWidth(8).setOverflowType(OverflowType.SCROLL)
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 0, 97, 11, 8).build(), 3, 4)
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 12, 98, 9, 6).build(), 3, 2)
.setTrackTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 0, 97, 11, 8).setSlices(3, 4, 0, 0).build())
.setThumbTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 12, 98, 9, 6).setSlices(3, 2, 0, 0).build())
.setThumbPadding(1, 1, 1, 1);
}
@@ -126,17 +124,13 @@ public class ScrollBar {
return getTrackWidth();
}
public ScrollBar setThumbTexture(DrawableResource texture, int sliceStart, int sliceEnd) {
public ScrollBar setThumbTexture(DrawableResource texture) {
this.thumbTexture = texture;
this.thumbSliceStart = sliceStart;
this.thumbSliceEnd = sliceEnd;
return this;
}
public ScrollBar setTrackTexture(DrawableResource texture, int sliceStart, int sliceEnd) {
public ScrollBar setTrackTexture(DrawableResource texture) {
this.trackTexture = texture;
this.trackSliceStart = sliceStart;
this.trackSliceEnd = sliceEnd;
return this;
}
@@ -379,13 +373,13 @@ public class ScrollBar {
final boolean disabled = getVisibleSize(container) >= getActualSize(container);
if (this.trackTexture != null) {
drawTexture(this.trackTexture, this.trackSliceStart, this.trackSliceEnd, trackBounds, false, opacity);
drawTexture(this.trackTexture, trackBounds, false, opacity);
} else {
drawTrackRect(container, trackBounds, opacity);
}
if (this.thumbTexture != null) {
drawTexture(this.thumbTexture, this.thumbSliceStart, this.thumbSliceEnd, thumbBounds, disabled, opacity);
drawTexture(this.thumbTexture, thumbBounds, disabled, opacity);
} else {
drawThumbRect(container, thumbBounds, selected, opacity);
}
@@ -417,8 +411,7 @@ public class ScrollBar {
NEIClientUtils.drawRect(bounds.x, bounds.y, bounds.w, bounds.h, thumbColor);
}
private void drawTexture(DrawableResource texture, int sliceStart, int sliceEnd, Rectangle4i bounds,
boolean disabled, float opacity) {
private void drawTexture(DrawableResource texture, Rectangle4i bounds, boolean disabled, float opacity) {
final float c = disabled ? 0.65f : 1f;
GL11.glColor4f(c, c, c, opacity);
@@ -426,9 +419,9 @@ public class ScrollBar {
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
if (this.scrollType == ScrollType.HORIZONTAL) {
texture.draw(bounds.x, bounds.y, bounds.w, texture.getHeight(), sliceStart, sliceEnd, 0, 0);
texture.draw(bounds.x, bounds.y, bounds.w, texture.getHeight());
} else {
texture.draw(bounds.x, bounds.y, texture.getWidth(), bounds.h, 0, 0, sliceStart, sliceEnd);
texture.draw(bounds.x, bounds.y, texture.getWidth(), bounds.h);
}
}
@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.function.Predicate;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
@@ -249,6 +250,8 @@ public class ScrollContainer extends WidgetContainer {
final boolean canScrollHorizontal = canScrollHorizontal();
if (canScrollVertical || canScrollHorizontal) {
dragScrollBars(mousex, mousey, canScrollVertical, canScrollHorizontal);
GuiHelper.useScissor(
this.x + this.paddingInlineStart,
this.y + this.paddingBlockStart,
@@ -273,6 +276,20 @@ public class ScrollContainer extends WidgetContainer {
}
private void dragScrollBars(int mousex, int mousey, boolean canScrollVertical, boolean canScrollHorizontal) {
if (!Mouse.isButtonDown(0)) {
return;
}
if (canScrollVertical) {
this.verticalScrollBar.onMouseDragged(this, mousex, mousey, 0, 0);
}
if (canScrollHorizontal) {
this.horizontalScrollBar.onMouseDragged(this, mousex, mousey, 0, 0);
}
}
protected void drawContent(int mousex, int mousey) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushMatrix();
@@ -14,6 +14,9 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.oredict.OreDictionary;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
@@ -197,9 +200,9 @@ public class ItemStackFilterParser {
}
private static ItemFilter getStrictIdentifierFilter(String rule) {
ItemStack item = NEIServerUtils.getModdedItem(rule, null);
Supplier<ItemStack> item = Suppliers.memoize(() -> NEIServerUtils.getModdedItem(rule, null));
return stack -> OreDictionary.itemMatches(item, stack, false);
return stack -> OreDictionary.itemMatches(item.get(), stack, false);
}
protected static ItemFilter getStringIdentifierFilter(String rule) {
@@ -289,6 +289,7 @@ nei.options.inventory.bookmarks.showRecipeMarkerMode=Show Recipe Marker
nei.options.inventory.bookmarks.showRecipeMarkerMode.0=None
nei.options.inventory.bookmarks.showRecipeMarkerMode.1=Text
nei.options.inventory.bookmarks.showRecipeMarkerMode.2=Background
nei.options.inventory.bookmarks.maxVerticalTooltipRows=Max Vertical Tooltip Rows (0 - disable)
nei.options.inventory.bookmarks.showRecipeHandlerIcon=Show Handler Icon
nei.options.inventory.bookmarks.showRecipeHandlerIcon.true=Yes
nei.options.inventory.bookmarks.showRecipeHandlerIcon.false=No
@@ -427,6 +428,9 @@ nei.options.inventory.autocrafting.false=Disabled
nei.options.inventory.guirecipe.shiftOverlayRecipe=Require holding shift to move items when overlaying recipe
nei.options.inventory.guirecipe.shiftOverlayRecipe.true=Yes
nei.options.inventory.guirecipe.shiftOverlayRecipe.false=No
nei.options.inventory.guirecipe.freezeCycleOnHover=Stop cycling the recipe while hovering over an ingredient or a button
nei.options.inventory.guirecipe.freezeCycleOnHover.true=Yes
nei.options.inventory.guirecipe.freezeCycleOnHover.false=No
nei.options.inventory.widgetsenabled.true=NEI Enabled
nei.options.inventory.widgetsenabled.false=NEI Disabled
nei.options.inventory.cheatmode.0=Recipe Mode
@@ -516,6 +520,13 @@ nei.options.inventory.itemzoom.neiOnly.tip=Zoom items only from the JEI ingredie
nei.options.inventory.itemzoom.onlySolid=Show Only Solid Items
nei.options.inventory.itemzoom.onlySolid.true=Enabled
nei.options.inventory.itemzoom.onlySolid.false=Disabled
nei.options.inventory.itemzoom.preferredSide=Preferred Side
nei.options.inventory.itemzoom.preferredSide.0=Auto
nei.options.inventory.itemzoom.preferredSide.1=Left
nei.options.inventory.itemzoom.preferredSide.2=Right
nei.options.inventory.itemzoom.preferredSide.0.tip=Prefer whichever side currently has fewer items in its panel.
nei.options.inventory.itemzoom.preferredSide.1.tip=Always prefer the left side, unless the cursor is there.
nei.options.inventory.itemzoom.preferredSide.2.tip=Always prefer the right side, unless the cursor is there.
nei.options.inventory.itemzoom.helpText=Display Help Text
nei.options.inventory.itemzoom.helpText.true=Enabled
nei.options.inventory.itemzoom.helpText.false=Disabled
@@ -617,6 +628,7 @@ nei.debug.RecipeHandler.Width=Width
nei.debug.RecipeHandler.ShowFavorites=Show Favorites
nei.debug.RecipeHandler.ShowOverlay=Show Overlays
nei.debug.RecipeHandler.ShowBadge=Show Badge
nei.debug.RecipeHandler.DuplicatesOnly=Duplicates Only
nei.debug.RecipeHandler.AllowOverflowY=Allow Overflow Y
nei.debug.RecipeHandler.AllowOverflowX=Allow Overflow X
nei.debug.RecipeHandler.Override=Override
@@ -625,6 +637,7 @@ nei.debug.RecipeHandler.checkbox.off=Off
nei.debug.RecipeHandler.yes=Yes
nei.debug.RecipeHandler.no=No
nei.debug.RecipeHandler.reset=Reset
nei.debug.RecipeHandler.RegisteredBy=Registered by: %s
nei.recipe.page=Page %s/%s
nei.recipe.tooltip=Recipes
@@ -693,6 +706,7 @@ nei.recipe.overlay.missing=Missing following
nei.recipe.overlay.mismatch=Mismatch Crafting Grid
nei.recipe.overlay.overlayRecipe=Overlay Recipe
nei.recipe.overlay.fillCraftingGrid=Fill Crafting Grid
nei.recipe.overlay.fillCraftingGridOnce=Fill Crafting Grid (single recipe)
nei.recipe.overlay.bookmarkRecipe=Save with Recipe
nei.recipe.overlay.bookmarkRecipeAndCount=Save with Recipe & Count
nei.recipe.overlay.sendRecipeToChat=Send Recipe to Chat
@@ -265,6 +265,9 @@ nei.options.inventory.optimize_gui_overlap_computation.false=Нет
nei.options.inventory.guirecipe.shiftOverlayRecipe=Требовать удержание Shift для перемещения предметов при отображении рецепта
nei.options.inventory.guirecipe.shiftOverlayRecipe.true=Да
nei.options.inventory.guirecipe.shiftOverlayRecipe.false=Нет
nei.options.inventory.guirecipe.freezeCycleOnHover=Останавливать перелистывание рецепта при наведении на предмет или кнопку
nei.options.inventory.guirecipe.freezeCycleOnHover.true=Да
nei.options.inventory.guirecipe.freezeCycleOnHover.false=Нет
nei.options.inventory.widgetsenabled.true=NEI включен
nei.options.inventory.widgetsenabled.false=NEI выключен
nei.options.inventory.cheatmode.0=Режим рецептов
@@ -332,6 +335,13 @@ nei.options.inventory.itemzoom.neiOnly.tip=Увеличивать предмет
nei.options.inventory.itemzoom.onlySolid=Показывать только твердые предметы
nei.options.inventory.itemzoom.onlySolid.true=Включено
nei.options.inventory.itemzoom.onlySolid.false=Отключено
nei.options.inventory.itemzoom.preferredSide=Предпочитаемая сторона
nei.options.inventory.itemzoom.preferredSide.0=Авто
nei.options.inventory.itemzoom.preferredSide.1=Слева
nei.options.inventory.itemzoom.preferredSide.2=Справа
nei.options.inventory.itemzoom.preferredSide.0.tip=Выбирать сторону, где сейчас меньше предметов в панели.
nei.options.inventory.itemzoom.preferredSide.1.tip=Всегда предпочитать левую сторону, если там нет курсора.
nei.options.inventory.itemzoom.preferredSide.2.tip=Всегда предпочитать правую сторону, если там нет курсора.
nei.options.inventory.itemzoom.helpText=Показывать текст помощи
nei.options.inventory.itemzoom.helpText.true=Включено
nei.options.inventory.itemzoom.helpText.false=Отключено
@@ -428,6 +438,7 @@ nei.recipe.overlay=отображение рецепта
nei.recipe.overlay.missing=Отсутствует следующее
nei.recipe.overlay.mismatch=Несоответствие сетки крафта
nei.recipe.overlay.fillCraftingGrid=Заполнить сетку крафта
nei.recipe.overlay.fillCraftingGridOnce=Заполнить сетку крафта (один рецепт)
nei.recipe.overlay.bookmarkRecipe=Сохранить с рецептом
nei.recipe.overlay.sendRecipeToChat=Отправить рецепт в чат
@@ -64,6 +64,7 @@ public class SearchTokenParserTest {
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 ModeValue[] nonGrammarPatternModes = { patternModes[0], patternModes[1], patternModes[2] };
private static final SearchParserProvider defaultParserProvider = parserProvider(
'\0',
@@ -119,6 +120,10 @@ public class SearchTokenParserTest {
private static final List<Arguments> allModesTestProvider = combinationsProvider(spaceModes, patternModes);
private static final List<Arguments> nonGrammarAllSpaceModesTestProvider = combinationsProvider(
spaceModes,
nonGrammarPatternModes);
private static final List<Arguments> extendedPlusAllSpaceModesTestProvider = combinationsProvider(
spaceModes,
extendedPlusPatternMode);
@@ -236,6 +241,36 @@ public class SearchTokenParserTest {
assertTrue(matchedItems.contains(itemA));
}
@DisplayName("Negated prefix (dash)")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("allModesTestProvider")
public void testNegatedPrefixDash(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(5, matchedItems.size());
assertFalse(matchedItems.contains(itemA));
}
@DisplayName("Negated prefix (bang)")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("nonGrammarAllSpaceModesTestProvider")
public void testNegatedPrefixBang(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(5, matchedItems.size());
assertFalse(matchedItems.contains(itemA));
}
@DisplayName("Space mode")
@ParameterizedTest(name = "{argumentsWithNames}")
@FieldSource("spaceIsSpaceAllModesTestProvider")