Compare commits

...
Author SHA1 Message Date
slprime ff428bf538 Allow plugins to force fill mode for overlay button (#960) 2026-06-28 13:11:27 -04:00
Martin Robertz a406021f26 deps 2026-06-27 15:15:43 +02:00
slprime 8500ac57eb Persist hidden items by GUID across reloads (#959) 2026-06-27 00:48:39 +03:00
slprime d97123ec13 ItemLoader Refactoring/Optimization (#958) 2026-06-26 21:25:07 +03:00
slprime 434931bfac Fix Loading Items error log (#956) 2026-06-21 00:57:41 +03:00
RanzuandMartin Robertz 860011036b Convert color class (#947)
Co-authored-by: Martin Robertz <[email protected]>
2026-06-16 10:21:20 +02:00
slprime 937c1858a4 Fix Config Option NPE (#951) 2026-06-14 21:14:44 +03:00
slprime 4562ecf087 Fix Broken DisplayName in Untranslator (#949) 2026-06-13 17:46:46 +03:00
slprime d621d5d5bd Fix Autocrefing Permission (#948) 2026-06-13 17:25:07 +03:00
Martin Robertz 3175b316af update+gradle+deps+bs 2026-06-12 23:32:41 +02:00
slprime 61930ac7a8 Add server-controlled autocraft permission (#942) 2026-06-10 18:58:23 +03:00
slprime fcbf1dcade Fix Single Item In Bookmarks With Amount (#941) 2026-06-07 20:22:42 +03:00
slprime 3fa69e826c Guard loadTransferRects against plugin init errors (#939) 2026-05-27 08:26:29 +03:00
31fff21a55 Smart-Fallback & Alchemic Chemistry Set Fix (#935)
Co-authored-by: FFS <FFS@OhFuckOff>
Co-authored-by: slprime <[email protected]>
2026-05-27 02:29:59 +03:00
slprime 2e9ab88692 Fix ItemStack load from NBT with short overflow (#938) 2026-05-27 02:22:47 +03:00
slprime a02d293d44 Fix Recipe Page Area (#932) 2026-05-11 18:47:36 +03:00
sisyphussy 1265113390 Fix mui2 screen capture (#930) 2026-05-07 20:30:52 +03:00
slprime 4ffd614017 GuiRecipe: Add scrollPages Option (#929) 2026-05-07 18:57:15 +03:00
slprime 41554f4c5b Options: fix memory leaks (#928) 2026-05-06 22:36:07 +02:00
slprime 2104184077 GuiInfo: fix memory leaks (#927) 2026-05-06 20:18:35 +02:00
slprime d5f25a4be8 Prevent ConcurrentModificationException while searching for fuels in ItemList (#926) 2026-05-06 19:43:50 +03:00
slprime ccbf94790a Add Horizontal Scroll to Recipe GUI (#924) 2026-05-06 19:31:00 +03:00
slprime 2f4f72a2d6 Fix Filter Loading (#925) 2026-05-06 07:55:49 +03:00
danyadevandslprime f8cec9a78c Much faster recipe dumper (#920)
Co-authored-by: slprime <[email protected]>
2026-04-25 14:58:50 +03:00
Lucaandslprime fb43a662eb Fix StringIndexOutOfBoundsException in FormattedTextField (#922)
Co-authored-by: slprime <[email protected]>
2026-04-25 10:30:32 +00:00
Luca b2e0b8878a Fix bookmark recipe item order being inconsistent (#921) 2026-04-25 13:21:46 +03:00
slprime 374f019103 Fix Autocrafting NPE (#917) 2026-04-14 01:29:26 +03:00
slprime 5dfcff6f30 Rollback PR#913 (#916) 2026-04-13 20:39:15 +03:00
slprime bd17301cb9 Fix Collapsed Recipes in Bookmarks (#915) 2026-04-13 15:41:30 +03:00
lc-1337 96ccd4e109 fix client -> server sync in creative gui (#913) 2026-04-12 14:47:27 +03:00
Kogepan229 b30ca57e68 Don't block slot click if canCheatItem is false (#912) 2026-04-08 18:35:08 +03:00
slprime 8f949bc1d1 Refactor recipe profiler (#911) 2026-04-07 13:10:39 +03:00
slprime d25365942a Fix Repair Handler with broken ItemStack (#910) 2026-04-06 21:35:47 +03:00
slprime eb58e1dabc Improve Recipe Badge (#909) 2026-04-06 20:39:41 +03:00
slprime f4601e7eed Disable badge by default (#907) 2026-04-04 21:12:05 +03:00
slprime ba48806667 Chance tooltip (#906) 2026-04-04 00:03:15 +03:00
Martin Robertz 70c332796a update+deps 2026-04-03 16:34:57 +02:00
slprime bcc25d4369 Fix Collapsed Recipes in Bookmarks (#905) 2026-03-31 20:47:23 +03:00
slprime 10a3109714 Add recipe chance to bookmarks (#903) 2026-03-30 20:36:55 +03:00
koolkrafter5 b60769864f Improve image exporter data dumper (#902) 2026-03-30 08:19:34 +03:00
slprime 6bc899769f Add Reset to Debug Handler (#901) 2026-03-26 22:22:13 +02:00
75 changed files with 2502 additions and 1083 deletions

No files matched your search

+18 -2
View File
@@ -1,8 +1,12 @@
// Add your dependencies here
configurations.create('gtnhlibEmbed') {
canBeConsumed = false
canBeResolved = true
}
dependencies {
api('com.github.GTNewHorizons:CodeChickenCore:1.4.10:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.9.13:dev')
api('com.github.GTNewHorizons:CodeChickenCore:1.4.16:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.20:dev')
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
@@ -17,4 +21,16 @@ dependencies {
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.20") { transitive = false }
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.20") { transitive = false }
}
[tasks.jar, tasks.shadowJar].each {
it.configure {
from({ project.configurations.getByName('gtnhlibEmbed').collect { zipTree(it) } }) {
include 'com/gtnewhorizon/gtnhlib/color/ColorResource.class'
include 'com/gtnewhorizon/gtnhlib/color/ColorResource$*.class'
}
}
}
+10 -10
View File
@@ -1,12 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/5ee463876563a46fff651cce1bc53a4c/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/5ee463876563a46fff651cce1bc53a4c/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e1118b4c7c2858d28154d086dbff010e/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/5ee463876563a46fff651cce1bc53a4c/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/b96cb7670c095245220e7d01d5927b58/redirect
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d76df094a9cbbabd3b08251f9e61444a/redirect
toolchainVersion=25
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26'
}
@@ -19,6 +19,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import org.apache.commons.io.IOUtils;
@@ -184,27 +185,33 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
if (itemStack != null && this.grid.indexOf(groupId, itemStack, recipeId, false) == -1) {
final boolean existsRecipe = recipeId != null
&& this.grid.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID);
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(itemStack);
long count = nbTag.getInteger("Count");
long factor = existsRecipe ? Math.max(count, 1) : 1;
final Recipe recipe = Recipe.of(recipeId);
final BookmarkItem.Builder builder;
if (recipe != null) {
builder = BookmarkItem.builder(groupId, itemStack, recipe, BookmarkItemType.RESULT).multiplier(1);
} else if (recipeId != null) {
builder = BookmarkItem.builder(groupId, itemStack).recipeId(recipeId)
.factor(StackInfo.getAmount(itemStack)).multiplier(1);
} else {
builder = BookmarkItem.builder(groupId, itemStack).factor(1).multiplier(StackInfo.getAmount(itemStack));
}
if (existsRecipe) {
builder.type(BookmarkItemType.RESULT);
for (int i = 0; i < this.grid.size(); i++) {
if (this.grid.getBookmarkItem(i).equalsRecipe(recipeId, groupId)) {
count *= this.grid.getBookmarkItem(i).getMultiplier();
builder.multiplier(this.grid.getBookmarkItem(i).getMultiplier());
break;
}
}
} else {
builder.type(BookmarkItemType.ITEM);
}
this.grid.addItem(
BookmarkItem.of(
groupId,
StackInfo.withAmount(itemStack, count),
factor,
recipeId,
existsRecipe ? BookmarkItemType.RESULT : BookmarkItemType.ITEM),
true);
this.grid.addItem(builder.build(), true);
return true;
}
@@ -212,9 +219,11 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
public boolean addRecipe(Recipe recipe, int multiplier, int groupId) {
if (recipe == null) return false;
final RecipeId recipeId = recipe.getRecipeId();
if (recipe != null && this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
if (this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
this.grid.addRecipe(recipe, multiplier, groupId);
return true;
}
@@ -245,15 +254,22 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
ItemStack stack = recipeId.getResult();
if (stack != null && uniqueRecipe.add(recipeId)) {
this.grid.addItem(
BookmarkItem
.of(groupId, stack, StackInfo.getAmount(stack), recipeId, BookmarkItemType.ITEM),
true);
final Recipe recipe = Recipe.of(recipeId);
final BookmarkItem.Builder builder;
if (recipe != null) {
builder = BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT);
} else {
builder = BookmarkItem.builder(groupId, stack).recipeId(recipeId)
.factor(StackInfo.getAmount(stack));
}
this.grid.addItem(builder.multiplier(1).type(BookmarkItemType.ITEM).build(), true);
uniqueStack.add(stack);
}
} else {
ItemStack stack = extractItem(item);
final ItemStack stack = extractItem(item);
if (stack != null && !uniqueStack.contains(stack)) {
this.grid.addItem(BookmarkItem.of(groupId, stack), true);
@@ -925,6 +941,17 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
if (slot != null) {
if (slot.getBookmarkItem().chance != PositionedStack.CHANCE_FULL) {
final String chanceText = NEIClientUtils
.formatChance(slot.getBookmarkItem().chance / (float) PositionedStack.CHANCE_FULL);
currenttip.add(
1,
EnumChatFormatting.GRAY + NEIClientUtils.translate(
slot.getType() == BookmarkItemType.INGREDIENT ? "recipe.badge.chance.consume"
: "recipe.badge.chance.output",
chanceText));
}
if (slot.getType() == BookmarkItemType.INGREDIENT && slot.getRecipeId() != null
&& slot.getBookmarkItem().permutations.size() > 1) {
RecipeId recipeId = slot.getRecipeId();
@@ -1119,7 +1146,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
for (BookmarkItem item : math.initialItems) {
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
final long amount = item.amount - invStackSize * item.fluidCellAmount;
final long amount = item.getAmount() - invStackSize * item.fluidCellAmount;
if (amount > 0) {
pullItemStacks.add(item.getItemStack(amount));
}
@@ -1133,7 +1160,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
for (BookmarkItem item : math.initialItems) {
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
final long strStackSize = containerStorage.getOrDefault(item.itemStack, 0L);
final long amount = item.amount - invStackSize * item.fluidCellAmount
final long amount = item.getAmount() - invStackSize * item.fluidCellAmount
- strStackSize * item.fluidCellAmount;
if (amount > 0) {
@@ -57,6 +57,10 @@ public abstract class Button extends Widget {
return label;
}
public int getRenderLabelColor() {
return 0;
}
public final String label;
public Image icon;
@@ -45,6 +45,7 @@ import codechicken.core.ClassDiscoverer;
import codechicken.core.ClientUtils;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.api.API;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.guihook.GuiContainerManager;
@@ -374,6 +375,7 @@ public class ClientHandler {
} else if (event.gui == null) {
/* prevent WorldClient reference being held in the Gui */
NEIController.manager = null;
GuiInfo.clearGuiHandlers();
}
}
@@ -0,0 +1,22 @@
package codechicken.nei;
import com.gtnewhorizon.gtnhlib.color.ColorResource;
public class ColorUtils {
private static final ColorResource.Factory color = new ColorResource.Factory("nei");
public static final ColorResource
// spotless:off
textGray = color.rgb("textGray", "0x404040"),
textLightGray = color.rgb("textLightGray", "0x606060"),
recipeTitle = color.rgb("recipeTitle", "0xFFFFFF"),
recipeTitleHover = color.rgb("recipeTitleHover", "0xFFFF55"),
subsetWidget = color.rgb("subsetWidget", "0xAA00AA"),
recipeBadge = color.rgb("recipeBadge", "0xFDD835"),
buttonLabelNormal = color.rgb("buttonLabelNormal", "0xE0E0E0"),
buttonLabelHover = color.rgb("buttonLabelHover", "0xFFFFA0"),
buttonLabelDisabled = color.rgb("buttonLabelDisabled", "0x601010"),
buttonLabelInactive = color.rgb("buttonLabelInactive", "0xA0A0A0");
// spotless:on
}
@@ -296,15 +296,26 @@ public class FormattedTextField extends GuiTextField {
int shift = 0;
for (int i = 0; i < position; i++) {
while (this.formattedText.length() > i + shift && this.formattedText.charAt(i + shift) == '\u00a7') {
while (this.formattedText.length() > i + shift + 1 && this.formattedText.charAt(i + shift) == '\u00a7'
&& isFormattingCode(this.formattedText.charAt(i + shift + 1))) {
shift += 2;
}
}
return position + shift;
return Math.min(position + shift, this.formattedText.length());
}
private static boolean isFormattingCode(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
|| (c >= 'k' && c <= 'o')
|| c == 'r'
|| (c >= 'A' && c <= 'F')
|| (c >= 'K' && c <= 'O')
|| c == 'R';
}
private String getPreviousColor(int position) {
position = Math.min(position, this.formattedText.length() - 1);
while (position >= 0) {
if (this.formattedText.charAt(position) == '\u00a7') {
return this.formattedText.substring(position, position + 2);
@@ -24,8 +24,8 @@ public class GuiEnchantmentModifier extends GuiContainer {
@Override
protected void drawGuiContainerForegroundLayer(int par1, int par2) {
fontRendererObj.drawString(translate("enchant"), 12, 6, 0x404040);
fontRendererObj.drawString(translate("enchant.level"), 19, 20, 0x404040);
fontRendererObj.drawString(translate("enchant"), 12, 6, ColorUtils.textGray.getColor());
fontRendererObj.drawString(translate("enchant.level"), 19, 20, ColorUtils.textGray.getColor());
}
protected void drawGuiContainerBackgroundLayer(float f, int i, int j) {
@@ -39,7 +39,11 @@ public class GuiEnchantmentModifier extends GuiContainer {
container.drawScrollBar(this);
String levelstring = "" + container.level;
fontRendererObj.drawString(levelstring, 33 - fontRendererObj.getStringWidth(levelstring) / 2, 34, 0xFF606060);
fontRendererObj.drawString(
levelstring,
33 - fontRendererObj.getStringWidth(levelstring) / 2,
34,
ColorUtils.textLightGray.getColor());
GL11.glTranslatef(-guiLeft, -guiTop, 0);
}
@@ -45,15 +45,14 @@ public class GuiNEIButton extends GuiButton {
}
protected int getTextColour(boolean mouseOver) {
int color = 0xe0e0e0;
if (!enabled) {
color = 0xffa0a0a0;
return ColorUtils.buttonLabelInactive.getColor();
} else if (mouseOver) {
color = 0xffffa0;
return ColorUtils.buttonLabelHover.getColor();
}
return color;
return ColorUtils.buttonLabelNormal.getColor();
}
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
@@ -289,10 +289,10 @@ public class GuiPotionCreator extends GuiContainerWidget {
CCRenderState.changeTexture("nei:textures/gui/potion.png");
drawTexturedModalRect(0, 0, 0, 0, xSize, ySize);
FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, 0x404040);
fontRendererObj.drawString(translate("potion.duration"), 12, 40, 0x404040);
fontRendererObj.drawString(translate("potion.level"), 19, 73, 0x404040);
FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, 0xFF606060);
FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, ColorUtils.textGray.getColor());
fontRendererObj.drawString(translate("potion.duration"), 12, 40, ColorUtils.textGray.getColor());
fontRendererObj.drawString(translate("potion.level"), 19, 73, ColorUtils.textGray.getColor());
FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, ColorUtils.textGray.getColor());
}
@Override
@@ -73,6 +73,7 @@ public class IMCForNEI {
API.registerRecipeHandler(new ProfilerRecipeHandler(true));
API.registerUsageHandler(new ProfilerRecipeHandler(false));
sendProfilerRecipeHandler();
}
private static void sendHandler(String aName, String aBlock) {
@@ -136,6 +137,19 @@ public class IMCForNEI {
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendProfilerRecipeHandler() {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", "codechicken.nei.recipe.ProfilerRecipeHandler");
aNBT.setString("modName", "Not Enough Items");
aNBT.setString("modId", "nei");
aNBT.setString("itemName", "minecraft:clock");
aNBT.setInteger("handlerHeight", 16);
aNBT.setInteger("handlerWidth", 166);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendCatalyst(String aName, String aStack) {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handlerID", aName);
+4 -273
View File
@@ -2,40 +2,22 @@ package codechicken.nei;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import net.minecraft.client.Minecraft;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraftforge.fluids.IFluidContainerItem;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import codechicken.nei.ThreadOperationTimer.TimeoutException;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.RepairRecipeHandler;
import codechicken.nei.search.TooltipFilter;
import codechicken.nei.util.ItemUntranslator;
import cpw.mods.fml.common.FMLLog;
public class ItemList {
@@ -53,9 +35,6 @@ public class ItemList {
public static final List<ItemFilterProvider> itemFilterers = new LinkedList<>();
public static final List<ItemsLoadedCallback> loadCallbacks = new LinkedList<>();
private static final HashSet<Item> erroredItems = new HashSet<>();
private static final HashSet<String> stackTraces = new HashSet<>();
private static Map<ItemStack, Integer> ordering = new HashMap<>();
/**
* Unlike {@link LayoutManager#itemsLoaded}, this indicates whether item loading is actually finished or not.
*/
@@ -218,231 +197,6 @@ public class ItemList {
return filters;
}
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
private void damageSearch(Item item, List<ItemStack> permutations) {
HashSet<String> damageIconSet = new HashSet<>();
for (int damage = 0; damage < 16; damage++) try {
ItemStack itemstack = new ItemStack(item, 1, damage);
IIcon icon = item.getIconIndex(itemstack);
String name = getTooltip(itemstack);
String s = name + "@" + (icon == null ? 0 : icon.hashCode());
if (damageIconSet.add(s)) {
permutations.add(itemstack);
}
} catch (TimeoutException t) {
throw t;
} catch (Throwable t) {
NEIServerUtils.logOnce(
t,
stackTraces,
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
item.toString());
}
}
private String getTooltip(ItemStack stack) {
try {
return String.join("\n", stack.getTooltip(Minecraft.getMinecraft().thePlayer, false));
} catch (Throwable ignored) {}
return "";
}
private void updateOrdering(List<ItemStack> items) {
final Map<ItemStack, Integer> newOrdering = new HashMap<>();
ItemSorter.sort(items);
if (!CollapsibleItems.isEmpty()) {
final HashMap<Integer, Integer> groups = new HashMap<>();
int orderIndex = 0;
for (ItemStack stack : items) {
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
if (groupIndex == -1) {
newOrdering.put(stack, orderIndex++);
} else {
if (!groups.containsKey(groupIndex)) {
groups.put(groupIndex, orderIndex++);
}
newOrdering.put(stack, groups.get(groupIndex));
}
}
} else {
int orderIndex = 0;
for (ItemStack stack : items) {
newOrdering.put(stack, orderIndex++);
}
}
ItemList.ordering = newOrdering;
}
private List<ItemStack> getPermutations(Item item) {
final List<ItemStack> permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item));
if (permutations.isEmpty()) {
item.getSubItems(item, null, permutations);
}
if (permutations.isEmpty()) {
damageSearch(item, permutations);
}
permutations.addAll(ItemInfo.itemVariants.get(item));
return permutations.stream()
.filter(
stack -> stack.getItem() != null && stack.getItem().delegate.name() != null
&& !ItemInfo.isHidden(stack))
.collect(Collectors.toList());
}
private void runChecked(ItemStack stack, Runnable action, String reason) {
int hashOld = 0;
if (stack.hasTagCompound()) {
hashOld = stack.stackTagCompound.hashCode();
}
action.run();
if (stack.hasTagCompound() && hashOld != stack.stackTagCompound.hashCode()) {
FMLLog.warning(
"NEI: Forced tag update with reason (" + reason
+ ") for "
+ stack
+ "("
+ stack.getItem()
+ ")");
}
}
private void forceTagCompoundInitialization(ItemStack stack) {
Item item = stack.getItem();
if (item == null) {
return;
}
if (item instanceof IFluidContainerItem) {
IFluidContainerItem fluidItem = (IFluidContainerItem) item;
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
}
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
runChecked(stack, () -> item.getDamage(stack), "getDamage");
runChecked(stack, () -> item.showDurabilityBar(stack), "showDurabilityBar");
runChecked(stack, () -> item.getAttributeModifiers(stack), "getAttributeModifiers");
runChecked(stack, () -> item.getDurabilityForDisplay(stack), "getDurabilityForDisplay");
runChecked(stack, () -> item.getItemStackLimit(stack), "getItemStackLimit");
runChecked(stack, () -> item.getToolClasses(stack), "getToolClasses");
runChecked(stack, () -> item.getUnlocalizedNameInefficiently(stack), "getUnlocalizedNameInefficiently");
runChecked(stack, () -> item.hasEffect(stack), "hasEffect");
runChecked(stack, () -> item.getDigSpeed(stack, Blocks.stone, 0), "getDigSpeed");
/*
* unused... for now runChecked(stack, () -> GameRegistry.getFuelValue(stack), "getFuelValue");
* runChecked(stack, () -> item.getUnlocalizedName(stack), "getUnlocalizedName"); runChecked(stack, () ->
* item.doesContainerItemLeaveCraftingGrid(stack), "doesContainerItemLeaveCraftingGrid"); runChecked(stack,
* () -> item.getItemUseAction(stack), "getItemUseAction"); runChecked(stack, () ->
* item.getMaxItemUseDuration(stack), "getMaxItemUseDuration"); runChecked(stack, () ->
* item.getPotionEffect(stack), "getPotionEffect"); runChecked(stack, () -> item.isPotionIngredient(stack),
* "isPotionIngredient"); runChecked(stack, () -> item.getRarity(stack), "getRarity"); runChecked(stack, ()
* -> item.hasCustomEntity(stack), "hasCustomEntity"); runChecked(stack, () ->
* item.getSmeltingExperience(stack), "getSmeltingExperience"); runChecked(stack, () ->
* item.getMaxDamage(stack), "getMaxDamage"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
* runChecked(stack, stack::isItemDamaged, "isItemDamaged"); runChecked(stack, () -> item.hasEffect(stack,
* 0), "hasEffect"); runChecked(stack, () -> item.getItemEnchantability(stack), "getItemEnchantability");
* runChecked(stack, () -> item.isBeaconPayment(stack), "isBeaconPayment"); runChecked(stack, () ->
* item.getDamage(stack), "getDamage"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
* runChecked(stack, () -> item.getDamage(stack), "getDamage");
*/
}
// Generate itemlist, permutations, orders, collapsibleitems, and informationhandler stacks
@Override
@SuppressWarnings("unchecked")
public void execute() {
if (!NEIClientConfig.isEnabled() || NEIClientUtils.mc().thePlayer == null) return;
ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
LayoutManager.itemsLoaded = true;
loadFinished = false;
SearchField.searchParser.clearCache();
ItemSorter.instance.ordering.clear();
CollapsibleItems.clearCache();
TooltipFilter.clearCache();
InformationHandler.clearCache();
List<ItemStack> items = new ArrayList<>();
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
ItemStackSet unique = new ItemStackSet();
StreamSupport.stream(((Iterable<Item>) Item.itemRegistry).spliterator(), true).forEach(item -> {
if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return;
try {
timer.reset(item);
List<ItemStack> permutations = getPermutations(item);
timer.reset();
for (ItemStack stack : permutations) {
if (!unique.contains(stack)) {
synchronized (unique) {
unique.add(stack);
}
synchronized (items) {
items.add(stack);
}
forceTagCompoundInitialization(stack);
CollapsibleItems.putItem(stack);
TooltipFilter.getSearchTooltip(stack);
InformationHandler.populateStacks(stack);
}
}
synchronized (itemMap) {
itemMap.putAll(item, permutations);
}
} catch (Throwable t) {
NEIServerConfig.logger.error("Removing item: {} from list.", item, t);
erroredItems.add(item);
}
});
int index = 0;
for (Item item : (Iterable<Item>) Item.itemRegistry) {
for (ItemStack stack : itemMap.get(item)) {
ItemSorter.instance.ordering.put(stack, index++);
}
}
if (interrupted()) return;
ItemList.items = items;
ItemList.itemMap = itemMap;
for (ItemsLoadedCallback callback : loadCallbacks) callback.itemsLoaded();
if (interrupted()) return;
updateOrdering(ItemList.items);
loadFinished = true;
RepairRecipeHandler.findRepairRecipesOnceParallel();
RecipeHandlerRef.clearCache();
FavoriteRecipes.reload();
SubsetWidget.updateHiddenItems();
ItemPanels.bookmarkPanel.load();
updateFilter.restart();
}
};
public static ForkJoinPool forkJoinPool;
static {
@@ -462,35 +216,12 @@ public class ItemList {
forkJoinPool = new ForkJoinPool(poolSize, factory, null, false);
}
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
// The item list pipeline lives in ItemListLoader; these references are kept for backward compatibility.
public static final RestartableTask loadItems = ItemListLoader.loadItems;
@Override
public void execute() {
public static final RestartableTask refreshItems = ItemListLoader.refreshItems;
if (!loadFinished) return;
ItemFilter filter = getItemListFilter();
ArrayList<ItemStack> filtered;
try {
filtered = ItemList.forkJoinPool.submit(
() -> items.parallelStream().filter(filter::matches)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
} catch (InterruptedException | ExecutionException e) {
filtered = new ArrayList<>();
e.printStackTrace();
stop();
}
if (interrupted()) return;
filtered.sort(Comparator.comparingInt(ItemList.ordering::get));
if (interrupted()) return;
ItemPanel.updateItemList(filtered);
}
};
public static final RestartableTask updateFilter = ItemListLoader.updateFilter;
/**
* @deprecated Use updateFilter.restart()
@@ -0,0 +1,376 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraftforge.fluids.IFluidContainerItem;
import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import codechicken.nei.ItemList.ItemsLoadedCallback;
import codechicken.nei.ThreadOperationTimer.TimeoutException;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.RepairRecipeHandler;
import codechicken.nei.search.TooltipFilter;
import cpw.mods.fml.common.FMLLog;
public class ItemListLoader {
private static final HashSet<Item> erroredItems = new HashSet<>();
private static final HashSet<String> stackTraces = new HashSet<>();
private static Map<ItemStack, Integer> ordering = new HashMap<>();
private ItemListLoader() {}
private static void damageSearch(Item item, List<ItemStack> permutations) {
final Set<String> damageIconSet = new HashSet<>();
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
for (int damage = 0; damage < 16; damage++) {
try {
final ItemStack itemstack = new ItemStack(item, 1, damage);
final IIcon icon = item.getIconIndex(itemstack);
final String key = itemstack.getDisplayName() + "@"
+ (icon == null ? 0 : icon.hashCode())
+ "@"
+ getInformation(itemstack, player);
if (damageIconSet.add(key)) {
permutations.add(itemstack);
}
} catch (TimeoutException t) {
throw t;
} catch (Throwable t) {
NEIServerUtils.logOnce(
t,
stackTraces,
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
item.toString());
}
}
}
private static String getInformation(ItemStack stack, EntityPlayer player) {
try {
final List<String> tooltip = new ArrayList<>();
stack.getItem().addInformation(stack, player, tooltip, false);
return String.join("\n", tooltip);
} catch (Throwable ignored) {}
return "";
}
private static void updateOrdering(List<ItemStack> items) {
final Map<ItemStack, Integer> newOrdering = new HashMap<>();
if (!CollapsibleItems.isEmpty()) {
final HashMap<Integer, Integer> groups = new HashMap<>();
int orderIndex = 0;
for (ItemStack stack : items) {
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
if (groupIndex == -1) {
newOrdering.put(stack, orderIndex++);
} else {
if (!groups.containsKey(groupIndex)) {
groups.put(groupIndex, orderIndex++);
}
newOrdering.put(stack, groups.get(groupIndex));
}
}
} else {
int orderIndex = 0;
for (ItemStack stack : items) {
newOrdering.put(stack, orderIndex++);
}
}
ordering = newOrdering;
}
private static List<ItemStack> getPermutations(Item item) {
final List<ItemStack> permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item));
if (permutations.isEmpty()) {
item.getSubItems(item, null, permutations);
}
if (permutations.isEmpty()) {
damageSearch(item, permutations);
}
permutations.addAll(ItemInfo.itemVariants.get(item));
return permutations.stream()
.filter(
stack -> stack.getItem() != null && stack.getItem().delegate.name() != null
&& !ItemInfo.isHidden(stack))
.collect(Collectors.toList());
}
private static void runChecked(ItemStack stack, Runnable action, String reason) {
int hashOld = 0;
if (stack.hasTagCompound()) {
hashOld = stack.stackTagCompound.hashCode();
}
action.run();
if (stack.hasTagCompound() && hashOld != stack.stackTagCompound.hashCode()) {
FMLLog.warning(
"NEI: Forced tag update with reason (" + reason + ") for " + stack + "(" + stack.getItem() + ")");
}
}
private static void forceTagCompoundInitialization(ItemStack stack) {
Item item = stack.getItem();
if (item == null) {
return;
}
if (item instanceof IFluidContainerItem) {
IFluidContainerItem fluidItem = (IFluidContainerItem) item;
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
}
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
runChecked(stack, () -> item.getDamage(stack), "getDamage");
runChecked(stack, () -> item.showDurabilityBar(stack), "showDurabilityBar");
runChecked(stack, () -> item.getAttributeModifiers(stack), "getAttributeModifiers");
runChecked(stack, () -> item.getDurabilityForDisplay(stack), "getDurabilityForDisplay");
runChecked(stack, () -> item.getItemStackLimit(stack), "getItemStackLimit");
runChecked(stack, () -> item.getToolClasses(stack), "getToolClasses");
runChecked(stack, () -> item.getUnlocalizedNameInefficiently(stack), "getUnlocalizedNameInefficiently");
runChecked(stack, () -> item.hasEffect(stack), "hasEffect");
runChecked(stack, () -> item.getDigSpeed(stack, Blocks.stone, 0), "getDigSpeed");
/*
* unused... for now runChecked(stack, () -> GameRegistry.getFuelValue(stack), "getFuelValue");
* runChecked(stack, () -> item.getUnlocalizedName(stack), "getUnlocalizedName"); runChecked(stack, () ->
* item.doesContainerItemLeaveCraftingGrid(stack), "doesContainerItemLeaveCraftingGrid"); runChecked(stack, ()
* -> item.getItemUseAction(stack), "getItemUseAction"); runChecked(stack, () ->
* item.getMaxItemUseDuration(stack), "getMaxItemUseDuration"); runChecked(stack, () ->
* item.getPotionEffect(stack), "getPotionEffect"); runChecked(stack, () -> item.isPotionIngredient(stack),
* "isPotionIngredient"); runChecked(stack, () -> item.getRarity(stack), "getRarity"); runChecked(stack, () ->
* item.hasCustomEntity(stack), "hasCustomEntity"); runChecked(stack, () -> item.getSmeltingExperience(stack),
* "getSmeltingExperience"); runChecked(stack, () -> item.getMaxDamage(stack), "getMaxDamage");
* runChecked(stack, () -> item.getDamage(stack), "getDamage"); runChecked(stack, stack::isItemDamaged,
* "isItemDamaged"); runChecked(stack, () -> item.hasEffect(stack, 0), "hasEffect"); runChecked(stack, () ->
* item.getItemEnchantability(stack), "getItemEnchantability"); runChecked(stack, () ->
* item.isBeaconPayment(stack), "isBeaconPayment"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
* runChecked(stack, () -> item.getDamage(stack), "getDamage"); runChecked(stack, () -> item.getDamage(stack),
* "getDamage");
*/
}
@SuppressWarnings("unchecked")
private static List<ItemStack> gatherItems(ThreadOperationTimer timer) {
ItemSorter.instance.ordering.clear();
List<ItemStack> items = new ArrayList<>();
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
ItemStackSet unique = new ItemStackSet();
final List<Item> registryItems = new ArrayList<>();
for (Item item : (Iterable<Item>) Item.itemRegistry) registryItems.add(item);
registryItems.parallelStream().forEach(item -> {
if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return;
try {
timer.reset(item);
List<ItemStack> permutations = getPermutations(item);
timer.reset();
synchronized (itemMap) {
itemMap.putAll(item, permutations);
}
} catch (Throwable t) {
NEIServerConfig.logger.error(
"Removing item: {} from list.\n{}: {}",
item,
t.getMessage(),
Throwables.getStackTraceAsString(t));
erroredItems.add(item);
}
});
int index = 0;
for (Item item : (Iterable<Item>) Item.itemRegistry) {
for (ItemStack stack : itemMap.get(item)) {
ItemSorter.instance.ordering.put(stack, index++);
if (!unique.contains(stack)) {
unique.add(stack);
items.add(stack);
}
}
}
ItemList.itemMap = itemMap;
return items;
}
private static void buildItemCaches(List<ItemStack> items) {
SearchField.searchParser.clearCache();
TooltipFilter.clearCache();
InformationHandler.clearCache();
items.parallelStream().forEach(stack -> {
try {
forceTagCompoundInitialization(stack);
InformationHandler.populateStacks(stack);
} catch (Throwable t) {
NEIServerConfig.logger.error(
"Failed to build caches for: {}\n{}: {}",
stack,
t.getMessage(),
Throwables.getStackTraceAsString(t));
}
});
}
private static void indexCollapsibleItems(List<ItemStack> items) {
CollapsibleItems.clearCache();
items.parallelStream().forEach(CollapsibleItems::putItem);
}
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
@Override
public void execute() {
if (!NEIClientConfig.isEnabled() || NEIClientUtils.mc().thePlayer == null) return;
final ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
LayoutManager.itemsLoaded = true;
ItemList.loadFinished = false;
warmTooltips.stop();
List<ItemStack> items = gatherItems(timer);
if (interrupted()) return;
buildItemCaches(items);
indexCollapsibleItems(items);
if (interrupted()) return;
ItemSorter.sort(items);
if (interrupted()) return;
ItemList.items = items;
for (ItemsLoadedCallback callback : ItemList.loadCallbacks) callback.itemsLoaded();
if (interrupted()) return;
updateOrdering(ItemList.items);
ItemList.loadFinished = true;
RepairRecipeHandler.findRepairRecipesOnceParallel();
RecipeHandlerRef.clearCache();
FavoriteRecipes.reload();
SubsetWidget.updateHiddenItems();
updateFilter.restart();
warmTooltips.restart();
loadBookmarks.restart();
}
};
public static final RestartableTask warmTooltips = new RestartableTask("NEI Tooltip Caching") {
@Override
public void execute() {
if (!ItemList.loadFinished) return;
// Use the common pool, not ItemList.forkJoinPool, so it does not queue behind updateFilter.
ItemList.items.parallelStream().forEach(stack -> {
if (interrupted()) return;
try {
TooltipFilter.getSearchTooltip(stack);
} catch (Throwable ignored) {}
});
}
};
public static final RestartableTask loadBookmarks = new RestartableTask("NEI Bookmark Loading") {
@Override
public void execute() {
ItemPanels.bookmarkPanel.load();
}
};
public static final RestartableTask refreshItems = new RestartableTask("NEI Item Reordering") {
@Override
public void execute() {
if (!ItemList.loadFinished) return;
final List<ItemStack> items = new ArrayList<>(ItemList.items);
indexCollapsibleItems(items);
if (interrupted()) return;
ItemSorter.sort(items);
if (interrupted()) return;
ItemList.items = items;
updateOrdering(ItemList.items);
FavoriteRecipes.reload();
SubsetWidget.updateHiddenItems();
updateFilter.restart();
loadBookmarks.restart();
}
};
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
@Override
public void execute() {
if (!ItemList.loadFinished) return;
ItemFilter filter = ItemList.getItemListFilter();
ArrayList<ItemStack> filtered;
try {
filtered = ItemList.forkJoinPool.submit(
() -> ItemList.items.parallelStream().filter(filter::matches)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
} catch (InterruptedException | ExecutionException e) {
filtered = new ArrayList<>();
e.printStackTrace();
stop();
}
if (interrupted()) return;
filtered.sort(Comparator.comparingInt(ordering::get));
if (interrupted()) return;
ItemPanel.updateItemList(filtered);
}
};
}
+18 -2
View File
@@ -15,6 +15,7 @@ import net.minecraft.client.shader.Framebuffer;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.GuiInfo;
@@ -27,7 +28,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
protected static class ScreenCapture {
private long nextCacheRefresh = 0;
private Framebuffer framebuffer;
private final Framebuffer framebuffer;
public ScreenCapture() {
this.framebuffer = new Framebuffer(1, 1, true);
@@ -51,7 +52,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
}
public void captureScreen(Runnable callback, int mode) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
resetFramebuffer();
this.framebuffer.bindFramebuffer(false);
@@ -84,6 +85,21 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
} else {
this.framebuffer.framebufferClear();
}
// Copy stencil values (required by mui2)
OpenGlHelper.func_153171_g(GL30.GL_READ_FRAMEBUFFER, minecraft.getFramebuffer().framebufferObject);
OpenGlHelper.func_153171_g(GL30.GL_DRAW_FRAMEBUFFER, this.framebuffer.framebufferObject);
GL30.glBlitFramebuffer(
0,
0,
minecraft.displayWidth,
minecraft.displayHeight,
0,
0,
minecraft.displayWidth,
minecraft.displayHeight,
GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT,
GL11.GL_NEAREST);
}
public void renderCapturedScreen() {
@@ -244,7 +244,11 @@ public class LayoutStyleMinecraft extends LayoutStyle {
Image icon = b.getRenderIcon();
if (icon == null) {
int colour = tex == 2 ? 0xffffa0 : tex == 0 ? 0x601010 : 0xe0e0e0;
int labelColor = b.getRenderLabelColor();
int colour = labelColor != 0 ? labelColor
: tex == 2 ? ColorUtils.buttonLabelHover.getColor()
: tex == 0 ? ColorUtils.buttonLabelDisabled.getColor()
: ColorUtils.buttonLabelNormal.getColor();
drawStringC(b.getRenderLabel(), b.x + b.w / 2, b.y + (b.h - 8) / 2, colour);
} else {
@@ -54,6 +54,7 @@ public class NEIActions {
addAction("magnet", true);
addAction("enchant", true);
addAction("potion", true);
addAction("autocraft");
canDisable.add("dawn");
canDisable.add("noon");
@@ -263,6 +263,10 @@ public class NEIClientConfig {
.setComment("Require holding shift to move items when overlaying recipe").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.shiftOverlayRecipe", true));
tag.getTag("inventory.guirecipe.scrollPages").setComment("Disable page scroll when overlaying recipe")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.scrollPages", true));
tag.getTag("inventory.guirecipe.profile").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
@@ -307,7 +311,7 @@ public class NEIClientConfig {
super.onClick(button);
CollapsibleItems.saveStates();
CollapsibleItems.load();
LayoutManager.markItemsDirty();
ItemList.refreshItems.restart();
return true;
}
});
@@ -1021,7 +1025,11 @@ public class NEIClientConfig {
}.start();
} else {
SubsetWidget.loadHidden();
ItemList.loadItems.restart();
if (ItemList.loadFinished) {
ItemList.refreshItems.restart();
} else {
ItemList.loadItems.restart();
}
}
}
@@ -1045,7 +1053,9 @@ public class NEIClientConfig {
}
public static boolean autocraftingEnabled() {
return getBooleanSetting("inventory.autocrafting");
if (!getBooleanSetting("inventory.autocrafting")) return false;
if (hasSMPCounterpart) return permissableActions.contains("autocraft");
return true;
}
public static boolean favoritesEnabled() {
@@ -95,6 +95,7 @@ public class NEIClientUtils extends NEIServerUtils {
/** Formats a number with group separator and at most 2 fraction digits. */
private static final Map<Locale, DecimalFormat> decimalFormatters = new HashMap<>();
private static final DecimalFormat CHANCE_FORMAT = new DecimalFormat("##0.##%");
public static final int ALT_HASH = NEIKeyboardUtils.ALT_HASH;
public static final int SHIFT_HASH = NEIKeyboardUtils.SHIFT_HASH;
@@ -126,7 +127,7 @@ public class NEIClientUtils extends NEIServerUtils {
});
}
public static String formatNumber(long aNumber) {
public static String formatNumber(Number aNumber) {
if (NEIModContainer.isGTNHLibLoaded()) {
return NumberFormatUtil.formatNumber(aNumber);
@@ -135,7 +136,7 @@ public class NEIClientUtils extends NEIServerUtils {
return getDecimalFormat().format(aNumber);
}
public static String formatFluid(long aNumber) {
public static String formatFluid(Number aNumber) {
if (NEIModContainer.isGTNHLibLoaded()) {
return NumberFormatUtil.formatFluid(aNumber);
@@ -144,6 +145,10 @@ public class NEIClientUtils extends NEIServerUtils {
return getDecimalFormat().format(aNumber) + " L";
}
public static String formatChance(float chance) {
return CHANCE_FORMAT.format(chance);
}
public static void deleteHeldItem() {
deleteSlotStack(-999);
}
@@ -1,6 +1,7 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.getMousePosition;
import static codechicken.nei.NEIClientConfig.canCheatItem;
import java.awt.Point;
import java.util.LinkedList;
@@ -151,6 +152,8 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
&& slot != null
&& slot.getStack() != null
&& slot.isItemValid(slot.getStack())) {
if (!canCheatItem(slot.getStack())) return false;
NEIClientUtils.cheatItem(slot.getStack(), button, 1);
return true;
}
@@ -108,6 +108,7 @@ public class NEIServerConfig {
setDefaultFeature("save-state");
setDefaultFeature("item");
setDefaultFeature("delete");
setDefaultFeature("autocraft", "ALL");
setDefaultFeature("notify-item", "CONSOLE, OP");
}
@@ -13,6 +13,7 @@ import net.minecraftforge.oredict.OreDictionary;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.Badge;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.StackInfo;
@@ -21,12 +22,16 @@ import codechicken.nei.recipe.StackInfo;
*/
public class PositionedStack {
/** Maximum chance value representing 100% probability (1 unit = 0.01%). */
public static final int CHANCE_FULL = 10_000;
public int relx;
public int rely;
public ItemStack[] items;
// compatibility dummy
public ItemStack item;
protected int chance = CHANCE_FULL;
private boolean permutated = false;
public PositionedStack(Object object, int x, int y, boolean genPerms) {
@@ -82,6 +87,18 @@ public class PositionedStack {
for (ItemStack item : items) if (item.stackSize > i) item.stackSize = i;
}
public List<Badge> getBadges() {
return null;
}
public int getChance() {
return this.chance;
}
public void setChance(int chance) {
this.chance = chance;
}
public PositionedStack copy() {
PositionedStack pStack = new PositionedStack(
Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new),
@@ -89,6 +106,7 @@ public class PositionedStack {
rely,
false);
pStack.permutated = this.permutated;
pStack.chance = this.chance;
return pStack;
}
@@ -251,7 +251,7 @@ public class PresetsList {
updateSubsets();
CollapsibleItems.saveStates();
CollapsibleItems.load();
LayoutManager.markItemsDirty();
ItemList.refreshItems.restart();
}
private static void updateSubsets() {
@@ -111,7 +111,7 @@ public class SearchTokenParser {
clearCache();
}
public void clearCache() {
public synchronized void clearCache() {
this.providersCache.clear();
this.filtersCache.clear();
this.recipesCache.clear();
+42 -24
View File
@@ -6,7 +6,6 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -19,6 +18,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.opengl.GL11;
@@ -413,9 +413,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
}
/**
* All operations on this variable should be synchronised.
* Canonical {@link StackInfo#getItemStackGUID(ItemStack)} of every hidden item, stored by GUID so the state
* survives world reloads. All operations should be synchronised.
*/
private static final ItemStackSet hiddenItems = new ItemStackSet();
private static final Set<String> hiddenItems = new HashSet<>();
private static final Map<String, SubsetTag> tags = new HashMap<>();
private static final SubsetTag root = new SubsetTag("");
@@ -456,11 +457,16 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
}
public static boolean isHidden(ItemStack item) {
final String guid = StackInfo.getItemStackGUID(item);
synchronized (hiddenLock) {
return hiddenItems.contains(item);
return hiddenItems.contains(guid);
}
}
private static List<String> toGUIDs(List<ItemStack> items) {
return items.stream().map(StackInfo::getItemStackGUID).collect(Collectors.toList());
}
private static List<ItemStack> getItems(SubsetTag tag, List<ItemStack> items) {
items.addAll(tag.items);
@@ -472,19 +478,22 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
}
public static void showOnly(SubsetTag tag) {
final List<String> allItems = toGUIDs(getItems(root, new ArrayList<>()));
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
synchronized (hiddenLock) {
hiddenItems.clear();
hiddenItems.addAll(getItems(root, new ArrayList<>()));
hiddenItems.removeAll(getItems(tag, new ArrayList<>()));
hiddenItems.addAll(allItems);
hiddenItems.removeAll(tagItems);
}
calculateVisibility();
}
public static void setHidden(SubsetTag tag, boolean hidden) {
synchronized (hiddenLock) {
final List<ItemStack> tagItems = getItems(tag, new ArrayList<>());
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
synchronized (hiddenLock) {
if (hidden) {
hiddenItems.addAll(tagItems);
} else {
@@ -496,11 +505,13 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
}
public static void setHidden(ItemStack item, boolean hidden) {
final String guid = StackInfo.getItemStackGUID(item);
synchronized (hiddenLock) {
if (hidden) {
hiddenItems.add(item);
hiddenItems.add(guid);
} else {
hiddenItems.remove(item);
hiddenItems.remove(guid);
}
}
calculateVisibility();
@@ -582,13 +593,23 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
}
public static void loadHidden() {
final List<ItemStack> itemList = new LinkedList<>();
final Set<String> guids = new HashSet<>();
try {
final NBTTagList guidList = NEIClientConfig.world.nbt.getTagList("hiddenItems", 8);
for (int i = 0; i < guidList.tagCount(); i++) {
guids.add(guidList.getStringTagAt(i));
}
final NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10);
for (int i = 0; i < list.tagCount(); i++) {
itemList.add(ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i)));
final ItemStack stack = ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i));
if (stack != null) {
guids.add(StackInfo.getItemStackGUID(stack));
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading hiddenItems", e);
@@ -597,7 +618,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
synchronized (hiddenLock) {
hiddenItems.clear();
hiddenItems.addAll(itemList);
hiddenItems.addAll(guids);
}
}
@@ -606,8 +627,8 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
final NBTTagList list = new NBTTagList();
synchronized (hiddenLock) {
for (ItemStack stack : hiddenItems.values()) {
list.appendTag(stack.writeToNBT(new NBTTagCompound()));
for (String guid : hiddenItems) {
list.appendTag(new NBTTagString(guid));
}
}
@@ -689,20 +710,17 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
}
@Override
public String getRenderLabel() {
public int getRenderLabelColor() {
return NEIClientConfig.subsetWidgetOnTop() ? 0 : ColorUtils.subsetWidget.getColor();
}
@Override
public String getRenderLabel() {
if (NEIClientConfig.subsetWidgetOnTop()) {
return NEIClientUtils.translate("inventory.item_subsets");
} else {
final String translated = NEIClientUtils.translate("gui.button.label.subset");
if (!translated.startsWith("nei.")) { // Optional localization string for resource packs
return translated;
}
return EnumChatFormatting.DARK_PURPLE + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX))
+ EnumChatFormatting.RESET;
return String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX));
}
}
@Override
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState;
import codechicken.nei.recipe.IRecipeHandler;
@@ -32,6 +33,10 @@ public interface IOverlayHandler {
return false;
}
default boolean requireShiftForOverlayRecipe() {
return NEIClientConfig.requireShiftForOverlayRecipe();
}
default List<ItemOverlayState> presenceOverlay(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) {
final List<ItemOverlayState> itemPresenceSlots = new ArrayList<>();
final List<PositionedStack> ingredients = recipe.getIngredientStacks(recipeIndex);
@@ -5,6 +5,7 @@ import java.util.LinkedList;
import net.minecraft.world.World;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.config.GuiOptionList;
import codechicken.nei.config.OptionCycled;
public class NEIInfo {
@@ -13,7 +14,8 @@ public class NEIInfo {
public static void load(World world) {
OptionCycled modeOption = (OptionCycled) NEIClientConfig.getOptionList().getOption("inventory.cheatmode");
modeOption.parent.synthesizeEnvironment(false);
// kept alive so the WeakReference in Option.slotRef isn't GC'd before copyGlobals/cycle use it
GuiOptionList env = modeOption.parent.synthesizeEnvironment(false);
if (!modeOption.optionValid(modeOption.value())) {
modeOption.copyGlobals();
modeOption.cycle();
@@ -23,6 +23,7 @@ import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.SearchField;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkGroup;
@@ -646,8 +647,10 @@ public abstract class ShortcutInputHandler {
final List<ItemStack> playerInventory = AutoCraftingManager.getInventoryItems(guiContainer).values();
final RecipeId rootRecipeId = math.createMasterRoot();
for (BookmarkItem item : math.recipeIngredients) {
if (item.amount > 0 && rootRecipeId.equals(item.recipeId)) {
for (int i = 0; i < math.recipeIngredients.size(); i++) {
final BookmarkItem item = math.recipeIngredients.get(i);
if (item.getAmount() > 0 && rootRecipeId.equals(item.recipeId)) {
long amount = 0;
for (ItemStack stack : playerInventory) {
@@ -656,8 +659,12 @@ public abstract class ShortcutInputHandler {
}
}
if (amount >= item.amount) {
item.factor = item.amount = amount + item.amount - (amount % item.amount);
if (amount >= item.getAmount()) {
math.recipeIngredients.set(
i,
BookmarkItem.builder(item).multiplier(1)
.factor(amount + item.getAmount() - (amount % item.getAmount()))
.chance(PositionedStack.CHANCE_FULL).build());
}
}
}
@@ -24,7 +24,7 @@ import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemStackAmount;
import codechicken.nei.ItemStackSet;
import codechicken.nei.ItemsGrid;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.LayoutManager;
@@ -33,6 +33,7 @@ import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.recipe.chain.RecipeChainMath;
@@ -139,17 +140,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (group.collapsedRecipes.contains(collapsedRecipeId)) {
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId)
&& item.amount == item.factor) {
item.amount = 0;
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 1) {
item.multiplier = 0;
}
}
} else {
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.amount == 0) {
item.amount = item.factor;
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 0) {
item.multiplier = 1;
}
}
@@ -627,15 +627,17 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId && (recipeRelations == null || recipeRelations.contains(item.recipeId))) {
long amount = item.amount;
if (recipeId != null && group.crafting != null
&& group.crafting.calculatedItems.containsKey(itemIndex)
&& recipeId.equals(item.recipeId)) {
amount = group.crafting.calculatedItems.get(itemIndex).getCalculatedAmount();
chainItems.add(
item.copyWithMultiplier(
group.crafting.calculatedItems.get(itemIndex).getCalculatedMultiplier()));
} else {
chainItems.add(item.copy());
}
chainItems.add(item.copyWithAmount(amount));
}
}
@@ -896,34 +898,38 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
public void addRecipe(Recipe recipe, int multiplier, int groupId) {
final RecipeId recipeId = recipe.getRecipeId();
final List<ItemStack> results = recipe.getResults().stream().map(res -> res.getItemStack())
.collect(Collectors.toList());
final List<ItemStack> ingredients = recipe.getIngredients().stream().map(ingr -> ingr.getItemStack())
.collect(Collectors.toList());
final ItemStackSet seenResults = new ItemStackSet();
final ItemStackSet seenIngredients = new ItemStackSet();
final List<ItemStack> results = new ArrayList<>();
final List<ItemStack> ingredients = new ArrayList<>();
for (ItemStack stack : ItemStackAmount.of(results).values()) {
long factor = StackInfo.getAmount(stack);
for (RecipeIngredient result : recipe.getResults()) {
ItemStack stack = result.getItemStack();
if (!seenResults.contains(stack)) {
seenResults.add(stack);
results.add(stack);
}
}
for (RecipeIngredient ingr : recipe.getIngredients()) {
ItemStack stack = ingr.getItemStack();
if (!seenIngredients.contains(stack)) {
seenIngredients.add(stack);
ingredients.add(stack);
}
}
for (ItemStack stack : results) {
this.addItem(
BookmarkItem.of(
groupId,
StackInfo.withAmount(stack, factor * multiplier),
factor,
recipeId,
BookmarkItemType.RESULT),
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT).multiplier(multiplier)
.build(),
true);
}
for (ItemStack stack : ItemStackAmount.of(ingredients).values()) {
long factor = StackInfo.getAmount(stack);
for (ItemStack stack : ingredients) {
this.addItem(
BookmarkItem.of(
groupId,
StackInfo.withAmount(stack, factor * multiplier),
factor,
recipeId,
BookmarkItemType.INGREDIENT,
BookmarkItem.generatePermutations(stack, recipe)),
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier)
.build(),
true);
}
}
@@ -1133,8 +1139,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final Set<Long> sizes = new HashSet<>();
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == targetGroupId && item.amount > 0) {
sizes.add(Math.max(item.getStackSize(), item.factor / item.fluidCellAmount));
if (item.groupId == targetGroupId && item.getAmount() > 0) {
sizes.add(Math.max(item.getStackSize(), item.getStackSize(item.getAmount(1))));
items.add(item);
}
}
@@ -1143,14 +1149,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : items) {
long customFactor = (long) Math.ceil(item.amount / (double) gcd);
long customMultiplier = (long) (customFactor > 0 ? Math.ceil(item.amount / (double) customFactor) : 0);
final long amount = item.getAmount();
final long customFactor = (amount + gcd - 1) / gcd;
final long customMultiplier = customFactor > 0 ? ((amount + customFactor - 1) / customFactor) : 0;
multiplier = Math.min(shiftMultiplier(customMultiplier, shift, 1), multiplier);
}
for (BookmarkItem item : items) {
long customFactor = (long) Math.ceil(item.amount / (double) gcd);
item.amount = customFactor * multiplier;
final long amount = item.getAmount();
final long customFactor = (amount + gcd - 1) / gcd;
item.multiplier = item.getMultiplierFromAmount(customFactor * multiplier);
}
} else {
final Map<RecipeId, Long> recipeMultipliers = new HashMap<>();
@@ -1158,7 +1166,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final List<BookmarkItem> items = new ArrayList<>();
for (BookmarkItem item : this.bookmarkItems) {
if (item.factor > 0
if (!item.emptyFactor()
&& outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) {
final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE);
final boolean recipeInMiddle = group.crafting != null
@@ -1168,7 +1176,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
item.recipeId,
Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
shift,
recipeInMiddle ? 1 : 0),
multiplier));
@@ -1177,7 +1185,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
for (BookmarkItem item : items) {
item.amount = item.factor * recipeMultipliers.getOrDefault(item.recipeId, 0L);
item.multiplier = recipeMultipliers.getOrDefault(item.recipeId, 0L);
}
}
@@ -1197,10 +1205,10 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) {
multiplier = Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
shift,
recipeInMiddle ? 1 : 0),
multiplier);
@@ -1208,8 +1216,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) {
item.amount = item.factor * multiplier;
if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) {
item.multiplier = multiplier;
}
}
@@ -1217,7 +1225,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarkItem item : this.bookmarkItems) {
if (targetItem.equals(item)) {
item.amount = item.factor * shiftMultiplier(item.getMultiplier(), shift, 0);
item.multiplier = shiftMultiplier(item.multiplier, shift, 0);
break;
}
}
@@ -66,10 +66,14 @@ public class BookmarkGridGenerator {
while (itemIndex < size && absoluteSlotIndex != -1) {
final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex);
final BookmarkGroup group = this.grid.getGroup(bookmarkItem.groupId);
final boolean isCollapsedRecipeAnchor = group.crafting != null && bookmarkItem.recipeId != null
&& bookmarkItem.type != BookmarkItemType.INGREDIENT
&& group.collapsedRecipes.contains(bookmarkItem.recipeId);
if (group.crafting != null && (!group.crafting.calculatedItems.containsKey(itemIndex)
|| bookmarkItem.recipeId != null && !bookmarkItem.recipeId
.equals(group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) {
if (group.crafting != null && !isCollapsedRecipeAnchor
&& (!group.crafting.calculatedItems.containsKey(itemIndex)
|| bookmarkItem.recipeId != null && !bookmarkItem.recipeId.equals(
group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) {
itemIndex++;
} else if (group.collapsed) {
final List<Integer> results = getGroupResults(bookmarkItem.groupId, itemIndex, size);
@@ -189,13 +193,11 @@ public class BookmarkGridGenerator {
for (int shadowIndex : shadows) {
final boolean isIngredient = shadows.get(0) != shadowIndex
|| group.crafting.calculatedItems.get(shadowIndex).calculatedType == CalculatedType.INGREDIENT;
final BookmarkItem shadowBookmarkItem = BookmarkItem.of(
bookmarkItem.groupId,
bookmarkItem.itemStack,
1,
bookmarkItem.recipeId,
isIngredient ? BookmarkItemType.INGREDIENT : BookmarkItemType.RESULT,
Collections.emptyMap());
final BookmarkItem shadowBookmarkItem = BookmarkItem.builder(bookmarkItem.groupId, bookmarkItem.itemStack)
.multiplier(1).factor(StackInfo.getAmount(bookmarkItem.itemStack)).recipeId(bookmarkItem.recipeId)
.type(isIngredient ? BookmarkItemType.INGREDIENT : BookmarkItemType.RESULT)
.permutations(Collections.emptyMap()).build();
if ((absoluteSlotIndex = nextSlotIndex(
absoluteSlotIndex + 1,
@@ -320,7 +322,7 @@ public class BookmarkGridGenerator {
return group.crafting.calculatedItems.get(itemIndex);
} else {
BookmarkChainItem item = BookmarkChainItem.of(this.grid.getBookmarkItem(itemIndex));
item.setRealAmount(item.getShiftAmount());
item.setRealMultiplier(item.getCalculatedMultiplier());
return item;
}
}
@@ -333,7 +335,7 @@ public class BookmarkGridGenerator {
return new BookmarksGridSlot(
slotIndex,
itemIndex,
chainItem.getRealAmount(),
chainItem.getRealMultiplier(),
0,
0,
chainItem.getItem(),
@@ -343,9 +345,9 @@ public class BookmarkGridGenerator {
return new BookmarksGridSlot(
slotIndex,
itemIndex,
chainItem.getRealAmount(),
chainItem.getRealMultiplier(),
chainItem.getShiftAmount(),
chainItem.getCalculatedAmount(),
chainItem.getCalculatedMultiplier(),
chainItem.getItem(),
chainItem.calculatedType,
group);
@@ -2,6 +2,7 @@ package codechicken.nei.bookmark;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -10,6 +11,7 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
@@ -17,6 +19,147 @@ import codechicken.nei.recipe.StackInfo;
public class BookmarkItem {
public static class Builder {
private static Map<ItemStack, String> fuzzyPermutations = new HashMap<>();
private final int groupId;
private final ItemStack stack;
private long factor;
private long multiplier = -1;
private long chance = PositionedStack.CHANCE_FULL;
private RecipeId recipeId = null;
private Recipe recipe = null;
private BookmarkItemType type = BookmarkItemType.ITEM;
private Map<String, ItemStack> permutations = null;
private Builder(int groupId, ItemStack stack) {
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack);
this.groupId = groupId;
this.stack = stack;
this.factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1;
}
public Builder factor(long factor) {
this.factor = factor;
return this;
}
public Builder multiplier(long multiplier) {
this.multiplier = multiplier;
return this;
}
public Builder chance(long chance) {
this.chance = chance;
return this;
}
public Builder recipe(Recipe recipe) {
this.recipe = recipe;
this.recipeId = null;
return this;
}
public Builder recipeId(RecipeId recipeId) {
this.recipeId = recipeId;
this.recipe = null;
return this;
}
public Builder type(BookmarkItemType type) {
this.type = type;
return this;
}
public Builder permutations(Map<String, ItemStack> permutations) {
this.permutations = permutations;
return this;
}
public BookmarkItem build() {
final Map<String, ItemStack> perms;
long multiplier = 0;
if (this.permutations != null) {
perms = this.permutations;
} else if (this.type == BookmarkItemType.INGREDIENT) {
if (this.recipe != null) {
perms = Builder.generatePermutations(this.stack, this.recipe);
} else if (this.recipeId != null) {
perms = Builder.generatePermutations(this.stack, Recipe.of(this.recipeId));
} else {
perms = Builder.generatePermutations(this.stack);
}
} else {
perms = Builder.generatePermutations(this.stack);
}
if (this.multiplier >= 0) {
multiplier = this.multiplier;
} else if (this.factor > 0) {
multiplier = StackInfo.getAmount(this.stack) / this.factor;
}
return new BookmarkItem(
this.groupId,
multiplier,
this.factor,
this.chance,
this.stack,
perms,
this.recipeId != null ? this.recipeId : this.recipe != null ? this.recipe.getRecipeId() : null,
this.type);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack) {
return Collections.singletonMap(getItemGUID(stack), stack);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack, Recipe recipe) {
if (recipe != null) {
final RecipeIngredient ingr = recipe.getIngredients().stream()
.filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null);
if (ingr != null) {
final Map<String, ItemStack> permutations = new HashMap<>();
for (ItemStack ingrStack : ingr.getPermutations()) {
permutations.put(getItemGUID(ingrStack), ingrStack);
}
return permutations;
}
}
return generatePermutations(stack);
}
private static synchronized String getItemGUID(ItemStack stack) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
if (fluidStack != null) {
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
} else {
for (Map.Entry<ItemStack, String> entry : Builder.fuzzyPermutations.entrySet()) {
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) {
Builder.fuzzyPermutations.put(stack, entry.getValue());
return entry.getValue();
}
}
final String stackGUID = StackInfo.getItemStackGUID(stack);
Builder.fuzzyPermutations.put(stack, stackGUID);
return stackGUID;
}
}
}
public enum BookmarkItemType {
INGREDIENT,
@@ -32,150 +175,128 @@ public class BookmarkItem {
}
}
private static Map<ItemStack, String> fuzzyPermutations = new HashMap<>();
public int groupId;
public long amount = 0L;
public final int fluidCellAmount;
public ItemStack itemStack;
public Map<String, ItemStack> permutations;
public long factor;
public RecipeId recipeId;
public BookmarkItemType type = BookmarkItemType.ITEM;
protected BookmarkItem(int groupId, long amount, int fluidCellAmount, ItemStack itemStack,
Map<String, ItemStack> permutations, long factor, RecipeId recipeId, BookmarkItemType type) {
public long multiplier = 1L;
public final long factor;
public final int fluidCellAmount;
public final long chance;
protected BookmarkItem(int groupId, long multiplier, long factor, long chance, ItemStack itemStack,
Map<String, ItemStack> permutations, RecipeId recipeId, BookmarkItemType type) {
final FluidStack fluidStack = StackInfo.getFluid(itemStack);
if (fluidStack != null) {
this.fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(itemStack) ? fluidStack.amount : 1);
} else {
this.fluidCellAmount = 1;
}
this.groupId = groupId;
this.amount = amount;
this.fluidCellAmount = fluidCellAmount;
this.itemStack = itemStack;
this.permutations = permutations;
this.factor = factor;
this.recipeId = recipeId;
this.type = type == null ? BookmarkItemType.ITEM : type;
this.multiplier = multiplier;
this.factor = factor;
this.chance = chance;
}
public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, BookmarkItemType type,
Map<String, ItemStack> permutations) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
int fluidCellAmount = 1;
long amount;
public static Builder builder(int groupId, ItemStack stack) {
return new Builder(groupId, stack);
}
if (fluidStack != null) {
amount = fluidStack.amount * (long) Math.max(0, stack.stackSize);
fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(stack) ? fluidStack.amount : 1);
} else {
amount = StackInfo.getAmount(stack);
public static Builder builder(BookmarkItem item) {
return new Builder(item.groupId, item.itemStack).factor(item.factor).multiplier(item.multiplier)
.chance(item.chance).recipeId(item.recipeId).type(item.type).permutations(item.permutations);
}
public static Builder builder(int groupId, ItemStack stack, Recipe recipe, BookmarkItemType type) {
final List<RecipeIngredient> items = type == BookmarkItemType.RESULT ? recipe.getResults()
: recipe.getIngredients();
final Builder builder = builder(groupId, stack).type(type).recipe(recipe);
long amount = 0;
for (RecipeIngredient res : items) {
if (res.contains(stack)) {
amount += 1L * res.getAmount() * res.getChance();
}
}
return new BookmarkItem(
groupId,
amount,
fluidCellAmount,
stack,
permutations,
factor * fluidCellAmount,
recipeId,
type);
}
if ((amount % PositionedStack.CHANCE_FULL) == 0) {
builder.factor(amount / PositionedStack.CHANCE_FULL);
} else {
builder.factor(1).chance(amount);
}
public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, BookmarkItemType type) {
return of(
groupId,
stack,
factor,
recipeId,
type,
generatePermutations(stack, recipeId, type == BookmarkItemType.INGREDIENT));
return builder;
}
public static BookmarkItem of(int groupId, ItemStack stack) {
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack);
final long factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1;
return BookmarkItem.of(
groupId,
stack,
factor,
null,
BookmarkItemType.ITEM,
Collections.singletonMap(getItemGUID(stack), stack));
return builder(groupId, stack).build();
}
public BookmarkItem copyWithAmount(long amount) {
public BookmarkItem copyWithMultiplier(long multiplier) {
return new BookmarkItem(
this.groupId,
amount,
this.fluidCellAmount,
multiplier,
this.factor,
this.chance,
this.itemStack,
this.permutations,
this.factor,
this.recipeId,
this.type);
}
public BookmarkItem copyWithAmount(long amount) {
return copyWithMultiplier(getMultiplierFromAmount(amount));
}
public BookmarkItem copy() {
return copyWithAmount(this.amount);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack, RecipeId recipeId,
boolean isIngredient) {
if (isIngredient && recipeId != null) {
return generatePermutations(stack, Recipe.of(recipeId));
}
return Collections.singletonMap(getItemGUID(stack), stack);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack, Recipe recipe) {
if (recipe != null) {
final RecipeIngredient ingr = recipe.getIngredients().stream()
.filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null);
if (ingr != null) {
final Map<String, ItemStack> permutations = new HashMap<>();
for (ItemStack ingrStack : ingr.getPermutations()) {
permutations.put(getItemGUID(ingrStack), ingrStack);
}
return permutations;
}
}
return Collections.singletonMap(getItemGUID(stack), stack);
}
private static synchronized String getItemGUID(ItemStack stack) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
if (fluidStack != null) {
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
} else {
for (Map.Entry<ItemStack, String> entry : BookmarkItem.fuzzyPermutations.entrySet()) {
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) {
BookmarkItem.fuzzyPermutations.put(stack, entry.getValue());
return entry.getValue();
}
}
final String stackGUID = StackInfo.getItemStackGUID(stack);
BookmarkItem.fuzzyPermutations.put(stack, stackGUID);
return stackGUID;
}
return copyWithMultiplier(this.multiplier);
}
public boolean containsItems(BookmarkItem item) {
return this.permutations.keySet().stream().anyMatch(item.permutations::containsKey);
}
public long getAmount() {
return getAmount(this.multiplier);
}
public long getAmount(long multiplier) {
long amount = this.factor * multiplier;
if (this.fluidCellAmount > 1) {
amount = amount * this.fluidCellAmount;
}
if (this.chance != PositionedStack.CHANCE_FULL) {
if (this.type == BookmarkItemType.INGREDIENT) {
// For ingredients, round up the expected amount to be conservative.
amount = (amount * this.chance + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL;
} else {
// For results, round down the expected amount to be conservative.
amount = (amount * this.chance) / PositionedStack.CHANCE_FULL;
}
}
return amount;
}
public ItemStack getItemStack() {
return getItemStack(this.amount);
return getItemStack(getAmount());
}
public ItemStack getItemStack(long amount) {
@@ -183,23 +304,34 @@ public class BookmarkItem {
}
public long getStackSize() {
return getStackSize(this.amount);
return getStackSize(getAmount());
}
public long getStackSize(long amount) {
return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.fluidCellAmount) : 0);
if (this.fluidCellAmount > 1) {
amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount;
}
return amount;
}
public long getMultiplierFromAmount(long amount) {
if (this.factor <= 0 || this.chance <= 0) return 0;
if (this.fluidCellAmount > 1) {
amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount;
}
if (this.chance == PositionedStack.CHANCE_FULL) {
return (amount + this.factor - 1) / this.factor;
}
return (amount * PositionedStack.CHANCE_FULL + this.factor * this.chance - 1) / (this.factor * this.chance);
}
public long getMultiplier() {
return getMultiplier(this.amount);
}
public long getMultiplier(long amount) {
return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.factor) : 0);
}
public long getFactor() {
return this.factor / this.fluidCellAmount;
return this.multiplier;
}
public boolean equalsRecipe(BookmarkItem meta) {
@@ -210,6 +342,10 @@ public class BookmarkItem {
return groupId == this.groupId && recipeId != null && recipeId.equals(this.recipeId);
}
public boolean emptyFactor() {
return this.factor <= 0 || this.chance <= 0;
}
@Override
public int hashCode() {
return Objects.hash(this.groupId, this.fluidCellAmount, this.type.toInt(), this.recipeId);
@@ -25,6 +25,7 @@ import com.google.gson.JsonSyntaxException;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.PositionedStack;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.StackInfo;
@@ -249,30 +250,36 @@ public class BookmarkStorage {
ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
if (itemStack != null) {
RecipeId recipeId = null;
boolean isFluid = itemStackNBT.hasKey("gtFluidName");
int groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt()
int groupId = jsonObject.has("groupId") && grid.groups.containsKey(jsonObject.get("groupId").getAsInt())
? jsonObject.get("groupId").getAsInt()
: BookmarkGrid.DEFAULT_GROUP_ID;
int factor = jsonObject.has("factor") ? Math.abs(jsonObject.get("factor").getAsInt()) : (isFluid ? 144 : 1);
BookmarkItemType type = jsonObject.has("type") ? BookmarkItemType.fromInt(jsonObject.get("type").getAsInt())
: null;
final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack);
if (type == null) {
// old format
type = jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()
? BookmarkItemType.INGREDIENT
: BookmarkItemType.RESULT;
if (jsonObject.has("type")) {
builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()));
} else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) {
builder.type(BookmarkItemType.INGREDIENT);
} else {
builder.type(BookmarkItemType.RESULT);
}
if (jsonObject.has("multiplier")) {
builder.multiplier(jsonObject.get("multiplier").getAsInt());
}
if (jsonObject.has("factor")) {
builder.factor(jsonObject.get("factor").getAsInt());
}
if (jsonObject.has("chance")) {
builder.chance(jsonObject.get("chance").getAsInt());
}
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
recipeId = RecipeId.of(recipeJson);
builder.recipeId(RecipeId.of(recipeJson));
}
if (!grid.groups.containsKey(groupId)) {
groupId = BookmarkGrid.DEFAULT_GROUP_ID;
}
grid.addItem(BookmarkItem.of(groupId, itemStack, factor, recipeId, type), false);
grid.addItem(builder.build(), false);
}
return itemStack != null;
@@ -321,7 +328,8 @@ public class BookmarkStorage {
JsonObject row = new JsonObject();
row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(item.getItemStack())));
row.add("factor", new JsonPrimitive(item.getFactor()));
row.add("multiplier", new JsonPrimitive(item.multiplier));
row.add("factor", new JsonPrimitive(item.factor));
row.add("type", new JsonPrimitive(item.type.toInt()));
if (item.groupId != BookmarkGrid.DEFAULT_GROUP_ID) {
@@ -332,6 +340,10 @@ public class BookmarkStorage {
row.add("recipeId", item.recipeId.toJsonObject());
}
if (item.chance != PositionedStack.CHANCE_FULL) {
row.add("chance", new JsonPrimitive(item.chance));
}
strings.add(NBTJson.toJson(row));
} catch (JsonSyntaxException e) {
NEIClientConfig.logger.error("Failed to stringify bookmarked ItemStack to json string");
@@ -14,6 +14,7 @@ import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.NEIClientUtils.Alignment;
import codechicken.nei.PositionedStack;
import codechicken.nei.bookmark.BookmarkGrid.BookmarkMouseContext;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.RecipeChainDetails.CalculatedType;
@@ -63,18 +64,18 @@ public class BookmarksGridSlot extends ItemsGridSlot {
protected final boolean isFirstOutput;
protected final int rowIndex;
public BookmarksGridSlot(int slotIndex, int itemIndex, long realAmount, long shiftAmount, long calculatedAmount,
BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) {
public BookmarksGridSlot(int slotIndex, int itemIndex, long realMultiplier, long shiftAmount,
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) {
super(
slotIndex,
itemIndex,
calculatedType != null ? bookmarkItem.getItemStack(calculatedAmount)
: bookmarkItem.getItemStack(realAmount));
calculatedType != null ? bookmarkItem.getItemStack(bookmarkItem.getAmount(calculatedMultiplier))
: bookmarkItem.getItemStack(bookmarkItem.getAmount(realMultiplier)));
this.emptyItemStack = bookmarkItem.getItemStack(0);
this.realAmount = realAmount;
this.realAmount = bookmarkItem.getAmount(realMultiplier);
this.shiftAmount = shiftAmount;
this.calculatedAmount = calculatedAmount;
this.calculatedAmount = calculatedType != null ? bookmarkItem.getAmount(calculatedMultiplier) : this.realAmount;
this.group = group;
this.calculatedType = calculatedType;
@@ -83,8 +84,8 @@ public class BookmarksGridSlot extends ItemsGridSlot {
this.isOutputRecipe = group.crafting != null && group.crafting.outputRecipes.contains(bookmarkItem.recipeId);
this.isFluidDisplay = StackInfo.itemStackToNBT(bookmarkItem.itemStack).hasKey("gtFluidName");
this.realMultiplier = bookmarkItem.getMultiplier(realAmount);
this.calculatedMultiplier = bookmarkItem.getMultiplier(calculatedAmount);
this.realMultiplier = realMultiplier;
this.calculatedMultiplier = calculatedMultiplier;
if (this.group.crafting != null && this.group.crafting.calculatedItems.containsKey(this.itemIndex)) {
this.realType = this.group.crafting.calculatedItems.get(this.itemIndex).calculatedType
@@ -347,17 +348,22 @@ public class BookmarksGridSlot extends ItemsGridSlot {
}
protected void drawStackSize(Rectangle4i rect, ShownItemType shownItemType) {
boolean drawChance = this.calculatedMultiplier > 0;
long amount = this.calculatedAmount;
if (shownItemType == null || shownItemType == ShownItemType.REAL || this.calculatedType == null) {
amount = this.realAmount;
drawChance = this.realMultiplier > 0;
} else if (shownItemType == ShownItemType.SHIFT) {
amount = this.shiftAmount;
drawChance = false;
}
if (amount > 0 || shownItemType == ShownItemType.REAL && this.bookmarkItem.factor > 0) {
if (amount > 0 || this.bookmarkItem.chance != PositionedStack.CHANCE_FULL && drawChance
|| shownItemType == ShownItemType.REAL && !this.bookmarkItem.emptyFactor()) {
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
final long stackSize = this.bookmarkItem.getStackSize(amount);
int color = 0xFFFFFF;
String amountString = "";
if (stackSize < 10_000) {
@@ -370,11 +376,16 @@ public class BookmarksGridSlot extends ItemsGridSlot {
amountString += "L";
}
if (this.bookmarkItem.chance != PositionedStack.CHANCE_FULL) {
amountString = "~" + amountString;
color = 0xFFAA00;
}
NEIClientUtils.drawNEIOverlayText(
amountString,
new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2),
panelFactor,
0xFFFFFF,
color,
true,
this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
}
@@ -42,34 +42,35 @@ public class RecipeChainDetails {
private final BookmarkItem item;
private long realAmount = 0L;
private long realMultiplier = 0L;
private long shiftAmount = 0L;
private long calculatedAmount = 0L;
private long calculatedMultiplier = 0L;
public CalculatedType calculatedType;
protected BookmarkChainItem(BookmarkItem item, long shiftItem, long calculatedItem,
protected BookmarkChainItem(BookmarkItem item, long shiftItem, long calculatedMultiplier,
CalculatedType calculatedType) {
this.item = item.copy();
this.shiftAmount = shiftItem;
this.calculatedAmount = calculatedItem;
this.calculatedMultiplier = calculatedMultiplier;
this.calculatedType = calculatedType;
}
public static BookmarkChainItem of(BookmarkItem item, long shiftItem, long calculatedAmount,
public static BookmarkChainItem of(BookmarkItem item, long shiftItem, long calculatedMultiplier,
CalculatedType calculatedType) {
return new BookmarkChainItem(item, shiftItem, calculatedAmount, calculatedType);
return new BookmarkChainItem(item, shiftItem, calculatedMultiplier, calculatedType);
}
public static BookmarkChainItem of(BookmarkItem item, long shiftItem, CalculatedType calculatedType) {
return new BookmarkChainItem(item, shiftItem, item.amount, calculatedType);
return new BookmarkChainItem(item, shiftItem, item.getMultiplier(), calculatedType);
}
public static BookmarkChainItem of(BookmarkItem item) {
final long amount = item.getAmount();
return new BookmarkChainItem(
item,
item.amount,
item.amount,
amount,
item.getMultiplier(),
item.type == BookmarkItemType.INGREDIENT ? CalculatedType.INGREDIENT : CalculatedType.RESULT);
}
@@ -77,30 +78,34 @@ public class RecipeChainDetails {
return this.item.getItemStack(amount);
}
public void setRealAmount(long amount) {
this.realAmount = amount;
public void setRealMultiplier(long multiplier) {
this.realMultiplier = multiplier;
}
public long getRealAmount() {
return this.realAmount;
public long getRealMultiplier() {
return this.realMultiplier;
}
public void setCalculatedMultiplier(long multiplier) {
this.calculatedMultiplier = multiplier;
}
public long getCalculatedMultiplier() {
return this.calculatedMultiplier;
}
public long getShiftAmount() {
return this.shiftAmount;
}
public long getCalculatedAmount() {
return this.calculatedAmount;
}
public BookmarkItem getItem() {
return this.item;
}
public void append(long amount, long calculatedAmount) {
this.shiftAmount += amount;
this.calculatedAmount += calculatedAmount;
this.item.amount = this.calculatedAmount;
this.calculatedMultiplier += this.item.getMultiplierFromAmount(calculatedAmount);
this.item.multiplier = this.calculatedMultiplier;
}
}
@@ -180,10 +185,9 @@ public class RecipeChainDetails {
if (itemIndex >= 0 && this.calculatedItems.containsKey(itemIndex)) {
final BookmarkItem item = entry.getValue();
if (this.recipeInMiddle.contains(item.recipeId)) {
this.calculatedItems.get(itemIndex)
.setRealAmount(Math.max(0, item.factor * (item.getMultiplier() - 1)));
this.calculatedItems.get(itemIndex).setRealMultiplier(Math.max(0, item.getMultiplier() - 1));
} else {
this.calculatedItems.get(itemIndex).setRealAmount(item.amount);
this.calculatedItems.get(itemIndex).setRealMultiplier(item.getMultiplier());
}
}
}
@@ -239,7 +243,7 @@ public class RecipeChainDetails {
for (BookmarkItem item : chainItems) {
if (recipeRelations.contains(item.recipeId)) {
subChainItems.add(item.copyWithAmount(recipeId.equals(item.recipeId) ? item.amount : 0));
subChainItems.add(item.copyWithMultiplier(recipeId.equals(item.recipeId) ? item.multiplier : 0));
}
}
@@ -258,7 +262,8 @@ public class RecipeChainDetails {
}
if (this.calculatedItems.containsKey(itemIndex)) {
this.calculatedItems.get(itemIndex).setRealAmount(value.getShiftAmount());
this.calculatedItems.get(itemIndex)
.setRealMultiplier(value.getItem().getMultiplierFromAmount(value.getShiftAmount()));
}
}
@@ -272,18 +277,19 @@ public class RecipeChainDetails {
for (RecipeId relRecipeId : recipeRelations) {
for (BookmarkItem item : math.recipeResults) {
if (item.recipeId.equals(relRecipeId)) {
final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L);
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
if (item.recipeId.equals(recipeId)) {
multiplier.put(item.recipeId, item.getMultiplier());
items.computeIfAbsent(
StackInfo.getItemStackGUID(item.itemStack),
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT))
.append(amount, item.amount);
.append(amount, item.getAmount());
} else if (amount > 0) {
items.computeIfAbsent(
StackInfo.getItemStackGUID(item.itemStack),
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT)).append(amount, amount);
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT))
.append(amount, item.getAmount());
}
}
}
@@ -292,17 +298,17 @@ public class RecipeChainDetails {
if (item.recipeId.equals(relRecipeId)) {
final BookmarkItem prefItem = math.preferredItems.get(item);
final long amount = math.requiredAmount.containsKey(prefItem) ? 0
: math.requiredAmount.getOrDefault(item, item.amount);
: math.requiredAmount.getOrDefault(item, item.getAmount());
final long refAmount = prefItem != null && !topLevelRecipes.contains(prefItem.recipeId)
? math.requiredAmount.getOrDefault(prefItem, 0L)
: 0;
if (amount != 0 || item.amount > refAmount && math.requiredAmount.containsKey(prefItem)
if (amount != 0 || item.getAmount() > refAmount && math.requiredAmount.containsKey(prefItem)
|| recipeId.equals(item.recipeId) && multiplier.get(recipeId) == 0) {
items.computeIfAbsent(
StackInfo.getItemStackGUID(item.itemStack),
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.INGREDIENT))
.append(amount, item.amount - refAmount);
.append(amount, item.getAmount() - refAmount);
}
}
}
@@ -312,7 +318,7 @@ public class RecipeChainDetails {
}
private void generateResult(RecipeChainMath math, BookmarkItem item, int itemIndex) {
final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L);
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
final CalculatedType calculatedType = math.outputRecipes.containsKey(item.recipeId) ? CalculatedType.RESULT
: CalculatedType.REMAINDER;
@@ -322,7 +328,7 @@ public class RecipeChainDetails {
private void generateIngredient(RecipeChainMath math, BookmarkItem item, int itemIndex) {
final BookmarkItem prefItem = math.preferredItems.get(item);
final long amount = math.requiredAmount.containsKey(prefItem) ? 0
: math.requiredAmount.getOrDefault(item, item.amount);
: math.requiredAmount.getOrDefault(item, item.getAmount());
this.calculatedItems.put(itemIndex, BookmarkChainItem.of(item, amount, CalculatedType.INGREDIENT));
}
@@ -8,13 +8,15 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import net.minecraft.command.CommandBase;
@@ -39,6 +41,7 @@ import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.RecipeHandlerQuery;
import codechicken.nei.recipe.TemplateRecipeHandler;
import codechicken.nei.util.NBTJson;
public class CommandRecipeId extends CommandBase {
@@ -125,12 +128,16 @@ public class CommandRecipeId extends CommandBase {
private ArrayList<ICraftingHandler> craftinghandlers = new ArrayList<>();
private ArrayList<ICraftingHandler> serialCraftingHandlers = new ArrayList<>();
protected final ICommandSender sender;
protected final File currFile;
protected final boolean fastDump;
public ProcessDumpThread(ICommandSender sender, File currFile) {
public ProcessDumpThread(ICommandSender sender, File currFile, boolean fastDump) {
this.sender = sender;
this.currFile = currFile;
this.fastDump = fastDump;
ClientHandler.loadSettingsFile("recipeidblacklist.cfg", lines -> {
final Set<String> names = lines.collect(Collectors.toSet());
@@ -150,38 +157,10 @@ public class CommandRecipeId extends CommandBase {
sendChatInfoMessage(sender, "nei.chat.recipeid.dump.start");
try (BufferedWriter writer = Files.newBufferedWriter(currFile.toPath(), StandardCharsets.UTF_8)) {
int total = ItemList.items.size();
int count = 0;
for (ItemStack stack : ItemList.items) {
if (count % 1000 == 0) {
NEIClientConfig.logger.info(
"({}/{}). Processing {} crafting recipes...",
count,
total,
stack.getDisplayName());
}
count++;
for (ICraftingHandler handler : getCraftingHandlers(stack)) {
for (int index = 0, num = handler.numRecipes(); index < num; index++) {
try {
final Recipe recipe = Recipe.of(handler, index);
if (!recipe.getIngredients().isEmpty() && !recipe.getResults().isEmpty()) {
writer.write(NBTJson.toJson(recipe.getRecipeId().toJsonObject()));
writer.newLine();
}
} catch (Exception ex) {
NEIClientConfig.logger.error(
"Found Broken RecipeId {}:{}",
GuiRecipeTab.getHandlerInfo(handler).getHandlerName(),
stack,
ex);
}
}
}
if (fastDump) {
processFastDump(writer);
} else {
processDump(writer);
}
} catch (Exception e) {
@@ -192,15 +171,118 @@ public class CommandRecipeId extends CommandBase {
sendChatInfoMessage(sender, "nei.chat.recipeid.dump.finish");
}
/**
* + technically more reliable and supports some weird handlers
* <p>
* - very very very slow, took 1 hour to get a dump in GTNH on M1 in background
* <p>
* - generates a lot of duplicates, up to 75% of file size is duplicates (and it's 300mb out of 400mb...)
*/
private void processDump(BufferedWriter writer) throws IOException {
final int total = ItemList.items.size();
int count = 0;
for (ItemStack stack : ItemList.items) {
if (count % 1000 == 0) {
NEIClientConfig.logger
.info("({}/{}). Processing {} crafting recipes...", count, total, stack.getDisplayName());
}
count++;
for (ICraftingHandler handler : getCraftingHandlers(stack)) {
writeLines(writer, collectRecipeLines(handler, stack.toString()));
}
}
}
/**
* - may skip some handlers if they're not TemplateRecipeHandler / just weird ones (like gendustry handlers)
* <p>
* + but in terms of GT recipes everything is dumped, and surprisingly it catches even more GT recipes than the
* regular dumper (in particular, it has a lot of new hammer, extruder, blast furnace, etc. recipes)
* <p>
* + way faster, took like 2.5 minutes
* <p>
* + doesn't have nearly as many duplicates, but something can still slip in, for example from
* FuelRecipeHandler. The dump has a size of only 100 MB instead of 400 MB from the regular dumper
*/
private void processFastDump(BufferedWriter writer) throws IOException {
final ArrayList<ICraftingHandler> handlers = getCraftingHandlers();
final int total = handlers.size();
final AtomicInteger count = new AtomicInteger();
try {
final List<List<String>> linesByHandler = ItemList.forkJoinPool.submit(() -> {
return handlers.parallelStream().map(handler -> {
final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
final List<String> lines = collectRecipeLines(handler, handlerName);
final int current = count.incrementAndGet();
NEIClientConfig.logger
.info("({}/{}). Processed {} handler recipes.", current, total, handlerName);
return lines;
}).collect(Collectors.toList());
}).get();
for (List<String> lines : linesByHandler) {
writeLines(writer, lines);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted while dumping RecipeId", e);
} catch (ExecutionException e) {
throw new IOException("Error while dumping RecipeId", e);
}
}
private List<String> collectRecipeLines(ICraftingHandler handler, String context) {
final List<String> lines = new ArrayList<>(handler.numRecipes());
for (int index = 0, num = handler.numRecipes(); index < num; index++) {
try {
final Recipe recipe = Recipe.of(handler, index);
if (!recipe.getIngredients().isEmpty() && !recipe.getResults().isEmpty()) {
lines.add(NBTJson.toJson(recipe.getRecipeId().toJsonObject()));
}
} catch (Exception ex) {
NEIClientConfig.logger.error(
"Found Broken RecipeId {}:{}",
GuiRecipeTab.getHandlerInfo(handler).getHandlerName(),
context,
ex);
}
}
return lines;
}
private void writeLines(BufferedWriter writer, List<String> lines) throws IOException {
for (String line : lines) {
writer.write(line);
writer.newLine();
}
}
private ArrayList<ICraftingHandler> getCraftingHandlers(Object... results) {
return new RecipeHandlerQuery<>(
h -> h.getRecipeHandler("item", results),
handler -> handler.getRecipeHandler("item", results),
this.craftinghandlers,
this.serialCraftingHandlers,
"Error while looking up crafting recipe",
"outputId: item",
"results: " + Arrays.toString(results))
.runWithProfiling(NEIClientUtils.translate("recipe.concurrent.crafting"));
"outputId: item").runWithProfiling(NEIClientUtils.translate("recipe.concurrent.crafting"));
}
private ArrayList<ICraftingHandler> getCraftingHandlers() {
return new RecipeHandlerQuery<>(handler -> {
if (handler instanceof TemplateRecipeHandler templateHandler) {
return templateHandler.getAllRecipeHandler();
}
return handler.getRecipeHandler("all");
},
this.craftinghandlers,
this.serialCraftingHandlers,
"Error while looking up crafting recipe",
"outputId: all").runWithProfiling(NEIClientUtils.translate("recipe.concurrent.crafting"));
}
}
@@ -212,7 +294,7 @@ public class CommandRecipeId extends CommandBase {
@Override
public String getCommandUsage(ICommandSender sender) {
return "/recipeid dump <filename> OR /recipeid diff <prev-filename> <curr-filename> [subset name]";
return "/recipeid [dump|fast-dump] <filename> OR /recipeid diff <prev-filename> <curr-filename> <subset name>";
}
@Override
@@ -220,12 +302,27 @@ public class CommandRecipeId extends CommandBase {
return 0;
}
@Override
public List<String> addTabCompletionOptions(ICommandSender sender, String[] args) {
if (args.length == 1) {
return getListOfStringsMatchingLastWord(args, "dump", "fast-dump", "diff");
}
if ("diff".equals(args[0]) && (args.length == 2 || args.length == 3)) {
return getListOfStringsMatchingLastWord(args, getRecipeIdFilenames());
}
return Collections.emptyList();
}
@Override
public void processCommand(ICommandSender sender, String[] args) {
final String command = args.length == 0 ? null : args[0];
if ("dump".equals(command)) {
processDumpCommand(sender, args);
processDumpCommand(sender, args, false);
} else if ("fast-dump".equals(command)) {
processDumpCommand(sender, args, true);
} else if ("diff".equals(command)) {
processDiffCommand(sender, args);
} else {
@@ -263,7 +360,7 @@ public class CommandRecipeId extends CommandBase {
(new ProcessDiffThread(sender, prevFilename, currFilename, diffFilename)).start();
}
protected void processDumpCommand(ICommandSender sender, String[] args) {
protected void processDumpCommand(ICommandSender sender, String[] args, boolean fastDump) {
if (args.length > 2) {
sendChatErrorMessage(sender, "nei.chat.recipeid.many_params", getCommandUsage(sender));
@@ -274,13 +371,30 @@ public class CommandRecipeId extends CommandBase {
final String currFilename = args.length == 2 ? args[1] : "recipeId";
if (!dir.exists()) dir.mkdirs();
(new ProcessDumpThread(sender, getFile(currFilename))).start();
(new ProcessDumpThread(sender, getFile(currFilename), fastDump)).start();
}
private static File getFile(String filename) {
return new File(CommonUtils.getMinecraftDir(), "recipeid/" + filename + ".json");
}
private static String[] getRecipeIdFilenames() {
final File dir = new File(CommonUtils.getMinecraftDir(), "recipeid");
final File[] files = dir.listFiles((currentDir, name) -> name.endsWith(".json"));
if (files == null || files.length == 0) {
return new String[] {};
}
final String[] names = new String[files.length];
for (int i = 0; i < files.length; i++) {
final String name = files[i].getName();
names[i] = name.substring(0, name.length() - ".json".length());
}
return names;
}
private static void sendChatInfoMessage(ICommandSender sender, String translationKey, Object... args) {
sender.addChatMessage(
new ChatComponentTranslation(translationKey, args)
@@ -92,8 +92,8 @@ public abstract class DataDumper extends Option {
}
public Rectangle4i dumpButtonSize() {
int width = 80;
return new Rectangle4i(slot.slotWidth() - width, 0, width, 20);
int width = 50;
return new Rectangle4i(getSlot().slotWidth() - width, 0, width, 20);
}
public Rectangle4i modeButtonSize() {
@@ -18,6 +18,7 @@ import org.lwjgl.input.Keyboard;
import codechicken.core.gui.GuiCCButton;
import codechicken.core.gui.GuiScreenWidget;
import codechicken.lib.config.ConfigTag;
import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.HUDRenderer;
@@ -27,12 +28,16 @@ public class GuiHighlightTips extends GuiScreenWidget {
private final String name;
private GuiCCButton toggleButton;
private final Option opt;
private final GuiOptionList parentGui;
private final boolean world;
private Point dragDown;
public GuiHighlightTips(Option opt) {
super(80, 20);
this.opt = opt;
name = opt.name;
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
this.world = parentGui.worldConfig();
}
@Override
@@ -48,7 +53,7 @@ public class GuiHighlightTips extends GuiScreenWidget {
@Override
public void actionPerformed(String ident, Object... params) {
if (ident.equals("show")) {
opt.getTag(name).setBooleanValue(!show());
getTag(name).setBooleanValue(!show());
updateNames();
}
}
@@ -58,13 +63,21 @@ public class GuiHighlightTips extends GuiScreenWidget {
}
private boolean show() {
return opt.renderTag(name).getBooleanValue();
return renderTag(name).getBooleanValue();
}
private ConfigTag getTag(String s) {
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
}
private ConfigTag renderTag(String s) {
return (world && opt.worldSpecific(s) ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
}
@Override
public void keyTyped(char c, int keycode) {
if (keycode == Keyboard.KEY_ESCAPE || keycode == Keyboard.KEY_BACK) {
Minecraft.getMinecraft().displayGuiScreen(opt.slot.getGui());
Minecraft.getMinecraft().displayGuiScreen(parentGui);
return;
}
super.keyTyped(c, keycode);
@@ -82,7 +95,7 @@ public class GuiHighlightTips extends GuiScreenWidget {
}
public Point getPos() {
return new Point(opt.renderTag(name + ".x").getIntValue(), opt.renderTag(name + ".y").getIntValue());
return new Point(renderTag(name + ".x").getIntValue(), renderTag(name + ".y").getIntValue());
}
public Dimension sampleSize() // copied from HUDManager when running with the sample for this gui
@@ -148,8 +161,8 @@ public class GuiHighlightTips extends GuiScreenWidget {
}
private void setPos(Point p) {
opt.getTag(name).setBooleanValue(show()); // duplicates global tag for the option gui if in world mode
opt.getTag(name + ".x").setIntValue(p.x);
opt.getTag(name + ".y").setIntValue(p.y);
getTag(name).setBooleanValue(show()); // duplicates global tag for the option gui if in world mode
getTag(name + ".x").setIntValue(p.x);
getTag(name + ".y").setIntValue(p.y);
}
}
@@ -43,6 +43,7 @@ public class GuiItemIconDumper extends GuiScreen {
}
private Option opt;
private final GuiOptionList parentGui;
private int drawIndex;
private int parseIndex;
private final File dir = new File(CommonUtils.getMinecraftDir(), "dumps/itempanel_icons");
@@ -52,6 +53,7 @@ public class GuiItemIconDumper extends GuiScreen {
public GuiItemIconDumper(Option opt, int iconSize) {
this.opt = opt;
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
this.iconSize = iconSize;
borderSize = iconSize / 16;
boxSize = iconSize + borderSize * 2;
@@ -62,7 +64,7 @@ public class GuiItemIconDumper extends GuiScreen {
}
private void returnScreen(IChatComponent msg) {
Minecraft.getMinecraft().displayGuiScreen(opt.slot.getGui());
Minecraft.getMinecraft().displayGuiScreen(parentGui);
NEIClientUtils.printChatMessage(msg);
}
@@ -7,11 +7,14 @@ import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import org.lwjgl.opengl.GL11;
import codechicken.lib.config.ConfigTag;
import codechicken.lib.math.MathHelper;
import codechicken.nei.ItemList;
import codechicken.nei.ItemSorter;
import codechicken.nei.ItemSorter.SortEntry;
import codechicken.nei.LayoutManager;
@@ -48,6 +51,8 @@ public class GuiItemSorter extends GuiOptionPane {
public final Option opt;
public final List<SortItem> slots = new ArrayList<>();
public final List<SortEntry> list;
private final GuiOptionList parentGui;
private final boolean world;
boolean dragging;
public SortItem dragged;
@@ -56,7 +61,9 @@ public class GuiItemSorter extends GuiOptionPane {
public GuiItemSorter(Option opt) {
this.opt = opt;
list = ItemSorter.fromSaveString(opt.renderTag().getValue());
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
this.world = parentGui.worldConfig();
list = ItemSorter.fromSaveString(renderTag(opt.configName()).getValue());
for (SortEntry e : list) slots.add(new SortItem(e));
}
@@ -98,7 +105,7 @@ public class GuiItemSorter extends GuiOptionPane {
@Override
public GuiScreen getParentScreen() {
return opt.slot.getGui();
return parentGui;
}
@Override
@@ -114,16 +121,25 @@ public class GuiItemSorter extends GuiOptionPane {
list.remove(dragged.e);
list.add(nslot, dragged.e);
opt.getTag().setValue(ItemSorter.getSaveString(list));
LayoutManager.markItemsDirty();
getTag(opt.configName()).setValue(ItemSorter.getSaveString(list));
if (opt.activeTag() == opt.getTag()) {
if (opt.activeTag() == getTag(opt.configName())) {
ItemSorter.list = new ArrayList<>(list);
}
ItemList.refreshItems.restart();
}
}
}
private ConfigTag getTag(String s) {
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
}
private ConfigTag renderTag(String s) {
return (world && opt.worldSpecific(s) ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
}
public SortItem itemAt(int mx, int my) {
if (!pane.windowBounds().contains(mx, my)) return null;
@@ -12,6 +12,7 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.core.gui.GuiScreenWidget;
import codechicken.lib.config.ConfigTag;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.vec.Rectangle4i;
@@ -21,7 +22,7 @@ import codechicken.nei.guihook.GuiContainerManager;
public class GuiPanelSettings extends GuiScreenWidget {
private final Option opt;
private final GuiOptionList parentGui;
private PanelPlaceholder bookmarksPanel;
private PanelPlaceholder itemsPanel;
@@ -54,6 +55,7 @@ public class GuiPanelSettings extends GuiScreenWidget {
private final Option opt;
private final String name;
private final boolean world;
private Rectangle4i margin;
private Point dragDown;
private String dragDir;
@@ -67,9 +69,10 @@ public class GuiPanelSettings extends GuiScreenWidget {
private PanelResizeButton rightButton = new PanelResizeButton(PANEL_SIZE, PANEL_SIZE * 2);
private PanelResizeButton bottomButton = new PanelResizeButton(PANEL_SIZE * 2, PANEL_SIZE);
public PanelPlaceholder(Option opt, String name) {
public PanelPlaceholder(Option opt, String name, boolean world) {
this.opt = opt;
this.name = name;
this.world = world;
}
public void resize(Rectangle4i margin) {
@@ -116,10 +119,10 @@ public class GuiPanelSettings extends GuiScreenWidget {
final int minWidth = ItemsGrid.SLOT_SIZE;
final int minHeight = ItemsGrid.SLOT_SIZE;
int paddingLeft = (int) Math.ceil(margin.w * opt.renderTag(name + ".left").getIntValue() / 100000.0);
int paddingTop = (int) Math.ceil(margin.h * opt.renderTag(name + ".top").getIntValue() / 100000.0);
int paddingRight = (int) Math.ceil(margin.w * opt.renderTag(name + ".right").getIntValue() / 100000.0);
int paddingBottom = (int) Math.ceil(margin.h * opt.renderTag(name + ".bottom").getIntValue() / 100000.0);
int paddingLeft = (int) Math.ceil(margin.w * getTag(name + ".left").getIntValue() / 100000.0);
int paddingTop = (int) Math.ceil(margin.h * getTag(name + ".top").getIntValue() / 100000.0);
int paddingRight = (int) Math.ceil(margin.w * getTag(name + ".right").getIntValue() / 100000.0);
int paddingBottom = (int) Math.ceil(margin.h * getTag(name + ".bottom").getIntValue() / 100000.0);
if (dragDir != null) {
final Point mouse = GuiDraw.getMousePosition();
@@ -159,6 +162,10 @@ public class GuiPanelSettings extends GuiScreenWidget {
return new int[] { paddingLeft, paddingTop, paddingRight, paddingBottom };
}
private ConfigTag getTag(String s) {
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
}
protected void drawItems() {
final int columns = (w - 2) / ItemsGrid.SLOT_SIZE;
final int rows = (h - (PANEL_SIZE + 2) * 2 - 2) / ItemsGrid.SLOT_SIZE;
@@ -219,10 +226,10 @@ public class GuiPanelSettings extends GuiScreenWidget {
if (button == 0 && dragDown != null) {
int[] padding = getPaddings();
opt.renderTag(name + ".left").setIntValue(padding[0] * 100000 / margin.w);
opt.renderTag(name + ".top").setIntValue(padding[1] * 100000 / margin.h);
opt.renderTag(name + ".right").setIntValue(padding[2] * 100000 / margin.w);
opt.renderTag(name + ".bottom").setIntValue(padding[3] * 100000 / margin.h);
getTag(name + ".left").setIntValue(padding[0] * 100000 / margin.w);
getTag(name + ".top").setIntValue(padding[1] * 100000 / margin.h);
getTag(name + ".right").setIntValue(padding[2] * 100000 / margin.w);
getTag(name + ".bottom").setIntValue(padding[3] * 100000 / margin.h);
dragDir = null;
dragDown = null;
@@ -232,10 +239,11 @@ public class GuiPanelSettings extends GuiScreenWidget {
public GuiPanelSettings(Option opt) {
super(176, 198);
this.opt = opt;
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
bookmarksPanel = new PanelPlaceholder(opt, opt.name + ".bookmarks");
itemsPanel = new PanelPlaceholder(opt, opt.name + ".items");
boolean world = parentGui.worldConfig();
bookmarksPanel = new PanelPlaceholder(opt, opt.name + ".bookmarks", world);
itemsPanel = new PanelPlaceholder(opt, opt.name + ".items", world);
}
@Override
@@ -247,7 +255,7 @@ public class GuiPanelSettings extends GuiScreenWidget {
public void keyTyped(char c, int keycode) {
if (keycode == Keyboard.KEY_ESCAPE || keycode == Keyboard.KEY_BACK) {
Minecraft.getMinecraft().displayGuiScreen(opt.slot.getGui());
Minecraft.getMinecraft().displayGuiScreen(parentGui);
return;
}
@@ -99,10 +99,12 @@ public class IMCHandler {
NEIClientConfig.logger.info("Error setting handler dimensions for {}", handler);
}
// true if not set to false
info.setUseCustomScroll(tag.hasKey("useCustomScroll") && tag.getBoolean("useCustomScroll"));
final boolean allowOverflow = tag.hasKey("useCustomScroll") && tag.getBoolean("useCustomScroll");
info.setAllowOverflowX(tag.hasKey("allowOverflowX") ? tag.getBoolean("allowOverflowX") : true);
info.setAllowOverflowY(tag.hasKey("allowOverflowY") ? tag.getBoolean("allowOverflowY") : allowOverflow);
info.setShowFavoritesButton(!tag.hasKey("showFavoritesButton") || tag.getBoolean("showFavoritesButton"));
info.setShowOverlayButton(!tag.hasKey("showOverlayButton") || tag.getBoolean("showOverlayButton"));
info.setShowBadge(tag.hasKey("showBadge") && tag.getBoolean("showBadge"));
GuiRecipeTab.handlerAdderFromIMC.remove(handler);
GuiRecipeTab.handlerAdderFromIMC.put(handler, info);
@@ -5,6 +5,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.item.Item;
@@ -23,7 +24,11 @@ import codechicken.nei.guihook.GuiContainerManager;
public class ItemPanelDumper extends DataDumper {
private static final int[] resolutions = new int[] { 16, 32, 48, 64, 128, 256 };
public static final int CSV = 0;
public static final int NBT = 1;
public static final int JSON = 2;
public static final int PNG = 3;
private static final Minecraft mc = Minecraft.getMinecraft();
public ItemPanelDumper(String name) {
super(name);
@@ -54,19 +59,29 @@ public class ItemPanelDumper extends DataDumper {
public int getRes() {
int i = renderTag(name + ".res").getIntValue(0);
if (i >= resolutions.length || i < 0) renderTag().setIntValue(i = 0);
return resolutions[i];
int size = 16 << i;
if (size >= getSmallerScreenAxisSize() || i < 0) {
getTag(name + ".res").setIntValue(0);
size = 16;
}
return size;
}
private int getSmallerScreenAxisSize() {
int width = mc.displayWidth;
int height = mc.displayHeight;
return Math.min(width, height);
}
public Rectangle4i resButtonSize() {
int width = 50;
int width = 60;
return new Rectangle4i(modeButtonSize().x - width - 6, 0, width, 20);
}
@Override
public void draw(int mousex, int mousey, float frame) {
super.draw(mousex, mousey, frame);
if (getMode() == 3) {
if (getMode() == PNG) {
int res = getRes();
drawButton(mousex, mousey, resButtonSize(), res + "x" + res);
}
@@ -74,23 +89,35 @@ public class ItemPanelDumper extends DataDumper {
@Override
public void mouseClicked(int mousex, int mousey, int button) {
if (getMode() == 3 && resButtonSize().contains(mousex, mousey)) {
if (getMode() != PNG) {
super.mouseClicked(mousex, mousey, button);
return;
}
if (resButtonSize().contains(mousex, mousey)) {
NEIClientUtils.playClickSound();
getTag(name + ".res").setIntValue((renderTag(name + ".res").getIntValue(0) + 1) % resolutions.length);
} else super.mouseClicked(mousex, mousey, button);
int current = renderTag(name + ".res").getIntValue(0);
int next;
// 31 - numberOfLeadingZeros(x) = floor(log2(x))
int max = 31 - Integer.numberOfLeadingZeros(getSmallerScreenAxisSize()) - 3;
if (button == 1) { // Right click to cycle backward
next = (current - 1 + max) % max;
} else { // Left click to cycle forward
next = (current + 1) % max;
}
getTag(name + ".res").setIntValue(next);
} else if (!dumpButtonSize().contains(mousex, mousey) || acknowledgedItemSizeWarning()) {
super.mouseClicked(mousex, mousey, button);
}
}
@Override
public String getFileExtension() {
switch (getMode()) {
case 0:
return ".csv";
case 1:
return ".nbt";
case 2:
return ".json";
}
return null;
return switch (getMode()) {
case CSV -> ".csv";
case NBT -> ".nbt";
case JSON -> ".json";
default -> null;
};
}
@Override
@@ -105,15 +132,29 @@ public class ItemPanelDumper extends DataDumper {
@Override
public void dumpFile() {
if (getMode() == 3) Minecraft.getMinecraft().displayGuiScreen(new GuiItemIconDumper(this, getRes()));
if (getMode() == PNG && acknowledgedItemSizeWarning())
mc.displayGuiScreen(new GuiItemIconDumper(this, getRes()));
else super.dumpFile();
}
private boolean acknowledgedItemSizeWarning() {
return !showItemSizeWarning() || NEIClientUtils.shiftKey();
}
private boolean showItemSizeWarning() {
int res = getRes();
// More than 4 items at 2048x2048, 16 items at 1024x1024, 64 at 512x512, etc.
int WARNING_SIZE = 16_777_216;
return ItemPanels.itemPanel.getItems().size() * res * res > WARNING_SIZE;
}
@Override
public void dumpTo(File file) throws IOException {
if (getMode() == 0) super.dumpTo(file);
else if (getMode() == 1) dumpNBT(file);
else dumpJson(file);
switch (getMode()) {
case CSV -> super.dumpTo(file);
case NBT -> dumpNBT(file);
default -> dumpJson(file);
}
}
public void dumpNBT(File file) throws IOException {
@@ -141,4 +182,22 @@ public class ItemPanelDumper extends DataDumper {
public int modeCount() {
return 4;
}
@Override
public List<String> handleTooltip(int mousex, int mousey, List<String> currenttip) {
if (getMode() == PNG && dumpButtonSize().contains(mousex, mousey) && showItemSizeWarning()) {
int res = getRes();
currenttip.add(
NEIClientUtils.translate(
"options.tools.dump.itempanel.icon.warning.1",
ItemPanels.itemPanel.getItems().size(),
res,
res));
currenttip.add(
NEIClientUtils.translate(
"options.tools.dump.itempanel.icon.warning.2",
NEIClientUtils.translate("key.shift")));
}
return currenttip;
}
}
@@ -1,5 +1,6 @@
package codechicken.nei.config;
import java.lang.ref.WeakReference;
import java.util.List;
import net.minecraft.client.Minecraft;
@@ -17,7 +18,11 @@ public abstract class Option {
.playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F));
}
public OptionScrollSlot slot;
private WeakReference<OptionScrollSlot> slotRef;
public OptionScrollSlot getSlot() {
return slotRef != null ? slotRef.get() : null;
}
public String namespace = null;
public final String name;
@@ -134,7 +139,7 @@ public abstract class Option {
* @return true if the gui is in world config mode
*/
public boolean worldConfig() {
return slot.getGui().worldConfig();
return getSlot().getGui().worldConfig();
}
/**
@@ -189,7 +194,7 @@ public abstract class Option {
}
public void onAdded(OptionScrollSlot slot) {
this.slot = slot;
this.slotRef = new WeakReference<>(slot);
}
public void onAdded(OptionList list) {
@@ -57,10 +57,10 @@ public abstract class OptionButton extends Option {
}
public Rectangle buttonSize() {
if (getPrefix() == null) return new Rectangle(0, 0, slot.slotWidth(), 20);
if (getPrefix() == null) return new Rectangle(0, 0, getSlot().slotWidth(), 20);
int width = Math.max(60, getStringWidth(getButtonText()) + 8);
return new Rectangle(slot.slotWidth() - width, 0, width, 20);
return new Rectangle(getSlot().slotWidth() - width, 0, width, 20);
}
public String getPrefix() {
@@ -85,7 +85,7 @@ public class OptionKeyBind extends OptionButton {
return false;
}
for (Option o : slot.options) {
for (Option o : getSlot().options) {
if (o instanceof OptionKeyBind && o != this && ((OptionKeyBind) o).getValue() == keyboard) {
return true;
}
@@ -131,13 +131,15 @@ public class OptionList extends OptionButton {
/**
* Adds this option to a temporary slot and gui for internal setting manipulation.
*/
public void synthesizeEnvironment(boolean world) {
getGui(null, this, world).addWidgets();
public GuiOptionList synthesizeEnvironment(boolean world) {
GuiOptionList gui = getGui(null, this, world);
gui.addWidgets();
return gui;
}
@Override
public boolean onClick(int button) {
openGui(slot.getGui(), slot.getGui().worldConfig());
openGui(getSlot().getGui(), getSlot().getGui().worldConfig());
return true;
}
@@ -131,6 +131,6 @@ public abstract class OptionStringSet extends Option {
}
public int buttonX() {
return slot.slotWidth() - (24 * options.size() - 4);
return getSlot().slotWidth() - (24 * options.size() - 4);
}
}
@@ -53,7 +53,7 @@ public class OptionTextField extends Option {
}
protected int getMaxInputWidth() {
return slot.slotWidth();
return getSlot().slotWidth();
}
public boolean isEnabled() {
@@ -64,9 +64,10 @@ public class OptionTextField extends Option {
public void draw(int mousex, int mousey, float frame) {
GuiDraw.drawString(getPrefix(), 10, 6, -1);
textField.w = Math
.max(60, Math.min(getMaxInputWidth(), slot.slotWidth() - GuiDraw.getStringWidth(getPrefix())) - 16);
textField.x = slot.slotWidth() - textField.w;
textField.w = Math.max(
60,
Math.min(getMaxInputWidth(), getSlot().slotWidth() - GuiDraw.getStringWidth(getPrefix())) - 16);
textField.x = getSlot().slotWidth() - textField.w;
textField.draw(mousex, mousey);
}
@@ -5,6 +5,7 @@ import static codechicken.lib.gui.GuiDraw.drawStringC;
import org.lwjgl.opengl.GL11;
import codechicken.nei.Button;
import codechicken.nei.ColorUtils;
import codechicken.nei.LayoutManager;
public abstract class CheckboxButton extends Button {
@@ -38,7 +39,8 @@ public abstract class CheckboxButton extends Button {
tex = 1;
}
int colour = tex == 2 ? 0xffffa0 : tex == 0 ? 0x601010 : 0xe0e0e0;
int colour = tex == 2 ? ColorUtils.buttonLabelHover.getColor()
: tex == 0 ? ColorUtils.buttonLabelDisabled.getColor() : ColorUtils.buttonLabelNormal.getColor();
LayoutManager.drawButtonBackground(x, y, w, h, true, isChecked() ? 0 : tex);
drawStringC(getRenderLabel(), x + w / 2, y + (h - 8) / 2, colour);
}
@@ -17,12 +17,14 @@ import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PresetsList;
import codechicken.nei.PresetsList.Preset;
import codechicken.nei.config.GuiOptionList;
import codechicken.nei.config.GuiOptionPane;
import codechicken.nei.config.Option;
public class GuiPresetList extends GuiOptionPane {
private final Option opt;
private final GuiOptionList parentGui;
protected static final int SLOT_HEIGHT = 24;
protected static final int BUTTON_HEIGHT = 20;
protected GuiCCButton createButton;
@@ -31,6 +33,7 @@ public class GuiPresetList extends GuiOptionPane {
public GuiPresetList(Option opt) {
this.opt = opt;
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
this.createButton = new GuiCCButton(0, 2, 0, 16, NEIClientUtils.translate("presets.new"))
.setActionCommand("create");
this.toggleButton = new GuiCCButton(0, 2, 0, 16, NEIClientUtils.translate("presets.toggle"))
@@ -253,7 +256,7 @@ public class GuiPresetList extends GuiOptionPane {
@Override
public GuiScreen getParentScreen() {
return opt.slot.getGui();
return parentGui;
}
@Override
@@ -91,7 +91,7 @@ public class AutoCraftingManager {
for (BookmarkItem item : math.initialItems) {
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
final long amount = Math.max(0, item.amount - invStackSize * item.fluidCellAmount);
final long amount = Math.max(0, item.getAmount() - invStackSize * item.fluidCellAmount);
if (amount > 0) {
initialItems.add(item.copyWithAmount(amount));
}
@@ -0,0 +1,89 @@
package codechicken.nei.recipe;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import net.minecraft.util.EnumChatFormatting;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ColorUtils;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.NEIClientUtils.Alignment;
public class Badge {
protected final String text;
protected final List<String> tooltip;
protected int badgeTextColor;
protected boolean shadow = false;
protected Alignment alignment = Alignment.TopLeft;
public Badge(String text, String... tooltip) {
this.text = text;
this.tooltip = Arrays.stream(tooltip).map(t -> EnumChatFormatting.GRAY + t).collect(Collectors.toList());
this.badgeTextColor = ColorUtils.recipeBadge.getColor();
}
public String getText() {
return this.text;
}
public List<String> getTooltip() {
return this.tooltip;
}
public Badge setColor(int color) {
this.badgeTextColor = color;
return this;
}
public int getColor() {
return this.badgeTextColor;
}
public Badge setShadow(boolean shadow) {
this.shadow = shadow;
return this;
}
public boolean hasShadow() {
return this.shadow;
}
public Badge setAlignment(Alignment alignment) {
this.alignment = alignment;
return this;
}
public Alignment getAlignment() {
return this.alignment;
}
public void draw(Rectangle4i rect) {
NEIClientUtils.drawNEIOverlayText(getText(), rect, 0.5f, getColor(), hasShadow(), getAlignment());
}
public static Badge notConsumed() {
return new Badge(
NEIClientUtils.translate("recipe.badge.nc"),
NEIClientUtils.translate("recipe.badge.nc.tooltip"));
}
public static Badge notConsumedParallel() {
return new Badge(
NEIClientUtils.translate("recipe.badge.ncp"),
NEIClientUtils.translate("recipe.badge.ncp.tooltip"));
}
public static Badge consumeChance(float chance) {
final String chanceText = NEIClientUtils.formatChance(chance);
return new Badge(chanceText, NEIClientUtils.translate("recipe.badge.chance.consume", chanceText));
}
public static Badge outputChance(float chance) {
final String chanceText = NEIClientUtils.formatChance(chance);
return new Badge(chanceText, NEIClientUtils.translate("recipe.badge.chance.output", chanceText));
}
}
@@ -197,7 +197,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
return ingredSlots;
}
private void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
protected void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
final Slot craftingSlot = getCraftingResultSlot(gui);
for (Slot slot : gui.inventorySlots.inventorySlots) {
@@ -249,7 +249,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
}
private int calculateRecipeQuantity(List<IngredientDistribution> assignedIngredients) {
protected int calculateRecipeQuantity(List<IngredientDistribution> assignedIngredients) {
int quantity = Integer.MAX_VALUE;
for (IngredientDistribution distrib : assignedIngredients) {
@@ -275,7 +275,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
return quantity;
}
private Slot[][] assignIngredSlots(GuiContainer gui, List<PositionedStack> ingredients,
protected Slot[][] assignIngredSlots(GuiContainer gui, List<PositionedStack> ingredients,
List<IngredientDistribution> assignedIngredients) {
Slot[][] recipeSlots = mapIngredSlots(gui, ingredients); // setup the slot map
@@ -320,7 +320,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
return recipeSlots;
}
private List<IngredientDistribution> assignIngredients(List<PositionedStack> ingredients,
protected List<IngredientDistribution> assignIngredients(List<PositionedStack> ingredients,
List<DistributedIngred> ingredStacks) {
ArrayList<IngredientDistribution> assignedIngredients = new ArrayList<>();
for (PositionedStack posstack : ingredients) // assign what we need and have
@@ -358,7 +358,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
return assignedIngredients;
}
private void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
protected void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
for (Slot slot : gui.inventorySlots.inventorySlots) /* work out how much we have to go round */ {
if (slot.getHasStack() && canMoveFrom(slot, gui)) {
final ItemStack pstack = slot.getStack();
@@ -390,7 +390,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
}
private List<DistributedIngred> getPermutationIngredients(List<PositionedStack> ingredients) {
protected List<DistributedIngred> getPermutationIngredients(List<PositionedStack> ingredients) {
ArrayList<DistributedIngred> ingredStacks = new ArrayList<>();
for (PositionedStack posstack : ingredients) /* work out what we need */ {
for (ItemStack pstack : posstack.items) {
@@ -21,7 +21,6 @@ import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.TemplateRecipeHandler.RecipeTransferRect;
public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
@@ -103,18 +102,7 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
private static ICraftingHandler buildAllRecipesHandler(ICraftingHandler handler) {
if (handler instanceof TemplateRecipeHandler templateHandler) {
TemplateRecipeHandler allHandler = templateHandler.newInstance();
if (allHandler.transferRects.isEmpty()) {
allHandler.loadCraftingRecipes("all");
return allHandler;
}
for (RecipeTransferRect rect : allHandler.transferRects) {
if (allHandler.specifyTransferRect() == null
|| allHandler.specifyTransferRect().equals(rect.outputId)) {
allHandler.loadCraftingRecipes(rect.outputId, rect.results);
return allHandler;
}
}
return templateHandler.getAllRecipeHandler();
}
return handler.getRecipeHandler("all");
}
@@ -19,6 +19,7 @@ import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IOverlayHandler;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
@@ -96,13 +97,18 @@ public class GuiOverlayButton extends GuiRecipeButton {
super(handlerRef, x, y, handlerRef.recipeIndex + BUTTON_ID_SHIFT, "+");
this.firstGui = firstGui != null && firstGui.inventorySlots != null ? firstGui : null;
IOverlayHandler overlayHandler = null;
if (this.firstGui != null) {
overlayHandler = this.handlerRef.getOverlayHandler(this.firstGui);
this.canUseOverlayRenderer = this.handlerRef.getRecipeOverlayRenderer(this.firstGui) != null;
this.canFillCraftingGrid = this.handlerRef.canFillCraftingGrid(this.firstGui);
this.hasOverlay = this.canUseOverlayRenderer || this.handlerRef.getOverlayHandler(this.firstGui) != null;
this.hasOverlay = this.canUseOverlayRenderer || overlayHandler != null;
}
setRequireShiftForOverlayRecipe(NEIClientConfig.requireShiftForOverlayRecipe());
setRequireShiftForOverlayRecipe(
overlayHandler != null ? overlayHandler.requireShiftForOverlayRecipe()
: NEIClientConfig.requireShiftForOverlayRecipe());
ingredientsOverlay();
}
@@ -17,7 +17,6 @@ import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
@@ -25,6 +24,7 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.Button;
import codechicken.nei.ColorUtils;
import codechicken.nei.GuiNEIButton;
import codechicken.nei.NEICPH;
import codechicken.nei.NEIClientConfig;
@@ -92,6 +92,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private GuiButton prevtype;
private GuiButton nexttype;
private Rectangle typeArea = new Rectangle();
private Rectangle pageArea = new Rectangle();
private int lastPage = -1;
private final GuiRecipeTabs recipeTabs;
private final GuiRecipeCatalyst recipeCatalyst;
@@ -308,7 +311,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
@Override
public Widget getWidgetUnderMouse(int mousex, int mousey) {
if (handlerInfo.getUseCustomScroll() && !this.widgets.isEmpty()) {
if (handlerInfo.isAllowOverflowY() && !this.widgets.isEmpty()) {
return this.widgets.get(0);
}
@@ -346,7 +349,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.container.x = this.guiLeft + 3;
this.container.y = this.guiTop + 32;
this.container.h = this.ySize - 32 - 5;
this.container.h = this.ySize - 32 - 4;
GuiRecipe.toggleSearch.icon = new DrawableBuilder("nei:textures/nei_sprites.png", 0, 76, 10, 10).build();
GuiRecipe.toggleSearch.w = GuiRecipe.toggleSearch.h = 12;
@@ -358,8 +361,18 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
GuiRecipe.searchField.w = this.xSize - (BORDER_PADDING + BUTTON_WIDTH) * 2 + 1 - GuiRecipe.toggleSearch.w - 45;
GuiRecipe.searchField.h = 14;
this.buttonList.addAll(Arrays.asList(this.prevtype, this.nexttype, this.prevpage, this.nextpage));
this.typeArea.setBounds(
this.prevtype.xPosition + BUTTON_WIDTH,
this.prevtype.yPosition,
this.nexttype.xPosition - this.prevtype.xPosition - BUTTON_WIDTH - 1,
BUTTON_HEIGHT);
this.pageArea.setBounds(
this.prevpage.xPosition + BUTTON_WIDTH,
this.prevpage.yPosition,
this.nextpage.xPosition - this.prevpage.xPosition - BUTTON_WIDTH - 1,
BUTTON_HEIGHT);
this.buttonList.addAll(Arrays.asList(this.prevtype, this.nexttype, this.prevpage, this.nextpage));
if (this.currenthandlers.size() == 1) {
this.prevtype.visible = false;
this.nexttype.visible = false;
@@ -372,9 +385,19 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
if (this.handlerPages.rebuildPages() || this.lastPage != this.handlerPages.getCurrentPageIndex()) {
this.lastPage = this.handlerPages.getCurrentPageIndex();
updateContainerSize();
this.container.setWidgets(this.handlerPages.getCurrentPageWidgets());
if (this.handlerInfo.getUseCustomScroll()) {
if (!this.handlerInfo.isAllowOverflowX() && this.handlerInfo.getWidth() > this.xSize - 6) {
this.container.setPaddingInline(0, 2);
this.container.setHorizontalScroll(ScrollBar.defaultHorizontalBar().setTrackPadding(1, 0, 1, 0));
} else {
this.container.setPaddingInline(0, 0);
this.container.setHorizontalScroll(null);
}
if (this.handlerInfo.isAllowOverflowY()) {
this.container.setVerticalScroll(null);
} else if (this.container.w == this.xSize - 6) {
this.container.setVerticalScroll(VERTICAL_SCROLLBAR);
@@ -416,7 +439,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.recipetype = (this.currenthandlers.size() + idx) % this.currenthandlers.size();
this.handler = new SearchRecipeHandler<>(this.currenthandlers.get(this.recipetype));
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.handler.original);
this.container.w = Math.max(this.xSize - 6, this.handlerInfo.getWidth());
updateContainerSize();
this.handlerPages = new RecipePageManager(this.handler, this.handlerInfo, this.container);
this.prevpage.enabled = this.nextpage.enabled = this.handlerPages.getNumPages() > 1;
this.lastPage = -1;
@@ -426,9 +449,21 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
GuiRecipe.toggleSearch.state = 0;
this.container.setVerticalScrollOffset(0);
this.container.setHorizontalScrollOffset(0);
this.recipeTabs.update(this);
}
private void updateContainerSize() {
final int width = this.xSize - 6;
this.container.w = Math.max(width, this.handlerInfo.getWidth());
this.container.h = this.ySize - 32 - 4;
if (!this.handlerInfo.isAllowOverflowX() && this.handlerInfo.getWidth() > width) {
this.container.w = width;
this.container.h -= 8;
}
}
public int openTargetRecipe(RecipeId recipeId) {
int refIndex = -1;
int recipetype = 0;
@@ -671,7 +706,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
// If shift is held, try switching to the next recipe handler. Replicates the
// GuiRecipeTabs.mouseScrolled()
// without the checking for the cursor being inside the tabbar.
if (NEIClientUtils.shiftKey()) {
if (NEIClientUtils.shiftKey() || this.typeArea.contains(mouse)) {
if (scroll < 0) {
nextType();
} else {
@@ -683,7 +718,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
// Finally, if nothing else has handled scrolling, try changing to the next
// recipe page.
if (new Rectangle(this.guiLeft, this.guiTop, this.xSize, this.ySize).contains(mouse)) {
if (NEIClientConfig.getBooleanSetting("inventory.guirecipe.scrollPages")
&& this.container.boundsInside().contains(mouse.x, mouse.y)
|| this.pageArea.contains(mouse)
&& (!this.handler.searchingAvailable() || !GuiRecipe.toggleSearch.contains(mouse.x, mouse.y))
&& (!GuiRecipe.searchField.isVisible() || !GuiRecipe.searchField.contains(mouse.x, mouse.y))) {
if (scroll > 0) {
prevPage();
} else {
@@ -784,9 +824,10 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
public void forceRefreshPage() {
this.currenthandlers.sort(NEIClientConfig.HANDLER_COMPARATOR);
final int currentPage = this.handlerPages.getCurrentPageIndex();
this.currenthandlers.sort(NEIClientConfig.HANDLER_COMPARATOR);
this.recipetype = this.currenthandlers.indexOf(this.handler.original);
setRecipePage(this.recipetype);
this.handlerPages.changePage(currentPage);
@@ -824,26 +865,26 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
final int textMiddle = (BUTTON_WIDTH - this.fontRendererObj.FONT_HEIGHT) / 2;
drawRect(
this.prevtype.xPosition + BUTTON_WIDTH - 1,
this.prevtype.yPosition,
this.nexttype.xPosition,
this.prevtype.yPosition + BUTTON_HEIGHT,
this.typeArea.x,
this.typeArea.y,
this.typeArea.x + this.typeArea.width,
this.typeArea.y + this.typeArea.height,
0x30000000);
drawRect(
this.prevpage.xPosition + BUTTON_WIDTH - 1,
this.prevpage.yPosition,
this.nextpage.xPosition,
this.prevpage.yPosition + BUTTON_HEIGHT,
this.pageArea.x,
this.pageArea.y,
this.pageArea.x + this.pageArea.width,
this.pageArea.y + this.pageArea.height,
0x30000000);
final String handlerTitle = this.handler.original.getRecipeName().trim();
final String titleColorCode = getHandlerTitleColorCode(isHandlerTitleHovered(mouseX, mouseY));
final int titleColor = getHandlerTitleColor(isHandlerTitleHovered(mouseX, mouseY));
drawCenteredString(
this.fontRendererObj,
titleColorCode + handlerTitle + EnumChatFormatting.RESET,
handlerTitle,
this.guiLeft + this.xSize / 2,
this.prevtype.yPosition + textMiddle,
0xffffff);
this.typeArea.y + textMiddle,
titleColor);
if (this.handler.searchingAvailable()) {
GuiRecipe.toggleSearch.draw(mouseX, mouseY);
@@ -865,7 +906,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.fontRendererObj,
recipePage,
GuiRecipe.searchField.x + GuiRecipe.searchField.w + 22,
this.prevpage.yPosition + textMiddle,
this.pageArea.y + textMiddle,
0xffffff);
} else {
final String recipePage = NEIClientUtils.translate(
@@ -876,7 +917,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.fontRendererObj,
recipePage,
this.guiLeft + this.xSize / 2,
this.prevpage.yPosition + textMiddle,
this.pageArea.y + textMiddle,
0xffffff);
}
@@ -897,13 +938,8 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
return new Rectangle(titleX, titleY, titleWidth, this.fontRendererObj.FONT_HEIGHT).contains(mousex, mousey);
}
private String getHandlerTitleColorCode(boolean hovered) {
final String key = hovered ? "recipe.title.color.hover" : "recipe.title.color.normal";
final String translated = NEIClientUtils.translate(key);
if (!translated.startsWith("nei.")) { // Optional localization string for resource packs
return translated;
}
return hovered ? EnumChatFormatting.YELLOW.toString() : EnumChatFormatting.WHITE.toString();
private int getHandlerTitleColor(boolean hovered) {
return hovered ? ColorUtils.recipeTitleHover.getColor() : ColorUtils.recipeTitle.getColor();
}
@Override
@@ -23,12 +23,13 @@ import net.minecraftforge.common.MinecraftForge;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.ColorUtils;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.Widget;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.event.NEIRegisterHandlerInfosEvent;
@@ -127,7 +128,7 @@ public abstract class GuiRecipeTab extends Widget {
int textCenterX = x + (int) (getWidth() / 2f);
int textCenterY = y + (int) (getHeight() / 2f) - 3;
int color = selected ? 0xffffa0 : 0xe0e0e0;
int color = selected ? ColorUtils.buttonLabelHover.getColor() : ColorUtils.buttonLabelNormal.getColor();
fontRenderer.drawStringWithShadow(
text,
textCenterX - (int) (fontRenderer.getStringWidth(text) / 2f),
@@ -139,18 +140,16 @@ public abstract class GuiRecipeTab extends Widget {
public void addTooltips(List<String> tooltip) {
tooltip.add(handler.getRecipeTabName().trim());
String handlerMod = getHandlerMod(handlerName, handlerID);
tooltip.add(EnumChatFormatting.BLUE + handlerMod);
boolean shiftHeld = Keyboard.getEventKeyState()
&& (Keyboard.getEventKey() == Keyboard.KEY_LSHIFT || Keyboard.getEventKey() == Keyboard.KEY_RSHIFT);
if (handlerMod.equals("Unknown") || shiftHeld) {
if (handlerMod.equals("Unknown") || NEIClientUtils.shiftKey()) {
tooltip.add("");
tooltip.add("HandlerName: " + handlerName);
tooltip.add("HandlerID: " + handlerID);
tooltip.add("HandlerOrder: " + NEIClientConfig.getHandlerOrder(handler));
tooltip.add(EnumChatFormatting.GRAY + "HandlerName: " + handlerName);
tooltip.add(EnumChatFormatting.GRAY + "HandlerID: " + handlerID);
tooltip.add(EnumChatFormatting.GRAY + "HandlerOrder: " + NEIClientConfig.getHandlerOrder(handler));
}
tooltip.add(EnumChatFormatting.BLUE + handlerMod);
}
public abstract boolean onButtonPress(boolean rightclick);
@@ -265,8 +264,13 @@ public abstract class GuiRecipeTab extends Widget {
NEIClientConfig.logger.info("Error setting handler dimensions for " + handler);
}
info.setUseCustomScroll(
record.isSet("useCustomScroll") ? Boolean.parseBoolean(record.get("useCustomScroll")) : false);
final boolean allowOverflow = record.isSet("useCustomScroll")
&& Boolean.parseBoolean(record.get("useCustomScroll"));
info.setAllowOverflowX(
record.isSet("allowOverflowX") ? Boolean.parseBoolean(record.get("allowOverflowX")) : true);
info.setAllowOverflowY(
record.isSet("allowOverflowY") ? Boolean.parseBoolean(record.get("allowOverflowY"))
: allowOverflow);
handlerMap.put(handler, info);
NEIClientConfig.logger.info("Loaded " + handler);
}
@@ -30,7 +30,10 @@ public class HandlerInfo {
private boolean multipleWidgetsAllowed = false;
private boolean showFavoritesButton = true;
private boolean showOverlayButton = true;
private boolean useCustomScroll = false;
private boolean showBadge = false;
private boolean allowOverflowX = true;
private boolean allowOverflowY = false;
private ItemStack itemStack = null;
private DrawableResource image = null;
@@ -142,12 +145,34 @@ public class HandlerInfo {
this.showOverlayButton = showOverlayButton;
}
public boolean getUseCustomScroll() {
return this.useCustomScroll;
public boolean getShowBadge() {
return this.showBadge;
}
public void setShowBadge(boolean showBadge) {
this.showBadge = showBadge;
}
@Deprecated
public void setUseCustomScroll(boolean useCustomScroll) {
this.useCustomScroll = useCustomScroll;
this.allowOverflowX = useCustomScroll;
this.allowOverflowY = useCustomScroll;
}
public boolean isAllowOverflowX() {
return this.allowOverflowX;
}
public void setAllowOverflowX(boolean allowOverflowX) {
this.allowOverflowX = allowOverflowX;
}
public boolean isAllowOverflowY() {
return this.allowOverflowY;
}
public void setAllowOverflowY(boolean allowOverflowY) {
this.allowOverflowY = allowOverflowY;
}
public static class Builder {
@@ -186,6 +211,7 @@ public class HandlerInfo {
return this;
}
@Deprecated
public Builder setUseCustomScroll(boolean useCustomScroll) {
this.info.setUseCustomScroll(useCustomScroll);
return this;
@@ -216,6 +242,11 @@ public class HandlerInfo {
return this;
}
public Builder setShowBadge(boolean showBadge) {
this.info.showBadge = showBadge;
return this;
}
@Deprecated
public Builder setMaxRecipesPerPage(int maxRecipesPerPage) {
this.info.multipleWidgetsAllowed = maxRecipesPerPage > 1;
@@ -227,6 +258,16 @@ public class HandlerInfo {
return this;
}
public Builder setAllowOverflowX(boolean allowOverflowX) {
this.info.allowOverflowX = allowOverflowX;
return this;
}
public Builder setAllowOverflowY(boolean allowOverflowY) {
this.info.allowOverflowY = allowOverflowY;
return this;
}
public HandlerInfo build() {
return this.info;
}
@@ -21,6 +21,7 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
@@ -40,6 +41,7 @@ public class NEIRecipeWidget extends Widget {
protected AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler;
protected final Map<PositionedStack, Integer> favoriteIndexes = new WeakHashMap<>();
protected final Map<PositionedStack, List<ItemStack>> permutations = new WeakHashMap<>();
protected final Map<PositionedStack, List<Badge>> badgeCache = new WeakHashMap<>();
protected int favoriteRevision = -1;
protected boolean update = true;
protected int cycleticks = 0;
@@ -64,7 +66,7 @@ public class NEIRecipeWidget extends Widget {
public void update() {
if (this.showAsWidget || !this.handlerInfo.getUseCustomScroll()) {
if (this.showAsWidget || !this.handlerInfo.isAllowOverflowY()) {
final int recipeHeight = this.handlerRef.handler.getRecipeHeight(this.handlerRef.recipeIndex);
this.w = Math.max(166, this.handlerInfo.getWidth());
this.h = recipeHeight > 0 ? recipeHeight : this.handlerInfo.getHeight();
@@ -143,7 +145,7 @@ public class NEIRecipeWidget extends Widget {
final List<GuiRecipeButton> buttons = new ArrayList<>();
final boolean showFavorites = NEIClientConfig.favoritesEnabled() && handlerInfo.getShowFavoritesButton();
final boolean showOverlay = handlerInfo.getShowOverlayButton();
final int x = Math.min(168, this.w) - GuiRecipeButton.BUTTON_WIDTH;
final int x = Math.min(166, this.w) - GuiRecipeButton.BUTTON_WIDTH;
int y = this.h - GuiRecipeButton.BUTTON_HEIGHT - 6;
if (guiContainer instanceof IGuiContainerOverlay overlay) {
@@ -196,11 +198,7 @@ public class NEIRecipeWidget extends Widget {
updatePermutationsFor(pStack);
}
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF));
}
drawItem(pStack, mouseX, mouseY, yShift, true);
}
for (PositionedStack pStack : getCatalysts()) {
@@ -209,19 +207,11 @@ public class NEIRecipeWidget extends Widget {
updatePermutationsFor(pStack);
}
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF));
}
drawItem(pStack, mouseX, mouseY, yShift, true);
}
for (PositionedStack pStack : getOutputs()) {
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF));
}
drawItem(pStack, mouseX, mouseY, yShift, false);
}
GuiContainerManager.disableMatrixStackLogging();
@@ -255,6 +245,55 @@ public class NEIRecipeWidget extends Widget {
DebugHandlerWidget.instance.drawGuiPlaceholder(this);
}
protected void drawItem(PositionedStack pStack, int mouseX, int mouseY, int yShift, boolean input) {
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
if (this.handlerInfo.getShowBadge()) {
drawBadge(pStack, input);
}
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF));
}
}
protected List<Badge> getBadges(PositionedStack pStack, boolean input) {
return this.badgeCache.computeIfAbsent(pStack, k -> {
final List<Badge> badges = k.getBadges();
if (badges == null) {
if (input) {
if (StackInfo.getAmount(pStack.item) == 0) {
return Collections.singletonList(Badge.notConsumed());
} else if (pStack.getChance() == 0) {
return Collections.singletonList(Badge.notConsumedParallel());
} else if (pStack.getChance() != PositionedStack.CHANCE_FULL) {
return Collections.singletonList(
Badge.consumeChance(pStack.getChance() / (float) PositionedStack.CHANCE_FULL));
}
} else if (pStack.getChance() != PositionedStack.CHANCE_FULL) {
return Collections.singletonList(
Badge.outputChance(pStack.getChance() / (float) PositionedStack.CHANCE_FULL));
}
return Collections.emptyList();
}
return badges;
});
}
protected void drawBadge(PositionedStack pStack, boolean input) {
final List<Badge> badges = getBadges(pStack, input);
for (Badge badge : badges) {
badge.draw(new Rectangle4i(pStack.relx, pStack.rely, 16, 16));
}
}
@Override
public boolean handleKeyPress(int keyID, char keyChar) {
@@ -366,27 +405,22 @@ public class NEIRecipeWidget extends Widget {
tooltip = this.handlerRef.handler.handleItemTooltip(guiRecipe, itemstack, tooltip, this.handlerRef.recipeIndex);
if (NEIClientConfig.showCycledIngredientsTooltip() && itemstack != null) {
final int yShift = this.handlerInfo.getYShift();
PositionedStack hovered = null;
if (itemstack != null) {
final PositionedStack hovered = getPositionedStackMouseOver(mousex, mousey);
for (PositionedStack pStack : getInputs()) {
if (pStack.contains(mousex - this.x, mousey - this.y - yShift)) {
hovered = pStack;
break;
}
}
if (hovered != null && this.handlerInfo.getShowBadge()) {
final List<Badge> badges = getBadges(hovered, getOutputs().indexOf(hovered) == -1);
if (hovered == null) {
for (PositionedStack pStack : getCatalysts()) {
if (pStack.contains(mousex - this.x, mousey - this.y - yShift)) {
hovered = pStack;
break;
for (Badge badge : badges) {
if (badge.getTooltip() != null && !badge.getTooltip().isEmpty()) {
tooltip.addAll(badge.getTooltip());
}
}
}
if (hovered == null || this.permutations.getOrDefault(hovered, Collections.emptyList()).size() <= 1) {
if (hovered == null || !NEIClientConfig.showCycledIngredientsTooltip()
|| this.permutations.getOrDefault(hovered, Collections.emptyList()).size() <= 1
|| getOutputs().indexOf(hovered) != -1) {
this.acceptsFollowingTooltipLineHandler = null;
} else if (this.acceptsFollowingTooltipLineHandler == null
|| this.acceptsFollowingTooltipLineHandler.tooltipGUID != hovered) {
@@ -568,6 +602,7 @@ public class NEIRecipeWidget extends Widget {
this.acceptsFollowingTooltipLineHandler = null;
this.favoriteIndexes.clear();
this.permutations.clear();
this.badgeCache.clear();
}
for (PositionedStack pStack : getInputs()) {
@@ -1,16 +1,25 @@
package codechicken.nei.recipe;
import static codechicken.lib.gui.GuiDraw.drawString;
import static codechicken.lib.gui.GuiDraw.getStringWidth;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Container;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import codechicken.core.TaskProfiler.ProfilerResult;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.ColorUtils;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
@@ -20,12 +29,104 @@ import codechicken.nei.util.AsyncTaskProfiler;
public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
private static final AsyncTaskProfiler profiler = new AsyncTaskProfiler();
public static class RecipeProfiler {
private final AsyncTaskProfiler profiler = new AsyncTaskProfiler();
private final Map<String, IRecipeHandler> handlerMap = new ConcurrentHashMap<>();
private final AtomicInteger idCounter = new AtomicInteger(0);
private boolean isProfiling = false;
public <T extends IRecipeHandler> ArrayList<T> start(String profilerSection, Supplier<ArrayList<T>> callback) {
profilerInfos.clear();
this.handlerMap.clear();
this.idCounter.set(0);
if (NEIClientConfig.isProfileRecipeEnabled()) {
this.profiler.clear();
this.profiler.start(EnumChatFormatting.getTextWithoutFormattingCodes(profilerSection));
this.isProfiling = true;
try {
return callback.get();
} finally {
this.profiler.end();
this.isProfiling = false;
final List<ProfilerResult> results = this.profiler.getResults();
results.sort(
Comparator.comparingLong((ProfilerResult profilerResult) -> profilerResult.time)
.reversed());
for (ProfilerResult profilerResult : results) {
profilerInfos.add(new ProfilerInfo(handlerMap.get(profilerResult.name), profilerResult));
}
}
} else {
return callback.get();
}
}
public <T extends IRecipeHandler> T profile(T handler, Supplier<T> callback) {
if (this.isProfiling && !(handler instanceof ProfilerRecipeHandler)) {
final String id = String.valueOf(idCounter.incrementAndGet());
this.handlerMap.put(id, handler);
this.profiler.clearCurrent();
this.profiler.start(id);
try {
T result = callback.get();
if (result != null) {
this.handlerMap.put(id, result);
}
return result;
} finally {
this.profiler.end();
}
} else {
return callback.get();
}
}
public static AsyncTaskProfiler getProfiler() {
return profiler;
}
protected static class ProfilerInfo {
public final String modName;
public final IRecipeHandler handler;
public final List<String> tooltip = new ArrayList<>();
public final String title;
public final String time;
public ProfilerInfo(IRecipeHandler handler, ProfilerResult result) {
this.handler = handler;
this.time = (result.time < 1_000_000 ? (result.time / 1_000) + "us" : (result.time / 1_000_000) + "ms");
if (handler == null) {
this.title = result.name;
this.modName = "Unknown";
} else {
final HandlerInfo handlerInfo = GuiRecipeTab.getHandlerInfo(handler);
final String handlerName = handlerInfo != null ? handlerInfo.getHandlerName() : "Unknown";
this.title = EnumChatFormatting.getTextWithoutFormattingCodes(handler.getRecipeName());
this.modName = handlerInfo != null ? handlerInfo.getModName() : "Unknown";
this.tooltip.add(EnumChatFormatting.GRAY + "HandlerName: " + handlerName);
this.tooltip.add(EnumChatFormatting.GRAY + "HandlerID: " + handler.getOverlayIdentifier());
this.tooltip.add(EnumChatFormatting.GRAY + "HandlerOrder: " + NEIClientConfig.getHandlerOrder(handler));
}
}
}
public static final RecipeProfiler recipeProfiler = new RecipeProfiler();
protected static final List<ProfilerInfo> profilerInfos = new ArrayList<>();
private static final int WIDTH = 166;
private final boolean crafting;
public ProfilerRecipeHandler(boolean crafting) {
@@ -39,11 +140,7 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public int numRecipes() {
if (!NEIClientConfig.isProfileRecipeEnabled()) return 0;
return (int) Math.ceil(
((crafting ? GuiCraftingRecipe.craftinghandlers.size() : GuiUsageRecipe.usagehandlers.size()) - 1)
/ 6D);
return NEIClientConfig.isProfileRecipeEnabled() ? Math.max(1, profilerInfos.size()) : 0;
}
@Override
@@ -51,21 +148,32 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public void drawForeground(int recipe) {
List<ProfilerResult> results = profiler.getResults();
results.removeIf(profilerResult -> getRecipeName().equals(profilerResult.name));
if (recipe >= profilerInfos.size()) return;
results.sort((o1, o2) -> o1.time < o2.time ? 1 : -1);
final ProfilerInfo info = profilerInfos.get(recipe);
final int valueWidth = getStringWidth(info.time);
final IRecipeHandler handler = info.handler;
final boolean hovered = handler != null && handler.numRecipes() > 0
&& isHandlerTitleHovered(recipe, GuiDraw.getMousePosition());
for (int i = recipe * 6; i < results.size() && i < (recipe + 1) * 6; i++) {
ProfilerResult r = results.get(i);
int y = (i % 6) * 20 + 6;
drawString(r.name, 8, y, 0xFF808080, false);
GuiDraw.drawString(
NEIClientUtils.cropText(GuiDraw.fontRenderer, info.title, WIDTH - valueWidth - 2),
0,
3,
hovered ? 0xFFFFDD00 : 0xFF808080,
false);
GuiDraw.drawString(info.time, WIDTH - valueWidth, 3, ColorUtils.textGray.getColor(), false);
}
String s;
if (r.time < 1000000L) s = (r.time / 1000) + "us";
else s = (r.time / 1000000) + "ms";
private boolean isHandlerTitleHovered(int recipe, Point mouse) {
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
drawString(s, 156 - getStringWidth(s), y + 10, 0xFF404040, false);
if (guiContainer instanceof GuiRecipe guiRecipe) {
final Point recipePosition = guiRecipe.getRecipePosition(recipe);
return new Rectangle(recipePosition.x, recipePosition.y, WIDTH, 16)
.contains(mouse.x - guiContainer.guiLeft, mouse.y - guiContainer.guiTop);
} else {
return false;
}
}
@@ -104,6 +212,22 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public List<String> handleTooltip(GuiRecipe<?> gui, List<String> currenttip, int recipe) {
if (recipe >= profilerInfos.size()) return currenttip;
final ProfilerInfo r = profilerInfos.get(recipe);
currenttip.add(r.title + ": " + r.time);
if (r.handler != null) {
currenttip.add(EnumChatFormatting.GRAY + "Recipes: " + r.handler.numRecipes() + GuiDraw.TOOLTIP_LINESPACE);
if (NEIClientUtils.shiftKey()) {
currenttip.addAll(r.tooltip);
}
currenttip.add(EnumChatFormatting.BLUE + r.modName);
}
return currenttip;
}
@@ -119,6 +243,23 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public boolean mouseClicked(GuiRecipe<?> gui, int button, int recipe) {
if (recipe >= profilerInfos.size()) return false;
final ProfilerInfo r = profilerInfos.get(recipe);
if (r.handler != null) {
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
if (guiContainer instanceof GuiRecipe guiRecipe) {
int index = guiRecipe.currenthandlers.indexOf(r.handler);
if (index != -1) {
guiRecipe.setRecipePage(index);
return true;
}
}
}
return false;
}
@@ -282,17 +282,20 @@ public class Recipe {
protected final ItemStack[] items;
protected int activeIndex = 0;
protected int amount = 0;
protected int chance = PositionedStack.CHANCE_FULL;
protected int relx;
protected int rely;
public RecipeIngredient(int relx, int rely, List<ItemStack> items, int activeIndex) {
public RecipeIngredient(int relx, int rely, List<ItemStack> items, int chance, int activeIndex) {
this.items = items.stream().map(ItemStack::copy).toArray(size -> new ItemStack[size]);
this.amount = StackInfo.getAmount(this.items[activeIndex]);
this.activeIndex = activeIndex;
this.relx = relx;
this.rely = rely;
this.chance = chance;
}
public static RecipeIngredient of(PositionedStack positionedStack) {
@@ -311,17 +314,21 @@ public class Recipe {
}
}
return of(positionedStack.relx, positionedStack.rely, stacks, activeIndex);
return of(positionedStack.relx, positionedStack.rely, stacks, positionedStack.getChance(), activeIndex);
}
public static RecipeIngredient of(int relx, int rely, List<ItemStack> stacks, int activeIndex) {
return new RecipeIngredient(relx, rely, stacks, activeIndex);
public static RecipeIngredient of(int relx, int rely, List<ItemStack> stacks, int chance, int activeIndex) {
return new RecipeIngredient(relx, rely, stacks, chance, activeIndex);
}
public int getAmount() {
return this.amount;
}
public int getChance() {
return this.chance;
}
public ItemStack getItemStack() {
return this.items[this.activeIndex];
}
@@ -348,11 +355,16 @@ public class Recipe {
return this;
}
public void setChance(int chance) {
this.chance = chance;
}
public RecipeIngredient copy() {
return new RecipeIngredient(
this.relx,
this.rely,
getPermutations().stream().map(ItemStack::copy).collect(Collectors.toList()),
this.chance,
this.activeIndex);
}
}
@@ -435,7 +447,7 @@ public class Recipe {
}
if (item instanceof ItemStack stack) {
return RecipeIngredient.of(0, 0, Arrays.asList(stack), 0);
return RecipeIngredient.of(0, 0, Arrays.asList(stack), PositionedStack.CHANCE_FULL, 0);
}
if (item instanceof RecipeIngredient ingr) {
@@ -12,10 +12,8 @@ import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import codechicken.core.TaskProfiler;
import codechicken.nei.ItemList;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.util.AsyncTaskProfiler;
public class RecipeHandlerQuery<T extends IRecipeHandler> {
@@ -34,30 +32,22 @@ public class RecipeHandlerQuery<T extends IRecipeHandler> {
}
public ArrayList<T> runWithProfiling(String profilerSection) {
TaskProfiler profiler = ProfilerRecipeHandler.getProfiler();
return ProfilerRecipeHandler.recipeProfiler.start(profilerSection, () -> {
// Save the current config state here, as it may be altered
// if getRecipeHandlesParallel took so long and player accidentally changed config.
boolean profileRecipes = NEIClientConfig.isProfileRecipeEnabled();
if (profileRecipes) {
profiler.clear();
profiler.start(profilerSection);
}
try {
ArrayList<T> handlers = getRecipeHandlersParallel();
try {
ArrayList<T> handlers = getRecipeHandlersParallel();
if (error) {
if (error) {
displayRecipeLookupError();
}
return handlers;
} catch (InterruptedException | ExecutionException e) {
printLog(e);
displayRecipeLookupError();
return new ArrayList<>(0);
}
return handlers;
} catch (InterruptedException | ExecutionException e) {
printLog(e);
displayRecipeLookupError();
return new ArrayList<>(0);
} finally {
if (profileRecipes) profiler.end();
}
});
}
private ArrayList<T> getRecipeHandlersParallel() throws InterruptedException, ExecutionException {
@@ -77,40 +67,42 @@ public class RecipeHandlerQuery<T extends IRecipeHandler> {
private ArrayList<T> getSerialHandlersWithRecipes() {
return serialRecipeHandlers.stream().map(handler -> {
AsyncTaskProfiler profiler = ProfilerRecipeHandler.getProfiler();
profiler.clearCurrent();
profiler.start(handler.getRecipeName());
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
} finally {
profiler.end();
}
}).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
return serialRecipeHandlers.stream()
.map(handler -> ProfilerRecipeHandler.recipeProfiler.profile(handler, () -> {
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
}
}))
.filter(
h -> h != null && h.numRecipes() > 0
&& SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new));
}
private ArrayList<T> getHandlersWithRecipes() throws InterruptedException, ExecutionException {
return ItemList.forkJoinPool.submit(() -> recipeHandlers.parallelStream().map(handler -> {
AsyncTaskProfiler profiler = ProfilerRecipeHandler.getProfiler();
profiler.clearCurrent();
profiler.start(handler.getRecipeName());
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
} finally {
profiler.end();
}
}).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new))).get();
return ItemList.forkJoinPool.submit(
() -> recipeHandlers.parallelStream()
.map(handler -> ProfilerRecipeHandler.recipeProfiler.profile(handler, () -> {
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
}
}))
.filter(
h -> h != null && h.numRecipes() > 0
&& SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
}
private boolean isHidden(T handler) {
@@ -77,9 +77,24 @@ public class RecipeHandlerRef {
return this.handler.getOverlayHandler(firstGui, this.recipeIndex);
}
if (firstGui != null && canUseSmartOverlay(firstGui)) {
return SmartOverlayHandler.INSTANCE;
}
return null;
}
private boolean canUseSmartOverlay(GuiContainer gui) {
if (!(this.handler instanceof TemplateRecipeHandler templateHandler)
|| templateHandler.getOverlayIdentifier() == null
|| templateHandler.getGuiClass() == null
|| !templateHandler.getGuiClass().isAssignableFrom(gui.getClass())) {
return false;
}
return SmartOverlayHandler.canHandle(gui, this.handler, this.recipeIndex);
}
public boolean canFillCraftingGrid(GuiContainer gui) {
final IOverlayHandler overlayHandler = getOverlayHandler(gui);
@@ -42,10 +42,10 @@ public class RecipePageManager {
this.cacheKey = cacheKey;
this.pageWidgets.clear();
final boolean useCustomScroll = this.handlerInfo.getUseCustomScroll();
final boolean allowOverflowY = this.handlerInfo.isAllowOverflowY();
final int handlerHeight = this.handlerInfo.getHeight() + this.handlerInfo.getYShift();
final boolean multipleWidgetsAllowed = !useCustomScroll && handlerInfo.isMultipleWidgetsAllowed();
final boolean infiniteScroll = !useCustomScroll
final boolean multipleWidgetsAllowed = !allowOverflowY && handlerInfo.isMultipleWidgetsAllowed();
final boolean infiniteScroll = !allowOverflowY
&& NEIClientConfig.getBooleanSetting("inventory.guirecipe.infiniteScroll");
final int heightAvailable = this.container.getVisibleHeight();
@@ -59,7 +59,7 @@ public class RecipePageManager {
final NEIRecipeWidget widget = RecipeHandlerRef.of(this.handler.original, recipeIndex).getRecipeWidget();
widget.w = width;
if (useCustomScroll) {
if (allowOverflowY) {
widget.h = Math.min(handlerHeight, heightAvailable);
}
@@ -84,7 +84,7 @@ public class RecipePageManager {
}
public boolean isMultipleWidgetsAllowed() {
return !this.handlerInfo.getUseCustomScroll() && this.handlerInfo.isMultipleWidgetsAllowed();
return !this.handlerInfo.isAllowOverflowY() && this.handlerInfo.isMultipleWidgetsAllowed();
}
public int getNumRecipes() {
@@ -120,10 +120,14 @@ public class RepairRecipeHandler extends TemplateRecipeHandler {
final List<ItemStack> permutations = new ArrayList<>();
for (ItemStack itemstack2 : ItemList.items) {
if (itemstack1.getItem().getIsRepairable(itemstack1, itemstack2)) {
final ItemStack right = itemstack2.copy();
right.stackSize = 4;
permutations.add(right);
try {
if (itemstack1.getItem().getIsRepairable(itemstack1, itemstack2)) {
final ItemStack right = itemstack2.copy();
right.stackSize = 4;
permutations.add(right);
}
} catch (Exception e) {
// Catch exceptions from mods that don't properly implement getIsRepairable
}
}
@@ -0,0 +1,232 @@
package codechicken.nei.recipe;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.inventory.SlotCrafting;
import net.minecraft.item.ItemStack;
import codechicken.nei.FastTransferManager;
import codechicken.nei.PositionedStack;
public class SmartOverlayHandler extends DefaultOverlayHandler {
public static final SmartOverlayHandler INSTANCE = new SmartOverlayHandler();
private static final int MAX_RECIPE_SLOTS = 12;
private static final int MAX_COST_DELTA = 16 * 16;
private SmartOverlayHandler() {}
public static boolean canHandle(GuiContainer gui, IRecipeHandler handler, int recipeIndex) {
return INSTANCE.resolveSlots(gui, handler.getIngredientStacks(recipeIndex)) != null;
}
@Override
public int transferRecipe(GuiContainer gui, IRecipeHandler handler, int recipeIndex, int multiplier) {
final List<PositionedStack> ingredients = handler.getIngredientStacks(recipeIndex);
final List<DistributedIngred> ingredStacks = getPermutationIngredients(ingredients);
final Slot[][] recipeSlots = resolveSlots(gui, ingredients);
if (recipeSlots == null || !clearMappedSlots(gui, recipeSlots)) return 0;
findInventoryQuantities(gui, ingredStacks);
final List<IngredientDistribution> assignedIngredients = assignIngredients(ingredients, ingredStacks);
if (assignedIngredients == null) return 0;
assignIngredSlots(gui, ingredients, assignedIngredients);
multiplier = Math.min(multiplier == 0 ? 64 : multiplier, calculateRecipeQuantity(assignedIngredients));
moveIngredients(gui, assignedIngredients, Math.max(1, multiplier));
return assignedIngredients.stream().anyMatch(distrib -> distrib.distrib.distributed == 0) ? 0 : multiplier;
}
@Override
public boolean canFillCraftingGrid(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex) {
return canHandle(firstGui, handler, recipeIndex);
}
@Override
protected Set<Slot> getCraftMatrixSlots(GuiContainer gui, IRecipeHandler handler) {
return Collections.emptySet();
}
@Override
public Slot[][] mapIngredSlots(GuiContainer gui, List<PositionedStack> ingredients) {
Slot[][] exactSlots = super.mapIngredSlots(gui, ingredients);
if (!hasEmptyMappings(exactSlots)) {
return exactSlots;
}
Slot[][] smartSlots = resolveSlots(gui, ingredients);
return smartSlots == null ? exactSlots : smartSlots;
}
private boolean clearMappedSlots(GuiContainer gui, Slot[][] recipeSlots) {
Set<Slot> cleared = new HashSet<>();
for (Slot[] slots : recipeSlots) {
for (Slot slot : slots) {
if (cleared.add(slot) && slot.getHasStack() && slot.canTakeStack(gui.mc.thePlayer)) {
FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1);
if (slot.getHasStack()) return false;
}
}
}
FastTransferManager.dropHeldItem(gui);
return gui.mc.thePlayer.inventory.getItemStack() == null;
}
private Slot[][] resolveSlots(GuiContainer gui, List<PositionedStack> ingredients) {
if (ingredients == null || ingredients.isEmpty() || ingredients.size() > MAX_RECIPE_SLOTS) {
return null;
}
List<Slot> candidates = new ArrayList<>();
for (Slot slot : gui.inventorySlots.inventorySlots) {
if (isDestinationCandidate(slot)) {
candidates.add(slot);
}
}
if (candidates.size() < ingredients.size()) {
return null;
}
Assignment best = new Assignment();
Assignment secondBest = new Assignment();
searchAssignments(
ingredients,
candidates,
new boolean[candidates.size()],
new Slot[ingredients.size()],
0,
0,
best,
secondBest);
if (best.slots == null || secondBest.cost - best.cost <= MAX_COST_DELTA) {
return null;
}
Slot[][] resolved = new Slot[ingredients.size()][];
for (int i = 0; i < ingredients.size(); i++) {
resolved[i] = new Slot[] { best.slots[i] };
}
return resolved;
}
private void searchAssignments(List<PositionedStack> ingredients, List<Slot> candidates, boolean[] used,
Slot[] assigned, int ingredientIndex, int cost, Assignment best, Assignment secondBest) {
if (cost >= secondBest.cost) {
return;
}
if (ingredientIndex == ingredients.size()) {
if (cost < best.cost) {
secondBest.copyFrom(best);
best.set(cost, assigned);
} else if (cost < secondBest.cost) {
secondBest.set(cost, assigned);
}
return;
}
PositionedStack ingredient = ingredients.get(ingredientIndex);
for (int i = 0; i < candidates.size(); i++) {
if (used[i]) continue;
Slot slot = candidates.get(i);
int slotCost = getSlotCost(ingredient, slot);
if (slotCost < 0) continue;
used[i] = true;
assigned[ingredientIndex] = slot;
searchAssignments(
ingredients,
candidates,
used,
assigned,
ingredientIndex + 1,
cost + slotCost,
best,
secondBest);
assigned[ingredientIndex] = null;
used[i] = false;
}
}
private int getSlotCost(PositionedStack ingredient, Slot slot) {
boolean acceptsAnyPermutation = false;
for (ItemStack permutation : ingredient.items) {
if (canUseSlot(slot, permutation)) {
acceptsAnyPermutation = true;
break;
}
}
if (!acceptsAnyPermutation) {
return -1;
}
int dx = slot.xDisplayPosition - (ingredient.relx + offsetx);
int dy = slot.yDisplayPosition - (ingredient.rely + offsety);
int cost = dx * dx + dy * dy;
if (slot.getHasStack()) {
cost += 10_000;
}
return cost;
}
private boolean canUseSlot(Slot slot, ItemStack stack) {
if (stack == null || stack.stackSize <= 0 || !slot.isItemValid(stack)) {
return false;
}
if (!slot.inventory.isItemValidForSlot(slot.getSlotIndex(), stack)) {
return false;
}
return !slot.getHasStack() || canStack(slot.getStack(), stack);
}
private boolean isDestinationCandidate(Slot slot) {
return slot != null && !(slot.inventory instanceof InventoryPlayer) && !(slot instanceof SlotCrafting);
}
private boolean hasEmptyMappings(Slot[][] recipeSlots) {
for (Slot[] recipeSlot : recipeSlots) {
if (recipeSlot.length == 0) {
return true;
}
}
return false;
}
private static class Assignment {
int cost = Integer.MAX_VALUE;
Slot[] slots;
void set(int cost, Slot[] slots) {
this.cost = cost;
this.slots = slots.clone();
}
void copyFrom(Assignment other) {
this.cost = other.cost;
this.slots = other.slots == null ? null : other.slots.clone();
}
}
}
@@ -84,12 +84,13 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
efuels.add(Item.getItemFromBlock(Blocks.wooden_door));
efuels.add(Item.getItemFromBlock(Blocks.trapped_chest));
Stopwatch stopwatch = Stopwatch.createStarted();
final Stopwatch stopwatch = Stopwatch.createStarted();
final List<ItemStack> itemsSnapshot = new ArrayList<>(ItemList.items);
if (parallel) {
try {
FurnaceRecipeHandler.afuels = ItemList.forkJoinPool
.submit(
() -> ItemList.items.parallelStream().map(TemplateRecipeHandler::identifyFuel)
() -> itemsSnapshot.parallelStream().map(TemplateRecipeHandler::identifyFuel)
.filter(Objects::nonNull).collect(Collectors.toCollection(ArrayList::new)))
.get();
} catch (InterruptedException | ExecutionException e) {
@@ -97,7 +98,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
e.printStackTrace();
}
} else {
FurnaceRecipeHandler.afuels = ItemList.items.stream().map(TemplateRecipeHandler::identifyFuel)
FurnaceRecipeHandler.afuels = itemsSnapshot.stream().map(TemplateRecipeHandler::identifyFuel)
.filter(Objects::nonNull).collect(Collectors.toCollection(ArrayList::new));
}
@@ -380,8 +381,13 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
public LinkedList<RecipeTransferRect> transferRects = new LinkedList<>();
public TemplateRecipeHandler() {
loadTransferRects();
RecipeTransferRectHandler.registerRectsToGuis(getRecipeTransferRectGuis(), transferRects);
try {
loadTransferRects();
RecipeTransferRectHandler.registerRectsToGuis(getRecipeTransferRectGuis(), transferRects);
} catch (Exception e) {
NEIClientConfig.logger
.error("NEI: Failed to load transfer rects for {}: {}", getClass().getName(), e.toString());
}
}
/**
@@ -535,6 +541,24 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
}
}
public ICraftingHandler getAllRecipeHandler() {
TemplateRecipeHandler handler = newInstance();
if (handler.transferRects.isEmpty()) {
handler.loadCraftingRecipes("all");
return handler;
}
final String transferRectId = handler.specifyTransferRect();
for (RecipeTransferRect rect : handler.transferRects) {
if (transferRectId == null || transferRectId.equals(rect.outputId)) {
handler.loadCraftingRecipes(rect.outputId, rect.results);
return handler;
}
}
return handler.getRecipeHandler("all");
}
public ICraftingHandler getRecipeHandler(String outputId, Object... results) {
TemplateRecipeHandler handler = newInstance();
handler.loadCraftingRecipes(outputId, results);
@@ -49,9 +49,9 @@ public class RecipeChainIterator implements Iterator<Map<RecipeId, Long>> {
final Set<RecipeId> skipRecipes = new HashSet<>();
final Map<RecipeId, Long> rootRecipes = new HashMap<>();
final HashMap<BookmarkItem, BookmarkItem> preferredItems = new HashMap<>(this.math.preferredItems);
preferredItems.values()
.removeIf(item -> item == null || item.amount == 0 || this.precessedRecipes.contains(item.recipeId));
preferredItems.keySet().removeIf(item -> item.amount == 0);
preferredItems.values().removeIf(
item -> item == null || item.getAmount() == 0 || this.precessedRecipes.contains(item.recipeId));
preferredItems.keySet().removeIf(item -> item.getAmount() == 0);
for (Map.Entry<BookmarkItem, BookmarkItem> entry : preferredItems.entrySet()) {
final BookmarkItem keyItem = entry.getKey();
@@ -14,6 +14,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import codechicken.nei.ItemStackAmount;
import codechicken.nei.ItemStackSet;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
@@ -48,6 +49,7 @@ public class RecipeChainMath {
public final List<BookmarkItem> recipeResults = new ArrayList<>();
public final Map<BookmarkItem, BookmarkItem> preferredItems = new HashMap<>();
public final Map<BookmarkItem, Long> requiredAmount = new HashMap<>();
public final List<ItemStack> containerItems = new ArrayList<>();
private final List<ItemStack> containerItemsBlacklist = new ArrayList<>();
@@ -59,9 +61,9 @@ public class RecipeChainMath {
if (item.recipeId == null || item.type == BookmarkItemType.ITEM) {
this.initialItems.add(item.copy());
} else if (item.type == BookmarkItemType.INGREDIENT) {
this.recipeIngredients.add(item.copyWithAmount(0));
this.recipeIngredients.add(item.copyWithMultiplier(0));
} else {
this.recipeResults.add(item.copyWithAmount(0));
this.recipeResults.add(item.copyWithMultiplier(0));
multipliers.put(
item.recipeId,
Math.max(multipliers.getOrDefault(item.recipeId, 0L), item.getMultiplier()));
@@ -129,17 +131,18 @@ public class RecipeChainMath {
visited.add(recipeId);
for (BookmarkItem ingrItem : this.recipeIngredients) {
if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId) && !preferredItems.containsKey(ingrItem)) {
if (!ingrItem.emptyFactor() && recipeId.equals(ingrItem.recipeId)
&& !preferredItems.containsKey(ingrItem)) {
BookmarkItem activeItem = null;
BookmarkItem prefItem = null;
for (BookmarkItem item : this.recipeResults) {
if (item.factor > 0 && !visited.contains(item.recipeId) && item.containsItems(ingrItem)) {
if (!item.emptyFactor() && !visited.contains(item.recipeId) && item.containsItems(ingrItem)) {
if ((activeItem == null || item.factor > activeItem.factor) && NEIClientUtils
if ((activeItem == null || item.getAmount(1) > activeItem.getAmount(1)) && NEIClientUtils
.areStacksSameTypeCraftingWithNBT(ingrItem.itemStack, item.itemStack)) {
activeItem = item;
} else if (prefItem == null || item.factor > prefItem.factor) {
} else if (prefItem == null || item.getAmount(1) > prefItem.getAmount(1)) {
prefItem = item;
}
@@ -164,7 +167,7 @@ public class RecipeChainMath {
int maxDepth = 0;
for (BookmarkItem ingrItem : preferredItems.keySet()) {
if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId)) {
if (!ingrItem.emptyFactor() && recipeId.equals(ingrItem.recipeId)) {
maxDepth = Math.max(maxDepth, getMaxDepth(preferredItems.get(ingrItem).recipeId, preferredItems) + 1);
}
}
@@ -176,7 +179,7 @@ public class RecipeChainMath {
visited.add(recipeId);
for (BookmarkItem ingrItem : this.recipeIngredients) {
if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId) && preferredItems.containsKey(ingrItem)) {
if (!ingrItem.emptyFactor() && recipeId.equals(ingrItem.recipeId) && preferredItems.containsKey(ingrItem)) {
BookmarkItem prefItem = preferredItems.get(ingrItem);
if (visited.contains(prefItem.recipeId)) {
@@ -184,6 +187,7 @@ public class RecipeChainMath {
} else {
removeLoop(prefItem.recipeId, preferredItems, visited);
}
}
}
@@ -195,22 +199,22 @@ public class RecipeChainMath {
for (BookmarkItem item : this.recipeResults) {
if (this.outputRecipes.containsKey(item.recipeId) && !ROOT_RECIPE_ID.equals(item.recipeId)) {
final long amount = item.factor * this.outputRecipes.get(item.recipeId);
final long multiplier = this.outputRecipes.get(item.recipeId);
final long amount = item.getAmount(multiplier);
rootIngredients.add(
BookmarkItem.of(
-1,
item.getItemStack(amount),
item.getStackSize(amount),
ROOT_RECIPE_ID,
BookmarkItemType.INGREDIENT,
BookmarkItem.generatePermutations(item.itemStack, null)));
BookmarkItem.builder(-1, item.getItemStack(amount)).factor(item.getStackSize(amount))
.permutations(BookmarkItem.Builder.generatePermutations(item.itemStack))
.recipeId(ROOT_RECIPE_ID).type(BookmarkItemType.INGREDIENT).build());
}
}
this.outputRecipes.clear();
this.outputRecipes.put(ROOT_RECIPE_ID, 1L);
this.recipeResults.removeIf(item -> ROOT_RECIPE_ID.equals(item.recipeId));
this.recipeResults.add(BookmarkItem.of(-1, ROOT_ITEM, 1, ROOT_RECIPE_ID, BookmarkItemType.RESULT));
this.recipeResults.add(
BookmarkItem.builder(-1, ROOT_ITEM).factor(1).recipeId(ROOT_RECIPE_ID).type(BookmarkItemType.RESULT)
.build());
this.recipeIngredients.addAll(rootIngredients);
return ROOT_RECIPE_ID;
@@ -226,24 +230,20 @@ public class RecipeChainMath {
public static RecipeChainMath of(Recipe recipe, long multiplier) {
final List<BookmarkItem> chainItems = new ArrayList<>();
final RecipeId recipeId = recipe.getRecipeId();
final ItemStack result = recipe.getResult();
final ItemStackSet ingredients = new ItemStackSet();
chainItems.add(BookmarkItem.of(-1, result, StackInfo.getAmount(result), recipeId, BookmarkItemType.RESULT));
chainItems
.add(BookmarkItem.builder(-1, result, recipe, BookmarkItemType.RESULT).multiplier(multiplier).build());
for (RecipeIngredient ingr : recipe.getIngredients()) {
chainItems.add(
BookmarkItem.of(
-1,
ingr.getItemStack(),
ingr.getAmount(),
recipeId,
BookmarkItemType.INGREDIENT,
BookmarkItem.generatePermutations(ingr.getItemStack(), recipe)));
ingredients.add(ingr.getItemStack());
}
for (BookmarkItem item : chainItems) {
item.amount *= multiplier;
for (ItemStack ingrStack : ingredients.values()) {
chainItems.add(
BookmarkItem.builder(-1, ingrStack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier)
.build());
}
return new RecipeChainMath(chainItems, Collections.emptySet());
@@ -253,7 +253,7 @@ public class RecipeChainMath {
final ItemStackAmount missedItems = new ItemStackAmount();
for (BookmarkItem item : this.recipeResults) {
long amount = item.amount - this.requiredAmount.getOrDefault(item, 0L);
long amount = item.getAmount() - this.requiredAmount.getOrDefault(item, 0L);
if (amount > 0) {
missedItems.add(item.getItemStack(amount));
}
@@ -261,7 +261,7 @@ public class RecipeChainMath {
for (BookmarkItem item : this.recipeIngredients) {
long amount = this.requiredAmount.containsKey(this.preferredItems.get(item)) ? 0
: this.requiredAmount.getOrDefault(item, item.amount);
: this.requiredAmount.getOrDefault(item, item.getAmount());
if (amount > 0) {
missedItems.add(item.getItemStack(amount));
}
@@ -279,11 +279,11 @@ public class RecipeChainMath {
private void resetCalculation() {
for (BookmarkItem item : this.recipeIngredients) {
item.amount = 0;
item.multiplier = 0;
}
for (BookmarkItem item : this.recipeResults) {
item.amount = 0;
item.multiplier = 0;
}
this.preferredItems.clear();
@@ -313,8 +313,8 @@ public class RecipeChainMath {
}
for (BookmarkItem prefItem : this.recipeResults) {
if (prefItem.factor > 0 && this.outputRecipes.containsKey(prefItem.recipeId)) {
final long prefAmount = prefItem.factor * this.outputRecipes.get(prefItem.recipeId);
if (!prefItem.emptyFactor() && this.outputRecipes.containsKey(prefItem.recipeId)) {
final long prefAmount = prefItem.getAmount(this.outputRecipes.get(prefItem.recipeId));
if (prefItem.itemStack.getItem().hasContainerItem(prefItem.itemStack)) {
this.containerItemsBlacklist.add(prefItem.itemStack);
@@ -327,9 +327,9 @@ public class RecipeChainMath {
}
for (BookmarkItem prefItem : this.recipeResults) {
if (prefItem.factor > 0 && this.outputRecipes.containsKey(prefItem.recipeId)
if (!prefItem.emptyFactor() && this.outputRecipes.containsKey(prefItem.recipeId)
&& this.requiredAmount.containsKey(prefItem)) {
final long prefAmount = prefItem.factor * this.outputRecipes.get(prefItem.recipeId);
final long prefAmount = prefItem.getAmount(this.outputRecipes.get(prefItem.recipeId));
this.requiredAmount.put(prefItem, this.requiredAmount.get(prefItem) - prefAmount);
}
}
@@ -338,14 +338,6 @@ public class RecipeChainMath {
return this;
}
private void prepareIngredients(RecipeId recipeId, long stepShift, List<RecipeId> visited) {
for (BookmarkItem item : this.recipeIngredients) {
if (item.factor > 0 && recipeId.equals(item.recipeId)) {
calculateSuitableRecipe(item, item.factor * stepShift, visited);
}
}
}
private void calculateSuitableRecipe(BookmarkItem ingrItem, long ingrAmount, List<RecipeId> visited) {
final BookmarkItem prefItem = this.preferredItems.get(ingrItem);
@@ -366,7 +358,7 @@ public class RecipeChainMath {
if (ingrAmount > 0) {
for (BookmarkItem item : this.initialItems) {
if (item.containsItems(ingrItem)
&& (ingrAmount = addRequiredAmount(item, ingrAmount, item.amount)) == 0) {
&& (ingrAmount = addRequiredAmount(item, ingrAmount, item.getAmount())) == 0) {
break;
}
}
@@ -379,19 +371,27 @@ public class RecipeChainMath {
addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE);
} else {
addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE);
final long shift = (long) Math
.ceil((this.requiredAmount.get(prefItem) - prefItem.amount) / (double) prefItem.factor);
final long multiplier = Math
.max(0, prefItem.getMultiplierFromAmount(this.requiredAmount.get(prefItem)) - prefItem.multiplier);
if (shift > 0) {
addShift(prefItem.recipeId, shift);
if (multiplier > 0) {
addShift(prefItem.recipeId, multiplier);
visited.add(prefItem.recipeId);
prepareIngredients(prefItem.recipeId, shift, visited);
prepareIngredients(prefItem.recipeId, multiplier, visited);
visited.remove(prefItem.recipeId);
}
}
}
private void prepareIngredients(RecipeId recipeId, long multiplier, List<RecipeId> visited) {
for (BookmarkItem item : this.recipeIngredients) {
if (!item.emptyFactor() && recipeId.equals(item.recipeId)) {
calculateSuitableRecipe(item, item.getAmount(multiplier), visited);
}
}
}
private long addRequiredAmount(BookmarkItem prefItem, long ingrAmount, long maxAmount) {
long shiftAmount = this.requiredAmount.getOrDefault(prefItem, 0L);
@@ -489,7 +489,7 @@ public class RecipeChainMath {
final NBTTagCompound toolStats = tagCompound.getCompoundTag("GT.ToolStats");
final long maxDamage = toolStats.getLong("MaxDamage");
final long damage = toolStats.getLong("Damage");
final long leftSteps = (long) Math.ceil((maxDamage - damage) / (double) damagePerContainerCraft);
final long leftSteps = (maxDamage - damage + damagePerContainerCraft - 1) / damagePerContainerCraft;
final long availableSteps = Math.min(steps, Math.max(1, leftSteps));
steps -= availableSteps;
@@ -552,13 +552,13 @@ public class RecipeChainMath {
private void addShift(RecipeId recipeId, long shift) {
for (BookmarkItem item : this.recipeIngredients) {
if (recipeId.equals(item.recipeId)) {
item.amount += item.factor * shift;
item.multiplier += shift;
}
}
for (BookmarkItem item : this.recipeResults) {
if (recipeId.equals(item.recipeId)) {
item.amount += item.factor * shift;
item.multiplier += shift;
}
}
}
@@ -73,7 +73,7 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler {
if (!this.lastControlKey) {
final List<ItemStack> items = inventory.values();
for (BookmarkItem item : math.recipeResults) {
if (item.factor > 0 && this.math.outputRecipes.containsKey(item.recipeId)) {
if (!item.emptyFactor() && this.math.outputRecipes.containsKey(item.recipeId)) {
long amount = 0;
for (ItemStack stack : items) {
@@ -83,15 +83,18 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler {
}
}
if (amount >= item.amount) {
final long itemAmount = item.factor * this.math.outputRecipes.get(item.recipeId);
if (amount >= item.getAmount()) {
final long itemAmount = item.getAmount(this.math.outputRecipes.get(item.recipeId));
if (itemAmount > 0) {
amount += itemAmount - amount % itemAmount;
}
this.math.outputRecipes.put(
item.recipeId,
Math.max(this.math.outputRecipes.get(item.recipeId), amount / item.factor));
Math.max(
this.math.outputRecipes.get(item.recipeId),
item.getMultiplierFromAmount(amount)));
}
}
}
@@ -122,7 +125,7 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler {
for (BookmarkItem item : math.recipeIngredients) {
final long amount = math.requiredAmount.containsKey(math.preferredItems.get(item)) ? 0
: math.requiredAmount.getOrDefault(item, item.amount);
: math.requiredAmount.getOrDefault(item, item.getAmount());
if (amount > 0) {
inputs.add(item.getItemStack(amount));
@@ -130,7 +133,7 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler {
}
for (BookmarkItem item : math.recipeResults) {
final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L);
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
if (amount > 0) {
if (math.outputRecipes.containsKey(item.recipeId)) {
@@ -155,12 +158,12 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler {
if (inventory.contains(item.itemStack)) {
final long invAmount = inventory.get(item.itemStack) * item.fluidCellAmount;
if ((item.amount - invAmount) > 0) {
inputs.add(item.getItemStack(item.amount - invAmount));
if ((item.getAmount() - invAmount) > 0) {
inputs.add(item.getItemStack(item.getAmount() - invAmount));
}
if (Math.min(item.amount, invAmount) > 0) {
available.add(item.getItemStack(Math.min(item.amount, invAmount)));
if (Math.min(item.getAmount(), invAmount) > 0) {
available.add(item.getItemStack(Math.min(item.getAmount(), invAmount)));
}
} else {
@@ -6,18 +6,18 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntPredicate;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.util.EnumChatFormatting;
import org.apache.commons.io.IOUtils;
import org.lwjgl.input.Keyboard;
@@ -25,6 +25,7 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.Button;
import codechicken.nei.ClientHandler;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
@@ -45,58 +46,94 @@ public class DebugHandlerWidget extends Widget {
private static class HandlerInfoRecord {
public HandlerInfo info;
public String handlerKey;
public int yShift;
public int width;
public int height;
public boolean multiWidgets;
public int order;
public boolean useCustomScroll;
public boolean allowOverflowX;
public boolean allowOverflowY;
public boolean showFavorites;
public boolean showOverlay;
public boolean showBadge;
private String csvLine;
public HandlerInfoRecord(String handlerKey, HandlerInfo info) {
this.info = info;
this.handlerKey = handlerKey;
this.yShift = info.getYShift();
this.width = info.getWidth();
this.height = info.getHeight();
this.multiWidgets = info.isMultipleWidgetsAllowed();
this.order = NEIClientConfig.handlerOrdering.getOrDefault(handlerKey, 0);
this.useCustomScroll = info.getUseCustomScroll();
this.allowOverflowX = info.isAllowOverflowX();
this.allowOverflowY = info.isAllowOverflowY();
this.showFavorites = info.getShowFavoritesButton();
this.showOverlay = info.getShowOverlayButton();
this.showBadge = info.getShowBadge();
this.csvLine = toCsvLine();
}
public void applyTo(HandlerInfo info) {
info.setYShift(this.yShift);
info.setHandlerDimensions(this.width, this.height);
info.setMultipleWidgetsAllowed(this.multiWidgets);
info.setUseCustomScroll(this.useCustomScroll);
info.setShowFavoritesButton(this.showFavorites);
info.setShowOverlayButton(this.showOverlay);
public void apply() {
this.info.setYShift(this.yShift);
this.info.setHandlerDimensions(this.width, this.height);
this.info.setMultipleWidgetsAllowed(this.multiWidgets);
this.info.setAllowOverflowX(this.allowOverflowX);
this.info.setAllowOverflowY(this.allowOverflowY);
this.info.setShowFavoritesButton(this.showFavorites);
this.info.setShowOverlayButton(this.showOverlay);
this.info.setShowBadge(this.showBadge);
NEIClientConfig.handlerOrdering.put(this.handlerKey, this.order);
}
public void apply(String csvLine) {
final String[] parts = csvLine.split(",");
this.yShift = intOrDefault(parts[1], this.yShift);
this.height = intOrDefault(parts[2], this.height);
this.width = intOrDefault(parts[3], this.width);
this.multiWidgets = intOrDefault(parts[4], this.multiWidgets ? 1 : 0) == 1;
this.order = intOrDefault(parts[5], this.order);
this.allowOverflowX = intOrDefault(parts[6], this.allowOverflowX ? 1 : 0) == 1;
this.allowOverflowY = intOrDefault(parts[7], this.allowOverflowY ? 1 : 0) == 1;
this.showFavorites = intOrDefault(parts[8], this.showFavorites ? 1 : 0) == 1;
this.showOverlay = intOrDefault(parts[9], this.showOverlay ? 1 : 0) == 1;
this.showBadge = intOrDefault(parts[10], this.showBadge ? 1 : 0) == 1;
apply();
}
public String toCsvLine() {
return String.join(
",",
handlerKey,
String.valueOf(yShift),
String.valueOf(height),
String.valueOf(width),
String.valueOf(multiWidgets ? 1 : 0),
String.valueOf(order),
String.valueOf(useCustomScroll ? 1 : 0),
String.valueOf(showFavorites ? 1 : 0),
String.valueOf(showOverlay ? 1 : 0));
this.handlerKey,
String.valueOf(this.yShift),
String.valueOf(this.height),
String.valueOf(this.width),
String.valueOf(this.multiWidgets ? 1 : 0),
String.valueOf(this.order),
String.valueOf(this.allowOverflowX ? 1 : 0),
String.valueOf(this.allowOverflowY ? 1 : 0),
String.valueOf(this.showFavorites ? 1 : 0),
String.valueOf(this.showOverlay ? 1 : 0),
String.valueOf(this.showBadge ? 1 : 0));
}
public boolean isUnmodified() {
return this.csvLine.equals(this.toCsvLine());
}
private int intOrDefault(String str, int defaultValue) {
if (str == null || str.isEmpty()) return defaultValue;
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
private static final int WINDOW_WIDTH = 176;
@@ -105,7 +142,7 @@ public class DebugHandlerWidget extends Widget {
private static final int LABEL_WIDTH = 50;
private static final String TOOLTIP_PREFIX = "debug.RecipeHandler.";
private static final String HEADER = "HandlerId,YShift,Height,Width,MultiWidgets,Order,UseCustomScroll,ShowFavorites,ShowOverlay";
private static final String HEADER = "HandlerId,YShift,Height,Width,MultiWidgets,Order,AllowOverflowX,AllowOverflowY,ShowFavorites,ShowOverlay,ShowBadge";
private final int[] COLORS = new int[] { 0x2200FF00, 0x22FF0000, 0x220000FF, 0x2200FFFF, 0x22FF00FF, 0x22FFFF00 };
public static DebugHandlerWidget instance = new DebugHandlerWidget();
@@ -114,12 +151,11 @@ public class DebugHandlerWidget extends Widget {
private final Map<String, IUpdatableWidget> values = new LinkedHashMap<>();
private Map<String, HandlerInfoRecord> patches = new HashMap<>();
private IRecipeHandler handler;
private HandlerInfo handlerInfo;
private HandlerInfoRecord record;
private Point dragPoint = null;
private boolean showWidget = false;
private Button resetButton;
private final DrawableResource BG_TEXTURE = new DrawableBuilder(
"nei:textures/gui/recipebg.png",
@@ -147,29 +183,19 @@ public class DebugHandlerWidget extends Widget {
y = addInfoWidget("HHack", y);
y = addInfoWidget("ModName", y);
y = addInfoWidget("ModId", y);
y = addInfoWidget("Override", y);
y = addOverrideInfoWidget("Override", y);
y = addTextFieldWidget("Order", 0, (field, oldText) -> {
if (field.getInteger() != record.order) {
record.order = field.getInteger();
saveHandlerInfoPatch();
}
}, y);
record.order = field.getInteger();
saveHandlerInfoPatch();
}, null, y);
y = addTextFieldWidget("yShift", 0, (field, oldText) -> {
if (field.getInteger() != handlerInfo.getYShift()) {
handlerInfo.setYShift(field.getInteger());
record.yShift = field.getInteger();
saveHandlerInfoPatch();
}
}, y);
record.yShift = field.getInteger();
saveHandlerInfoPatch();
}, null, y);
y = addCheckboxWidget("MultiWidgets", button -> {
handlerInfo.setMultipleWidgetsAllowed(button.value);
record.multiWidgets = button.value;
saveHandlerInfoPatch();
}, y);
@@ -182,43 +208,40 @@ public class DebugHandlerWidget extends Widget {
this.values.put("height_readonly", readonly);
y = addTextFieldWidget("Height", HandlerInfo.DEFAULT_HEIGHT, (field, oldText) -> {
if (field.getInteger() != handlerInfo.getHeight()) {
handlerInfo.setHandlerDimensions(handlerInfo.getWidth(), field.getInteger());
record.height = field.getInteger();
saveHandlerInfoPatch();
}
}, y);
record.height = field.getInteger();
saveHandlerInfoPatch();
}, newValue -> newValue > 0, y);
y = addTextFieldWidget("Width", HandlerInfo.DEFAULT_WIDTH, (field, oldText) -> {
record.width = field.getInteger();
saveHandlerInfoPatch();
}, newValue -> newValue >= HandlerInfo.DEFAULT_WIDTH, y);
if (field.getInteger() != handlerInfo.getWidth()) {
handlerInfo.setHandlerDimensions(field.getInteger(), handlerInfo.getHeight());
record.width = field.getInteger();
saveHandlerInfoPatch();
}
y = addCheckboxWidget("AllowOverflowX", button -> {
record.allowOverflowX = button.value;
saveHandlerInfoPatch();
}, y);
y = addCheckboxWidget("UseCustomScroll", button -> {
handlerInfo.setUseCustomScroll(button.value);
record.useCustomScroll = button.value;
y = addCheckboxWidget("AllowOverflowY", button -> {
record.allowOverflowY = button.value;
saveHandlerInfoPatch();
}, y);
y = addCheckboxWidget("ShowFavorites", button -> {
handlerInfo.setShowFavoritesButton(button.value);
record.showFavorites = button.value;
saveHandlerInfoPatch();
}, y);
y = addCheckboxWidget("ShowOverlay", button -> {
handlerInfo.setShowOverlayButton(button.value);
record.showOverlay = button.value;
saveHandlerInfoPatch();
}, y);
y = addCheckboxWidget("ShowBadge", button -> {
record.showBadge = button.value;
saveHandlerInfoPatch();
}, y);
this.h = 10 + LINE_HEIGHT + y;
this.container.h = this.h - INLINE_PADDING * 2 - LINE_HEIGHT;
}
@@ -241,9 +264,10 @@ public class DebugHandlerWidget extends Widget {
return y + value.h + 1;
}
private int addTextFieldWidget(String key, int defaultValue, BiConsumer<IntegerField, String> onChange, int y) {
private int addTextFieldWidget(String key, int defaultValue, BiConsumer<IntegerField, String> onChange,
IntPredicate validator, int y) {
final LabelWidget label = new LabelWidget(0x888888, false);
final IntegerField value = new IntegerField(key, onChange, defaultValue);
final IntegerField value = new IntegerField(key, onChange, validator, defaultValue);
label.y = value.y = y;
value.w = 60;
@@ -278,10 +302,52 @@ public class DebugHandlerWidget extends Widget {
return y + value.h + 1;
}
private int addOverrideInfoWidget(String key, int y) {
final LabelWidget label = new LabelWidget(0x888888, false);
final LabelWidget value = new LabelWidget(0xffffff, true);
this.resetButton = new Button(NEIClientUtils.translate("debug.RecipeHandler.reset")) {
@Override
public boolean onButtonPress(boolean rightclick) {
if (record != null) {
record.apply(record.csvLine);
saveHandlerInfoPatch();
record = null;// force update of button state
}
return true;
}
@Override
public void draw(int mousex, int mousey) {
this.state = record != null && record.isUnmodified() ? 2 : 0;
super.draw(mousex, mousey);
}
};
this.resetButton.w = 58;
this.resetButton.h = 16;
this.resetButton.y = label.y = value.y = y;
this.resetButton.x = this.w - INLINE_PADDING * 2 - this.resetButton.w - 1;
label.w = LABEL_WIDTH;
value.x = LABEL_WIDTH + 5;
value.w = this.resetButton.x - value.x - 2;
label.updateValue(NEIClientUtils.translate(TOOLTIP_PREFIX + key));
this.container.addWidget(label);
this.container.addWidget(value);
this.values.put(key.toLowerCase(), value);
this.container.addWidget(this.resetButton);
return y + value.h + 1;
}
@Override
public void draw(int mx, int my) {
if (this.handler != null) {
if (this.record != null) {
GL11.glColor4f(1F, 1F, 1F, 1F);
GL11.glScaled(1, 1, 2f);
GL11.glDisable(GL11.GL_DEPTH_TEST);
@@ -318,24 +384,22 @@ public class DebugHandlerWidget extends Widget {
if (this.showWidget && gui instanceof GuiRecipe recipe) {
if (this.handler != recipe.getHandler()) {
this.handler = recipe.getHandler();
this.handlerInfo = GuiRecipeTab.getHandlerInfo(handler);
if (this.record == null || !this.record.handlerKey.equals(getHandlerID(recipe.getHandler()))) {
final IRecipeHandler handler = recipe.getHandler();
this.record = this.patches.computeIfAbsent(
getHandlerID(handler),
handlerKey -> new HandlerInfoRecord(handlerKey, handlerInfo));
handlerKey -> new HandlerInfoRecord(handlerKey, GuiRecipeTab.getHandlerInfo(handler)));
final boolean isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream()
.map(pattern -> pattern.matcher(this.handler.getHandlerId())).anyMatch(Matcher::matches);
.map(pattern -> pattern.matcher(handler.getHandlerId())).anyMatch(Matcher::matches);
this.values.get("name").updateValue(this.handler.getRecipeName());
this.values.get("id").updateValue(this.handler.getOverlayIdentifier());
this.values.get("key").updateValue(this.handler.getHandlerId());
this.values.get("name").updateValue(handler.getRecipeName());
this.values.get("id").updateValue(handler.getOverlayIdentifier());
this.values.get("key").updateValue(handler.getHandlerId());
this.values.get("hhack").updateValue(
isHeightHackApplied ? NEIClientUtils.translate("debug.RecipeHandler.yes")
: NEIClientUtils.translate("debug.RecipeHandler.no"));
this.values.get("modname").updateValue(this.handlerInfo.getModName());
this.values.get("modid").updateValue(this.handlerInfo.getModId());
this.values.get("modname").updateValue(this.record.info.getModName());
this.values.get("modid").updateValue(this.record.info.getModId());
this.values.get("order").updateValue(String.valueOf(this.record.order));
this.values.get("yshift").updateValue(String.valueOf(this.record.yShift));
@@ -344,14 +408,14 @@ public class DebugHandlerWidget extends Widget {
this.values.get("width").updateValue(String.valueOf(this.record.width));
this.values.get("multiwidgets").updateValue(String.valueOf(this.record.multiWidgets));
this.values.get("usecustomscroll").updateValue(String.valueOf(this.record.useCustomScroll));
this.values.get("allowoverflowx").updateValue(String.valueOf(this.record.allowOverflowX));
this.values.get("allowoverflowy").updateValue(String.valueOf(this.record.allowOverflowY));
this.values.get("showfavorites").updateValue(String.valueOf(this.record.showFavorites));
this.values.get("showoverlay").updateValue(String.valueOf(this.record.showOverlay));
this.values.get("showbadge").updateValue(String.valueOf(this.record.showBadge));
this.values.get("override").updateValue(
this.record.isUnmodified() ? NEIClientUtils.translate("debug.RecipeHandler.no")
: NEIClientUtils.translate("debug.RecipeHandler.yes"));
updateOverride();
this.container.removeIf(
widget -> this.values.get("height") == widget || this.values.get("height_readonly") == widget);
@@ -364,9 +428,7 @@ public class DebugHandlerWidget extends Widget {
}
} else {
this.handler = null;
this.record = null;
this.handlerInfo = null;
this.showWidget = false;
}
@@ -375,12 +437,12 @@ public class DebugHandlerWidget extends Widget {
@Override
public List<String> handleTooltip(int mx, int my, List<String> tooltip) {
return this.handler != null ? this.container.handleTooltip(mx, my, tooltip) : tooltip;
return this.record != null ? this.container.handleTooltip(mx, my, tooltip) : tooltip;
}
@Override
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
return this.handler != null ? this.container.handleHotkeys(mousex, mousey, hotkeys) : hotkeys;
return this.record != null ? this.container.handleHotkeys(mousex, mousey, hotkeys) : hotkeys;
}
@Override
@@ -390,7 +452,7 @@ public class DebugHandlerWidget extends Widget {
&& NEIClientUtils.shiftKey()) {
this.showWidget = !this.showWidget;
return true;
} else if (this.handler != null) {
} else if (this.record != null) {
if (this.container.handleKeyPress(keyID, keyChar)) {
return true;
@@ -406,7 +468,7 @@ public class DebugHandlerWidget extends Widget {
@Override
public void onGuiClick(int mx, int my) {
if (this.handler != null) {
if (this.record != null) {
this.container.onGuiClick(mx, my);
}
}
@@ -414,7 +476,7 @@ public class DebugHandlerWidget extends Widget {
@Override
public boolean handleClick(int mx, int my, int button) {
if (this.handler != null) {
if (this.record != null) {
if (new Rectangle4i(this.x + INLINE_PADDING, this.y + BORDER_PADDING, this.w - INLINE_PADDING * 2, 12)
.contains(mx, my)) {
@@ -424,19 +486,19 @@ public class DebugHandlerWidget extends Widget {
this.container.handleClick(mx, my, button);
}
return this.handler != null;
return this.record != null;
}
@Override
public boolean contains(int px, int py) {
return this.handler != null && super.contains(px, py);
return this.record != null && super.contains(px, py);
}
@Override
public void mouseUp(int mx, int my, int button) {
this.dragPoint = null;
if (this.handler != null) {
if (this.record != null) {
this.container.mouseUp(mx, my, button);
}
}
@@ -444,7 +506,7 @@ public class DebugHandlerWidget extends Widget {
@Override
public boolean onMouseWheel(int scrolled, int mousex, int mousey) {
if (this.handler != null && contains(mousex, mousey)) {
if (this.record != null && contains(mousex, mousey)) {
this.container.onMouseWheel(scrolled, mousex, mousey);
return true;
}
@@ -454,7 +516,7 @@ public class DebugHandlerWidget extends Widget {
@Override
public boolean handleClickExt(int mx, int my, int button) {
return this.handler != null && this.container.handleClickExt(mx, my, button);
return this.record != null && this.container.handleClickExt(mx, my, button);
}
@Override
@@ -539,27 +601,23 @@ public class DebugHandlerWidget extends Widget {
public void loadHandlerInfoPatch() {
ClientHandler.loadSettingsFile("handlers.patch", lines -> {
final List<String> linesList = lines.collect(Collectors.toCollection(ArrayList::new));
for (String line : lines.collect(Collectors.toCollection(HashSet::new))) {
if (HEADER.equals(line)) continue;
final String[] parts = Arrays.copyOf((line + ",null,null,null,null,null,null,null,null").split(","), 9);
final String handler = parts[0];
if (linesList.isEmpty() || !HEADER.equals(linesList.get(0))) {
return;
}
if (GuiRecipeTab.handlerMap.containsKey(handler)) {
final HandlerInfo info = GuiRecipeTab.handlerMap.get(handler);
final HandlerInfoRecord record = patches
.computeIfAbsent(handler, handlerKey -> new HandlerInfoRecord(handler, info));
linesList.remove(0);
record.yShift = intOrDefault(parts[1], info.getYShift());
record.height = intOrDefault(parts[2], info.getHeight());
record.width = intOrDefault(parts[3], info.getWidth());
record.multiWidgets = intOrDefault(parts[4], info.isMultipleWidgetsAllowed() ? 1 : 0) == 1;
record.order = intOrDefault(parts[5], NEIClientConfig.handlerOrdering.getOrDefault(handler, 0));
record.useCustomScroll = intOrDefault(parts[6], info.getUseCustomScroll() ? 1 : 0) == 1;
record.showFavorites = intOrDefault(parts[7], info.getShowFavoritesButton() ? 1 : 0) == 1;
record.showOverlay = intOrDefault(parts[8], info.getShowOverlayButton() ? 1 : 0) == 1;
for (String line : linesList) {
final String[] parts = line.split(",");
final String handlerKey = parts[0];
record.applyTo(info);
if (GuiRecipeTab.handlerMap.containsKey(handlerKey)) {
final HandlerInfoRecord record = patches.computeIfAbsent(
handlerKey,
key -> new HandlerInfoRecord(key, GuiRecipeTab.handlerMap.get(key)));
record.apply((line + ",null,null,null,null,null,null,null,null,null,null"));
}
}
@@ -585,9 +643,8 @@ public class DebugHandlerWidget extends Widget {
} catch (IOException e) {}
if (this.record != null) {
this.values.get("override").updateValue(
this.record.isUnmodified() ? NEIClientUtils.translate("debug.RecipeHandler.no")
: NEIClientUtils.translate("debug.RecipeHandler.yes"));
updateOverride();
record.apply();
}
if (NEIClientUtils.getGuiContainer() instanceof GuiRecipe<?>recipe) {
@@ -595,12 +652,15 @@ public class DebugHandlerWidget extends Widget {
}
}
private int intOrDefault(String str, int defaultValue) {
if (str == null || str.isEmpty()) return defaultValue;
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
return defaultValue;
private void updateOverride() {
this.container.removeIf(widget -> widget == this.resetButton);
if (this.record.isUnmodified()) {
this.values.get("override").updateValue(NEIClientUtils.translate("debug.RecipeHandler.no"));
} else {
this.values.get("override")
.updateValue(EnumChatFormatting.RED + NEIClientUtils.translate("debug.RecipeHandler.yes"));
this.container.addWidget(this.resetButton);
}
}
@@ -1,6 +1,7 @@
package codechicken.nei.recipe.debug;
import java.util.function.BiConsumer;
import java.util.function.IntPredicate;
import net.minecraft.client.Minecraft;
@@ -13,10 +14,13 @@ public class IntegerField extends TextField implements IUpdatableWidget {
protected int defaultValue = 0;
protected BiConsumer<IntegerField, String> onChange;
protected IntPredicate validator;
public IntegerField(String ident, BiConsumer<IntegerField, String> onChange, int defaultValue) {
public IntegerField(String ident, BiConsumer<IntegerField, String> onChange, IntPredicate validator,
int defaultValue) {
super(ident);
this.onChange = onChange;
this.validator = validator;
this.defaultValue = defaultValue;
this.h = 16;
this.z = 2;
@@ -68,8 +72,12 @@ public class IntegerField extends TextField implements IUpdatableWidget {
}
protected int getInteger() {
return getInteger(text());
}
protected int getInteger(String text) {
try {
return Integer.parseInt(text());
return Integer.parseInt(text);
} catch (NumberFormatException nfe) {
return this.defaultValue;
}
@@ -78,10 +86,17 @@ public class IntegerField extends TextField implements IUpdatableWidget {
@Override
public boolean onMouseWheel(int i, int mx, int my) {
if (!contains(mx, my)) return false;
setText(Integer.toString(getInteger() + i));
setText(Integer.toString(getInteger() - i));
return true;
}
@Override
public void setText(String newValue) {
if (validator == null || validator.test(getInteger(newValue))) {
super.setText(newValue);
}
}
public void updateValue(String newValue) {
setText(newValue);
}
@@ -22,7 +22,7 @@ public class DefaultStackStringifyHandler implements IStackStringifyHandler {
final NBTTagCompound nbTag = new NBTTagCompound();
nbTag.setString("strId", strId);
nbTag.setInteger("Count", saveStackSize ? stack.stackSize : 1);
nbTag.setShort("Damage", (short) stack.getItemDamage());
nbTag.setInteger("Damage", stack.getItemDamage());
if (stack.hasTagCompound() && !stack.getTagCompound().hasNoTags()) {
nbTag.setTag("tag", stack.getTagCompound().copy());
@@ -35,7 +35,7 @@ public class DefaultStackStringifyHandler implements IStackStringifyHandler {
final String strId = nbtTag.getString("strId");
nbtTag = (NBTTagCompound) nbtTag.copy();
nbtTag.setShort("id", (short) GameData.getItemRegistry().getId(strId)); // getObject
nbtTag.setInteger("id", GameData.getItemRegistry().getId(strId)); // getObject
final ItemStack stack = ItemStack.loadItemStackFromNBT(nbtTag);
@@ -179,11 +179,15 @@ public class ItemUntranslator {
}
private void collectDisplayNameForStack(ItemStack stack, Map<String, String> items) {
final String displayName = stripFormatting(stack.getDisplayName());
final String guidKey = determineGUIDForGeneration(stack, displayName, items);
try {
final String displayName = stripFormatting(stack.getDisplayName());
final String guidKey = determineGUIDForGeneration(stack, displayName, items);
if (!items.containsKey(guidKey)) {
items.put(guidKey, displayName);
if (!items.containsKey(guidKey)) {
items.put(guidKey, displayName);
}
} catch (Throwable e) {
NEIClientConfig.logger.warn("Failed to get display name for itemstack {}", stack, e);
}
}
+21 -1
View File
@@ -301,6 +301,11 @@ nei.options.inventory.guirecipe.handlerInfo=Handler Info Widget
nei.options.inventory.guirecipe.handlerInfo.tip=Use shift+d while in the Recipes GUI
nei.options.inventory.guirecipe.handlerInfo.true=Enabled
nei.options.inventory.guirecipe.handlerInfo.false=Disabled
nei.options.inventory.guirecipe.scrollPages=Scroll Pages
nei.options.inventory.guirecipe.scrollPages.true=Enabled
nei.options.inventory.guirecipe.scrollPages.false=Disabled
nei.options.inventory.guirecipe.maxHeight=Max Height
nei.options.inventory.showItemQuantityWidget=Show Item Quantity Widget
nei.options.inventory.showItemQuantityWidget.true=Yes
@@ -519,6 +524,8 @@ nei.options.tools.dump.itempanel.mode.2=Json
nei.options.tools.dump.itempanel.mode.3=PNG
nei.options.tools.dump.itempanel.icon.dumped=Item Panel icons were dumped to %s
nei.options.tools.dump.itempanel.icon.cancelled=Dump cancelled
nei.options.tools.dump.itempanel.icon.warning.1=§cWARNING§r: You are about to dump %s items at %sx%s resolution
nei.options.tools.dump.itempanel.icon.warning.2=Hold %s while clicking this button to proceed
nei.options.tools.dump.handlers=NEI Handlers
nei.options.tools.dump.handlers.dumped=handlers were dumped to %s
nei.options.tools.dump.handlers.mode.0=CSV
@@ -539,12 +546,15 @@ nei.debug.RecipeHandler.Height=Height
nei.debug.RecipeHandler.Width=Width
nei.debug.RecipeHandler.ShowFavorites=Show Favorites
nei.debug.RecipeHandler.ShowOverlay=Show Overlays
nei.debug.RecipeHandler.UseCustomScroll=Use Custom Scroll
nei.debug.RecipeHandler.ShowBadge=Show Badge
nei.debug.RecipeHandler.AllowOverflowY=Allow Overflow Y
nei.debug.RecipeHandler.AllowOverflowX=Allow Overflow X
nei.debug.RecipeHandler.Override=Override
nei.debug.RecipeHandler.checkbox.on=On
nei.debug.RecipeHandler.checkbox.off=Off
nei.debug.RecipeHandler.yes=Yes
nei.debug.RecipeHandler.no=No
nei.debug.RecipeHandler.reset=Reset
nei.recipe.page=Page %s/%s
nei.recipe.tooltip=Recipes
@@ -554,6 +564,16 @@ nei.recipe.shapeless=Shapeless Crafting
nei.recipe.furnace=Smelting
nei.recipe.fuel=Fuel
nei.recipe.fuel.smeltCount=Smelts %s items
nei.recipe.badge.nc=NC
nei.recipe.badge.nc.tooltip=Does not get consumed in the process
nei.recipe.badge.ncp=NC(P)
nei.recipe.badge.ncp.tooltip=Considered in parallel calculation, but not get consumed
nei.recipe.badge.chance.consume=Consume Chance: %s
nei.recipe.badge.chance.output=Output Chance: %s
# Deprecated
nei.recipe.fuel.required=%s required
# Deprecated