Compare commits

...
30 Commits
Author SHA1 Message Date
slprime 8e6b31de18 Speed up GUID cache and bookmark recipe lookups (#1119) 2026-11-13 23:25:20 +02:00
DarkShadow44andslprime 1a27b02412 Don't copy stack unless it changed (#1116)
Co-authored-by: slprime <[email protected]>
2026-11-13 01:53:07 +02:00
slprime 0185cd64b1 Optimize GUI overlap scan for item panels (#1118) 2026-11-12 01:35:53 +02:00
slprime fbbeafcd79 Fix WorldClient leak after leaving the world (#1115) 2026-11-10 19:22:17 +02:00
UltraProdigy 9839d1d08f ci: run build and test on release branches 2026-11-09 01:11:38 -05:00
slprime 84d42de15f Make name optional in API.addCollapsibleGroup (#1114) 2026-11-08 18:13:37 +02:00
slprime d04e1c8e83 Add API.addCollapsibleGroup for registering collapsible item groups (#1113) 2026-11-08 17:37:44 +02:00
slprime 514af5669c Rollback fluent setters in PositionedStack added in #1106 (#1110) 2026-11-06 21:26:08 +01:00
slprime 41c2b96c88 Fix NPE in StackInfo.getItemStackWithMinimumDamage for unregistered items (#1109) 2026-11-05 18:19:48 +02:00
slprime 8e1541eab1 Remove creative tab style option for recipe tabs (#1107) 2026-11-04 16:34:56 +02:00
slprime 9411541fd8 Move RecipeTransferRectHandler out of TemplateRecipeHandler and tidy up PositionedStack (#1106) 2026-11-03 20:35:40 +02:00
slprime ae0d6c6108 Add getResultStacks/getAuxiliaryStacks to IRecipeHandler (#1105) 2026-11-03 17:33:07 +02:00
slprime aea4c2866a Stop auto-creating NEI config files, add export buttons instead (#1104) 2026-11-02 22:20:09 +02:00
slprime c1a525594c Fix ConcurrentModificationException when iterating the crafting recipe list (#1103) 2026-11-02 19:38:18 +02:00
slprimeandMartin Robertz afc8a2380b Remove per-world config override (#1094)
Co-authored-by: Martin Robertz <[email protected]>
2026-11-02 18:20:18 +02:00
slprime a0b2430458 Remove hardcoded vanilla block name overrides (#1102) 2026-11-01 21:53:37 +02:00
Armisael5 4dc6be839b Add NEI Recipe Exporting (#1100) 2026-11-01 15:25:25 +02:00
slprime 030795e3c7 Fix NEI plugin loading crash under a SecurityManager (#1099) 2026-10-29 20:59:01 +02:00
slprime 897a22ad17 Run NEI hotkeys before third-party lastKeyTyped handlers (#1097) 2026-10-28 17:37:45 +02:00
5faed21218 Revert missing item count rendering changes from #1044 (#1091)
Co-authored-by: Martin Robertz <[email protected]>
Co-authored-by: slprime <[email protected]>
2026-10-26 00:21:23 +02:00
Worive aa9da2e0a0 Fix NEI initialization aborting while logging plugin errors (#1093) 2026-10-25 17:10:38 +02:00
slprime e9a3a494d5 Add Mouse Binding (#1092) 2026-10-25 02:49:38 +03:00
slprime ebe83f970f Fix group drag between bookmark namespaces (#1090) 2026-10-24 01:09:00 +03:00
slprime ad9beb7f90 Fill crafting grid with one recipe when Ctrl is held (#1089) 2026-10-23 23:44:16 +03:00
slprime 68bdb51c6b Update tree camera and scrollbar dragging every frame (#1087) 2026-10-22 23:58:41 +03:00
slprime e4e1964dec Fix NPE in BookmarksGridSlot when dragging a group across namespaces (#1086) 2026-10-22 16:22:23 +03:00
slprime 560618024f Autocrafting Optimization (#1084) 2026-10-21 01:24:40 +03:00
slprime e2a2d2a9df Batch 9-slice quads in DrawableResource (#1082) 2026-10-19 15:47:52 +03:00
slprime 7163630cb1 Move 9-slice setup into DrawableBuilder (#1081) 2026-10-16 23:18:28 +03:00
RessedandMartin Robertz ab168ad6ba Make StrictIdentifierFilter (<modid:name:meta>) resolve item on first match (#1080)
Co-authored-by: Martin Robertz <[email protected]>
2026-10-16 20:42:59 +03:00
135 changed files with 2872 additions and 1687 deletions

No files matched your search

+2 -2
View File
@@ -3,9 +3,9 @@ name: Build and test
on:
pull_request:
branches: [ master, main ]
branches: [ master, main, release/** ]
push:
branches: [ master, main ]
branches: [ master, main, release/** ]
jobs:
build-and-test:
+1 -1
View File
@@ -75,7 +75,7 @@ All items that match `filter` will show your information page. See `config/NEI/c
#### Item Information Config File
If you're a modpack creator and want to make custom item information pages, they can easily be added in `config/NEI/informationpages.cfg`, which has a few more examples.
If you're a modpack creator and want to make custom item information pages, they can easily be added in `config/NEI/informationpages.cfg`, which has a few more examples. The file is not created automatically: use the Export button in the NEI options under Tools > Default Config Files to copy the defaults to `config/NEI`.
## Other items of note:
@@ -8,6 +8,7 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.api.ShortcutInputHandler;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
@@ -55,12 +56,14 @@ public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSl
}
protected void updateLinePadding() {
GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
final Rectangle4i rect = new Rectangle4i(this.x, this.y - 1, 10, 2);
this.linePaddingStart = 0;
while (GuiInfo
.hideItemPanelSlot(guiContainer, new Rectangle4i(this.x + this.linePaddingStart, this.y - 1, 10, 2))) {
while (this.linePaddingStart < this.w && GuiInfo.hideItemPanelSlot(guiContainer, rect, handlers)) {
this.linePaddingStart += 10;
rect.x += 10;
}
}
@@ -574,7 +574,8 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
} else if (this.sortableGroup != null) {
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
if (overRowIndex >= 0 && this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
if (overRowIndex >= 0 && !isOverNamespaceControls(mousex, mousey)
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
this.grid.moveGroup(this.sortableGroup, overRowIndex);
}
@@ -1022,14 +1023,18 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
this.mouseDownSlot = -1;
}
@Override
public boolean onMouseWheel(int shift, int mousex, int mousey) {
if ((!inEditingState() || this.sortableGroup != null) && new Rectangle4i(
private boolean isOverNamespaceControls(int mousex, int mousey) {
return new Rectangle4i(
namespacePrev.x,
namespacePrev.y,
namespaceNext.x + namespaceNext.w - namespacePrev.x,
namespacePrev.h).contains(mousex, mousey)) {
namespacePrev.h).contains(mousex, mousey);
}
@Override
public boolean onMouseWheel(int shift, int mousex, int mousey) {
if ((!inEditingState() || this.sortableGroup != null) && isOverNamespaceControls(mousex, mousey)) {
if (shift > 0) {
this.storage.prevNamespace();
@@ -1108,7 +1113,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
if (this.sortableGroup != null && overRowIndex >= 0
&& this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
this.grid.moveGroup(this.sortableGroup, overRowIndex);
}
+100 -46
View File
@@ -2,8 +2,6 @@ package codechicken.nei;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
@@ -12,7 +10,8 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -37,8 +36,10 @@ import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import codechicken.core.ClassDiscoverer;
@@ -50,8 +51,13 @@ import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.bookmark.tree.GuiCraftingTree;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.RecipeItemInputHandler;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.recipe.debug.DebugHandlerWidget;
import cpw.mods.fml.client.CustomModLoadingErrorDisplayException;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
@@ -67,6 +73,22 @@ public class ClientHandler {
private static ClientHandler instance;
private final ArrayList<EntityItem> SMPmagneticItems = new ArrayList<>();
private static final String HANDLER_ORDERING = "handlerordering.csv";
public static final List<String> DEFAULT_CONFIGS = ImmutableList.of(
"serialhandlers.cfg",
"heighthackhandlers.cfg",
"hiddenhandlers.cfg",
"enableautofocus.cfg",
"guicraftablesblacklist.cfg",
"defaultbookmarkcontainers.cfg",
"hiddenitems.cfg",
"guidfilters.cfg",
"informationpages.cfg",
"collapsibleitems.cfg",
"recipeidblacklist.cfg",
"handlers.csv",
"catalysts.csv",
HANDLER_ORDERING);
private World lastworld;
public void addSMPMagneticItem(int i, World world) {
@@ -160,17 +182,30 @@ public class ClientHandler {
StackInfo.loadGuidFilters();
}
public static void loadSettingsFile(String resource, Consumer<Stream<String>> callback) {
loadSettingsFile(resource, (file, writer) -> {
String folder = resource.substring(resource.lastIndexOf(".") + 1);
URL defaultResource = ClientHandler.class.getResource("/assets/nei/" + folder + "/" + resource);
private static URL getDefaultResource(String resource) {
final String folder = resource.substring(resource.lastIndexOf(".") + 1);
return ClientHandler.class.getResource("/assets/nei/" + folder + "/" + resource);
}
if (defaultResource != null) {
try {
IOUtils.copy(defaultResource.openStream(), writer);
} catch (IOException e) {}
}
}, callback);
public static boolean exportDefaultConfig(String resource) {
final File file = new File(NEIClientConfig.configDir, resource);
if (file.exists() && !NEIClientUtils.backupFile(file)) return false;
if (HANDLER_ORDERING.equals(resource)) {
return writeDefaultHandlerOrdering();
}
final URL url = getDefaultResource(resource);
if (url == null) return false;
try {
FileUtils.copyURLToFile(url, file);
NEIClientConfig.logger.info("Exported default '{}' to {}", resource, file);
return true;
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to export default '{}' to file {}", resource, file, e);
return false;
}
}
public static boolean loadSettingsResource(String resource, Consumer<Stream<String>> callback) {
@@ -196,30 +231,24 @@ public class ClientHandler {
return false;
}
public static boolean loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
Consumer<Stream<String>> callback) {
File file = new File(NEIClientConfig.configDir, resource);
public static boolean loadSettingsFile(String resource, Consumer<Stream<String>> callback) {
final File file = new File(NEIClientConfig.configDir, resource);
final boolean fromJar = !file.exists();
if (!file.exists() && createDefault != null) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
createDefault.accept(file, writer);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default '{}' to file {}", resource, file, e);
}
}
try {
final URL url = fromJar ? getDefaultResource(resource) : file.toURI().toURL();
if (url == null) return false;
if (file.exists()) {
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) {
NEIClientConfig.logger.info("Loading '{}' from {}", resource, fromJar ? "JAR" : file);
callback.accept(
IOUtils.readLines(reader).stream()
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
.map(String::trim).filter(line -> !line.isEmpty()));
return true;
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
}
return false;
@@ -292,26 +321,46 @@ public class ClientHandler {
}
public static void loadHandlerOrdering() {
final String COMMA_DELIMITER = ",";
final String[] defaultHandlerOrdering = {
loadSettingsFile(
HANDLER_ORDERING,
lines -> lines.map(line -> line.split(",")).filter(parts -> parts.length == 2).forEach(parts -> {
String handlerId = parts[0];
int ordering = Integer.parseInt(parts[1]);
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
}));
}
private static boolean writeDefaultHandlerOrdering() {
final File file = new File(NEIClientConfig.configDir, HANDLER_ORDERING);
final List<String> lines = Lists.newArrayList(
"# Each line in this file should either be a comment (starts with '#') or an ordering.",
"# Ordering lines are <handler ID>,<ordering number>.",
"# Handlers will be sorted in order of number ascending, so smaller numbers first.",
"# Any handlers that are missing from this file will be assigned to 0.", "# Negative numbers are fine.",
"# If you delete this file, it will be regenerated with all registered handler IDs.", };
"# Any handlers that are missing from this file keep their own order, 0 for most of them.",
"# Negative numbers are fine.");
final Map<String, Integer> orders = new TreeMap<>();
final List<IRecipeHandler> handlers = new ArrayList<>();
handlers.addAll(GuiCraftingRecipe.craftinghandlers);
handlers.addAll(GuiCraftingRecipe.serialCraftingHandlers);
handlers.addAll(GuiUsageRecipe.usagehandlers);
handlers.addAll(GuiUsageRecipe.serialUsageHandlers);
loadSettingsFile("handlerordering.csv", (file, writer) -> {
List<String> toWrite = Lists.newArrayList(defaultHandlerOrdering);
GuiRecipeTab.handlerMap.keySet().stream().sorted()
.forEach(handlerId -> toWrite.add(String.format("%s" + COMMA_DELIMITER + "0", handlerId)));
try {
IOUtils.writeLines(toWrite, "\n", writer);
} catch (IOException e) {}
}, lines -> lines.map(line -> line.split(COMMA_DELIMITER)).filter(parts -> parts.length == 2).forEach(parts -> {
String handlerId = parts[0];
int ordering = Integer.parseInt(parts[1]);
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
}));
for (IRecipeHandler handler : handlers) {
orders.putIfAbsent(
NEIClientConfig.HANDLER_ID_FUNCTION.apply(handler),
NEIClientConfig.getHandlerOrder(handler));
}
orders.forEach((handlerId, order) -> lines.add(handlerId + "," + order));
try {
NEIClientConfig.logger.info("Creating default '{}' {}", HANDLER_ORDERING, file);
FileUtils.writeLines(file, StandardCharsets.UTF_8.name(), lines, "\n");
return true;
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default '{}' to file {}", HANDLER_ORDERING, file, e);
return false;
}
}
public static void loadPluginsList() {
@@ -398,6 +447,11 @@ public class ClientHandler {
if (event.world == mc.theWorld) {
NEIClientConfig.unloadWorld();
ItemMobSpawner.clearEntityReferences();
RecipeItemInputHandler.instance.clearTooltipCache();
RecipeHandlerRef.clearCache();
ItemPanels.itemPanel.craftablesPanel.clearGuiReferences();
DebugHandlerWidget.instance.clearGuiReferences();
NEIController.manager = null;
}
}
@@ -7,8 +7,11 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTBase.NBTPrimitive;
@@ -35,6 +38,7 @@ public class CollapsibleItems {
public ItemFilter filter;
public boolean expanded = false;
public String displayName = "";
public String unlocalizedName;
public void setFilter(String filter) {
this.filter = ItemStackFilterParser.parse(filter.trim());
@@ -46,15 +50,38 @@ public class CollapsibleItems {
this.guid = guid;
}
public String getDisplayName() {
if (this.unlocalizedName != null && !this.unlocalizedName.isEmpty()) {
final String translated = StatCollector.translateToLocal(this.unlocalizedName);
if (!translated.equals(this.unlocalizedName)) {
return translated;
}
}
return this.displayName;
}
public boolean matches(ItemStack stack) {
return this.filter.matches(stack);
}
}
public static class ApiGroup {
private String unlocalizedName;
public ApiGroup setName(String unlocalizedName) {
this.unlocalizedName = unlocalizedName;
return this;
}
}
private static final String STATE_KEY = "collapsibleitems";
protected static File statesFile;
protected static final List<GroupItem> groups = new ArrayList<>();
protected static final List<Supplier<GroupItem>> apiGroups = new CopyOnWriteArrayList<>();
protected static final Map<ItemStack, Integer> cache = new ConcurrentHashMap<>();
private CollapsibleItems() {}
@@ -77,11 +104,57 @@ public class CollapsibleItems {
ClientHandler.loadSettingsFile(
"collapsibleitems.cfg",
lines -> parseFile(lines.collect(Collectors.toCollection(ArrayList::new))));
for (Supplier<GroupItem> apiGroup : CollapsibleItems.apiGroups) {
try {
addGroup(apiGroup.get());
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to add API group", e);
}
}
}
loadStates();
}
public static ApiGroup registerGroup(String filter) {
final ApiGroup apiGroup = new ApiGroup();
CollapsibleItems.apiGroups.add(() -> {
GroupItem group = new GroupItem();
group.setFilter(filter);
group.unlocalizedName = apiGroup.unlocalizedName;
return group;
});
return apiGroup;
}
public static ApiGroup registerGroup(Iterable<ItemStack> items) {
final ApiGroup apiGroup = new ApiGroup();
final ItemStackSet filter = new ItemStackSet();
for (ItemStack stack : items) {
if (stack != null) filter.add(stack.copy());
}
if (filter.isEmpty()) return apiGroup;
final String key = filter.keys().stream()
.map(stack -> Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + stack.getItemDamage())
.sorted().collect(Collectors.joining(","));
final String guid = UUID.nameUUIDFromBytes(key.getBytes()).toString();
CollapsibleItems.apiGroups.add(() -> {
GroupItem group = new GroupItem();
group.setFilter(filter, guid);
group.unlocalizedName = apiGroup.unlocalizedName;
return group;
});
return apiGroup;
}
private static void parseFile(List<String> itemStrings) {
final JsonParser parser = new JsonParser();
GroupItem group = new GroupItem();
@@ -92,17 +165,8 @@ public class CollapsibleItems {
if (itemStr.startsWith("; ")) {
JsonObject settings = parser.parse(itemStr.substring(2)).getAsJsonObject();
if (settings.get("displayName") != null) {
group.displayName = settings.get("displayName").getAsString();
}
if (settings.get("unlocalizedName") != null) {
String unlocalizedName = settings.get("unlocalizedName").getAsString();
String displayName = StatCollector.translateToLocal(unlocalizedName);
if (!displayName.equals(unlocalizedName)) {
group.displayName = displayName;
}
group.unlocalizedName = settings.get("unlocalizedName").getAsString();
}
if (settings.get("expanded") != null) {
@@ -176,7 +240,7 @@ public class CollapsibleItems {
public static String getDisplayName(int groupIndex) {
if (groupIndex < CollapsibleItems.groups.size()) {
return CollapsibleItems.groups.get(groupIndex).displayName;
return CollapsibleItems.groups.get(groupIndex).getDisplayName();
}
return null;
@@ -9,7 +9,6 @@ import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -24,7 +23,6 @@ import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.io.IOUtils;
import com.google.common.io.Files;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
@@ -34,7 +32,6 @@ import codechicken.nei.api.API;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.StackInfo;
@@ -95,7 +92,7 @@ public class FavoriteRecipes {
for (int i = 0; i < handler.numRecipes(); i++) {
RecipeHandlerRef ref = null;
for (PositionedStack result : getOutputs(handler, i)) {
for (PositionedStack result : handler.getResultStacks(i)) {
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(result.items);
final FastKey key = new FastKey(stack);
@@ -130,11 +127,6 @@ public class FavoriteRecipes {
updateSubsetVisibility();
}
protected List<PositionedStack> getOutputs(IRecipeHandler handler, int recipeIndex) {
final PositionedStack pStackResult = handler.getResultStack(recipeIndex);
return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex);
}
};
public static final String FAVORITE_ITEM = "FavoriteItem";
@@ -244,14 +236,7 @@ public class FavoriteRecipes {
if (hasErrors) {
try {
Files.copy(
favoriteFile,
new File(favoriteFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
NEIClientConfig.logger.info("Backed up invalid favorite file to {}", favoriteFile);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to backup invalid favorite file to {}", favoriteFile, e);
}
NEIClientUtils.backupFile(favoriteFile);
save();
}
@@ -0,0 +1,188 @@
package codechicken.nei;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.function.Predicate;
import net.minecraft.client.gui.inventory.GuiContainer;
import codechicken.lib.vec.Rectangle4i;
final class GridOverlapScanner {
private static boolean scanRequested = true;
private static boolean scanFrame = true;
private static boolean wasShowTooltip = true;
private static WeakReference<GuiContainer> lastGui = new WeakReference<>(null);
private boolean invalidated = true;
private final Rectangle4i rect = new Rectangle4i();
private boolean[] checked = new boolean[0];
private int[] queue = new int[0];
private int tail;
private Predicate<Rectangle4i> isHidden;
private boolean[] hidden;
private int hiddenCount;
private int left;
private int top;
private int slotSize;
private int columns;
public static void requestScan() {
scanRequested = true;
}
public static void beginFrame(GuiContainer gui, boolean shouldShowTooltip) {
if (wasShowTooltip != shouldShowTooltip) {
scanRequested = true;
}
wasShowTooltip = shouldShowTooltip;
if (lastGui.get() != gui) {
lastGui = new WeakReference<>(gui);
scanRequested = true;
}
scanFrame = scanRequested;
scanRequested = false;
}
public static boolean isScanFrame() {
return scanFrame;
}
public void invalidate() {
this.invalidated = true;
}
public boolean shouldRefresh() {
final boolean refresh = this.invalidated || scanFrame;
this.invalidated = false;
return refresh;
}
public int scan(Predicate<Rectangle4i> isHidden, int left, int top, int slotSize, int rows, int columns,
boolean seedLeft, boolean seedRight, boolean[] hidden) {
final int size = rows * columns;
if (this.checked.length < size) {
this.checked = new boolean[size];
this.queue = new int[size];
} else {
Arrays.fill(this.checked, 0, size, false);
}
this.isHidden = isHidden;
this.hidden = hidden;
this.hiddenCount = 0;
this.tail = 0;
this.left = left;
this.top = top;
this.slotSize = slotSize;
this.columns = columns;
for (int r = 0; r < rows; r++) {
if (seedLeft) {
probe(r, 0);
}
if (seedRight) {
probe(r, columns - 1);
}
}
for (int head = 0; head < this.tail; head++) {
final int row = this.queue[head] / columns;
final int column = this.queue[head] % columns;
for (int r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) {
for (int c = Math.max(0, column - 1); c <= Math.min(columns - 1, column + 1); c++) {
probe(r, c);
}
}
}
for (int r = 0; r < rows; r++) {
for (int c = 0; c < columns; c++) {
if (this.checked[r * columns + c]) {
continue;
}
int c1 = c;
while (c1 + 1 < columns && !this.checked[r * columns + c1 + 1]) {
c1++;
}
int r1 = r;
while (r1 + 1 < rows && isUnchecked(r1 + 1, c, c1)) {
r1++;
}
scanArea(r, r1, c, c1);
c = c1;
}
}
this.isHidden = null;
this.hidden = null;
return this.hiddenCount;
}
private void probe(int row, int column) {
final int idx = row * this.columns + column;
if (!this.checked[idx]) {
this.checked[idx] = true;
if (test(row, row, column, column)) {
this.hidden[idx] = true;
this.queue[this.tail++] = idx;
this.hiddenCount++;
}
}
}
private void scanArea(int r0, int r1, int c0, int c1) {
if (!test(r0, r1, c0, c1)) {
for (int r = r0; r <= r1; r++) {
Arrays.fill(this.checked, r * this.columns + c0, r * this.columns + c1 + 1, true);
}
} else if (r0 == r1 && c0 == c1) {
this.checked[r0 * this.columns + c0] = true;
this.hidden[r0 * this.columns + c0] = true;
this.hiddenCount++;
} else if (r1 - r0 >= c1 - c0) {
final int mid = (r0 + r1) / 2;
scanArea(r0, mid, c0, c1);
scanArea(mid + 1, r1, c0, c1);
} else {
final int mid = (c0 + c1) / 2;
scanArea(r0, r1, c0, mid);
scanArea(r0, r1, mid + 1, c1);
}
}
private boolean isUnchecked(int row, int c0, int c1) {
for (int c = c0; c <= c1; c++) {
if (this.checked[row * this.columns + c]) {
return false;
}
}
return true;
}
private boolean test(int r0, int r1, int c0, int c1) {
this.rect.set(
this.left + c0 * this.slotSize,
this.top + r0 * this.slotSize,
(c1 - c0 + 1) * this.slotSize,
(r1 - r0 + 1) * this.slotSize);
return this.isHidden.test(this.rect);
}
}
+18 -22
View File
@@ -7,40 +7,36 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
public class GuiNEIButton extends GuiButton {
protected static final ResourceLocation guiTex = new ResourceLocation("textures/gui/widgets.png");
private static final DrawableResource[] BACKGROUNDS = new DrawableResource[3];
static {
for (int state = 0; state < BACKGROUNDS.length; state++) {
BACKGROUNDS[state] = new DrawableBuilder(guiTex, 0, 46 + state * 20, 200, 20).setSlices(2, 2, 2, 2).build();
}
}
public GuiNEIButton(int i, int j, int k, int l, int i1, String s) {
super(i, j, k, l, i1, s);
}
protected DrawableResource getBackground(int mouseX, int mouseY, boolean mouseOver) {
return BACKGROUNDS[getHoverState(mouseOver)];
}
public void drawButton(Minecraft minecraft, int x, int y) {
if (!visible) return;
minecraft.renderEngine.bindTexture(guiTex);
GL11.glColor4f(1, 1, 1, 1);
boolean mouseOver = x >= xPosition && y >= yPosition && x < xPosition + width && y < yPosition + height;
int k = getHoverState(mouseOver);
drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height / 2); // top left
drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height / 2); // top
// right
drawTexturedModalRect(
xPosition,
yPosition + height / 2,
0,
46 + k * 20 + 20 - height / 2,
width / 2,
height / 2); // bottom left
drawTexturedModalRect(
xPosition + width / 2,
yPosition + height / 2,
200 - width / 2,
46 + k * 20 + 20 - height / 2,
width / 2,
height / 2); // bottom right
mouseDragged(minecraft, x, y);
GL11.glColor4f(1, 1, 1, 1);
getBackground(x, y, mouseOver).draw(xPosition, yPosition, width, height);
mouseDragged(minecraft, x, y);
drawContent(minecraft, x, y, mouseOver);
}
@@ -55,7 +51,7 @@ public class GuiNEIButton extends GuiButton {
return ColorUtils.buttonLabelNormal.getColor();
}
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
protected void drawContent(Minecraft minecraft, int x, int y, boolean mouseOver) {
FontRenderer fontrenderer = minecraft.fontRenderer;
int color = getTextColour(mouseOver);
@@ -13,7 +13,6 @@ import java.util.stream.IntStream;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidContainerRegistry;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
@@ -24,6 +23,7 @@ import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.recipe.StackInfo;
@@ -137,7 +137,11 @@ public class ItemCraftablesPanel
@Override
public void execute() {
pendingResult.set(generateCraftables(ItemCraftablesPanel.this.pendingSnapshot));
final CraftablesSnapshot snapshot = ItemCraftablesPanel.this.pendingSnapshot;
if (snapshot != null) {
pendingResult.set(generateCraftables(snapshot));
}
}
};
@@ -298,6 +302,11 @@ public class ItemCraftablesPanel
}
}
public void clearGuiReferences() {
this.lastGuiContainer = null;
this.pendingSnapshot = null;
}
private GuiContainer getGuiContainer() {
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
return (firstGui instanceof GuiRecipe<?>gui) ? gui.firstGui : firstGui;
@@ -378,48 +387,19 @@ public class ItemCraftablesPanel
}
private static ItemStack getCheapResultItem(IRecipeHandler handler, int recipeIndex) {
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
if (resultStack != null) {
return resultStack.item;
}
final List<PositionedStack> otherStacks = handler.getOtherStacks(recipeIndex);
for (PositionedStack otherStack : otherStacks) {
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|| StackInfo.getFluid(otherStack.items[0]) != null) {
return otherStack.item;
}
}
return otherStacks.isEmpty() ? null : otherStacks.get(0).item;
final PositionedStack result = Recipe
.getMainResult(handler.getResultStacks(recipeIndex), pStack -> pStack.items[0]);
return result != null ? result.item : null;
}
private static ItemStack getCheapResult(IRecipeHandler handler, int recipeIndex) {
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
final List<PositionedStack> results = resultStack != null ? Collections.singletonList(resultStack)
: handler.getOtherStacks(recipeIndex);
final List<PositionedStack> results = handler.getResultStacks(recipeIndex);
if (results.isEmpty()) {
return null;
}
ItemStack stack = null;
if (results.size() > 1) {
for (PositionedStack result : results) {
if (!FluidContainerRegistry.isContainer(result.item) || StackInfo.getFluid(result.item) != null) {
stack = result.item;
break;
}
}
}
if (stack == null) {
stack = results.get(0).item;
}
final ItemStack stack = Recipe.getMainResult(results, pStack -> pStack.items[0]).item;
int stackSize = 0;
for (PositionedStack result : results) {
@@ -207,6 +207,33 @@ public class ItemList {
return filters;
}
public static ItemStack[] expandPermutations(ItemStack[] items) {
List<ItemStack> stacks = new ArrayList<>();
for (ItemStack item : items) {
if (item == null || item.getItem() == null) continue;
if (item.getItemDamage() == Short.MAX_VALUE) {
List<ItemStack> permutations = itemMap.get(item.getItem());
if (!permutations.isEmpty()) {
for (ItemStack stack : permutations) {
ItemStack toAdd = stack.copy();
toAdd.stackSize = item.stackSize;
stacks.add(toAdd);
}
} else {
ItemStack base = new ItemStack(item.getItem(), item.stackSize);
base.stackTagCompound = item.stackTagCompound;
stacks.add(base);
}
continue;
}
stacks.add(item.copy());
}
return stacks.toArray(new ItemStack[0]);
}
public static ForkJoinPool forkJoinPool;
static {
@@ -36,7 +36,6 @@ 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() {}
@@ -60,11 +59,10 @@ public class ItemListLoader {
} catch (TimeoutException t) {
throw t;
} catch (Throwable t) {
NEIServerUtils.logOnce(
t,
stackTraces,
NEIClientUtils.errorOnce(
"ItemListLoader: " + item.toString(),
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
item.toString());
t);
}
}
}
@@ -118,14 +118,7 @@ public class ItemSorter implements Comparator<ItemStack> {
return name1.compareTo(name2);
});
tag.getTag("inventory.itemsort").setDefaultValue(getSaveString(list));
API.addOption(new OptionOpenGui("inventory.itemsort", GuiItemSorter.class) {
@Override
public void useGlobals() {
super.useGlobals();
list = fromSaveString(activeTag().getValue());
}
});
API.addOption(new OptionOpenGui("inventory.itemsort", GuiItemSorter.class));
}
public static String getSaveString(List<SortEntry> list) {
+11 -1
View File
@@ -127,7 +127,12 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
this.displayName = "Unnamed";
}
this.stack = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack), 0);
// Copying the stack copies its whole NBT, so it is only done when the hovered stack changes
final boolean sameStack = this.stack != null && this.stack.isItemEqual(stack)
&& ItemStack.areItemStackTagsEqual(this.stack, stack);
if (!sameStack) {
this.stack = StackInfo.withAmount(stack, 0);
}
} else {
this.stack = null;
this.displayName = "";
@@ -219,6 +224,11 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isHashDown("itemzoom.toggle", NEIClientUtils.SHIFT_HASH)) {
NEIClientConfig.getSetting("inventory.itemzoom.enabled")
+26 -36
View File
@@ -282,6 +282,8 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
protected boolean[] invalidSlotMap;
private final GridOverlapScanner overlapScanner = new GridOverlapScanner();
protected ScreenCapture screenCapture = null;
public ArrayList<ItemStack> getItems() {
@@ -359,12 +361,13 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
width = newWidth;
height = newHeight;
columns = width / SLOT_SIZE;
rows = height / SLOT_SIZE;
this.columns = newWidth / SLOT_SIZE;
this.rows = newHeight / SLOT_SIZE;
paddingLeft = (width % SLOT_SIZE) / 2;
if (changed) {
this.overlapScanner.invalidate();
onGridChanged();
}
}
@@ -405,55 +408,42 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
}
public void refresh(GuiContainer gui) {
final int gridRenderingCacheMode = getGridRenderingCacheMode();
if (gridRenderingCacheMode <= 0 || screenCapture == null || screenCapture.needRefresh(gridRenderingCacheMode)) {
if (this.overlapScanner.shouldRefresh()) {
updateGuiOverlapSlots(gui);
}
shiftPage(0);
}
protected void updateGuiOverlapSlots(GuiContainer gui) {
final boolean[] oldSlotMap = invalidSlotMap;
final int oldRows = rows;
final int oldColumns = columns;
final boolean[] oldSlotMap = this.invalidSlotMap;
final int size = this.rows * this.columns;
final boolean[] slotMap = new boolean[size];
invalidSlotMap = new boolean[rows * columns];
perPage = columns * rows;
this.perPage = size;
if (gui != null) {
if (gui != null && size > 0) {
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
if (NEIClientConfig.optimizeGuiOverlapComputation()) {
checkGuiOverlap(gui, handlers, 0, columns - 2, 1);
checkGuiOverlap(gui, handlers, columns - 1, 1, -1);
} else {
checkGuiOverlap(gui, handlers, 0, columns, 1);
}
final Rectangle4i origin = getSlotRect(0, 0);
this.perPage -= this.overlapScanner.scan(
rect -> GuiInfo.hideItemPanelSlot(gui, rect, handlers),
origin.x,
origin.y,
SLOT_SIZE,
this.rows,
this.columns,
origin.x + this.columns * SLOT_SIZE > gui.guiLeft,
origin.x < gui.guiLeft + gui.xSize,
slotMap);
}
if (oldRows != rows || oldColumns != columns || !Arrays.equals(oldSlotMap, invalidSlotMap)) {
this.invalidSlotMap = slotMap;
if (!Arrays.equals(oldSlotMap, slotMap)) {
onGridChanged();
}
}
private void checkGuiOverlap(GuiContainer gui, INEIGuiHandler[] handlers, int start, int end, int dir) {
boolean validColumn = false;
for (int c = start; c != end && (!NEIClientConfig.optimizeGuiOverlapComputation() || !validColumn); c += dir) {
validColumn = true;
for (int r = 0; r < rows; r++) {
final int idx = columns * r + c;
if (idx >= 0 && idx < invalidSlotMap.length
&& !invalidSlotMap[idx]
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c), handlers)) {
invalidSlotMap[idx] = true;
validColumn = false;
perPage--;
}
}
}
}
public T getSlotMouseOver(int mousex, int mousey) {
if (!contains(mousex, mousey)) {
return null;
+46 -4
View File
@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.BooleanSupplier;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
@@ -29,6 +30,7 @@ public class KeyManager {
private static final Map<String, KeyBinding> keyBindings = new HashMap<>();
private static final Map<String, String> keyAliases = new HashMap<>();
private static volatile boolean pendingOptionsReload = false;
private static int clickedButton = -1;
static {
// Aliases for legacy keybinds
@@ -73,7 +75,8 @@ public class KeyManager {
final int keyCode = binding.getKeyCode();
if (keyCode < 0) {
return Mouse.isButtonDown(keyCode + 100);
final int button = keyCode + 100;
return Mouse.isButtonDown(button) || button == clickedButton;
} else if (keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE) {
return Keyboard.isKeyDown(keyCode);
}
@@ -82,6 +85,40 @@ public class KeyManager {
return false;
}
/**
* Whether any NEI key binding is assigned to the given mouse button.
*/
public static boolean isMouseBound(int button) {
for (KeyBinding binding : keyBindings.values()) {
if (binding.getKeyCode() == button - 100) {
return true;
}
}
return false;
}
/**
* Runs a hotkey handler for a click of the given mouse button if the button is bound to any NEI key binding. The
* button is reported as down by {@link #isKeyDown} for the duration of the call, because the LWJGL button state can
* already be released when a quick click is processed.
*
* @return true if the click was consumed by the handler
*/
public static boolean handleMouseHotkey(int button, BooleanSupplier handler) {
if (!isMouseBound(button)) {
return false;
}
final int previous = clickedButton;
clickedButton = button;
try {
return handler.getAsBoolean();
} finally {
clickedButton = previous;
}
}
public static boolean isHashDown(String ident) {
return isHashDown(ident, 0);
}
@@ -107,18 +144,23 @@ public class KeyManager {
public static String getKeyName(String ident, int meta) {
final int keyCode = getKeyCode(ident);
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode + meta);
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode, meta);
}
public static String getKeyName(String ident, int meta, int mouseBind) {
final int keyCode = getKeyCode(ident);
return keyCode == Keyboard.KEY_NONE && mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? null
: getKeyName(keyCode + meta, mouseBind);
: getKeyName(keyCode, meta, mouseBind);
}
public static String getKeyName(int keyBind, int mouseBind) {
final int keyCode = NEIKeyboardUtils.unhash(keyBind);
return getKeyName(keyCode, keyBind - keyCode, mouseBind);
}
private static String getKeyName(int keyCode, int hash, int mouseBind) {
StringJoiner keyText = new StringJoiner(" + ");
String keyHash = keyBind == Keyboard.KEY_NONE ? "" : NEIKeyboardUtils.getKeyName(keyBind);
String keyHash = keyCode == Keyboard.KEY_NONE && hash == 0 ? "" : NEIKeyboardUtils.getKeyName(keyCode, hash);
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
if (!keyHash.isEmpty()) {
@@ -220,6 +220,20 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) {
if (inputFocused == null) {
widget.lastKeyTyped(keyID, keyChar);
}
}
}
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isKeyDown("gui.hide")) {
toggleBooleanSetting("inventory.hidden");
return true;
@@ -234,8 +248,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
return true;
}
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) if (inputFocused == null) widget.lastKeyTyped(keyID, keyChar);
if (isEnabled() && !isHidden() && inputFocused == null) {
for (Widget widget : controlWidgets) {
if (widget.onHotkeyPressed()) {
return true;
}
}
}
return false;
@@ -370,6 +388,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
}
public static void layout(GuiContainer gui) {
GridOverlapScanner.beginFrame(gui, GuiContainerManager.shouldShowTooltip(gui));
VisiblityData visiblity = new VisiblityData();
visiblity.showNEI = !isHidden();
visiblity.showBookmarkPanel = !isBookmarkPanelHidden();
@@ -427,14 +447,14 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!rightclick) {
if (Keyboard.getEventKeyState() && getIsAccessibleControlEventKey()
&& NEIClientConfig.canChangeCheatMode()) {
NEIClientConfig.cycleSetting("inventory.cheatmode", 3);
NEIClientConfig.setCheatMode((NEIClientConfig.getCheatMode() + 1) % 3);
} else {
if (Keyboard.getEventKeyState() && (Keyboard.getEventKey() == Keyboard.KEY_LSHIFT
|| Keyboard.getEventKey() == Keyboard.KEY_RSHIFT)) {
GuiRecipeTab.loadHandlerInfo();
RecipeCatalysts.loadCatalystInfo();
}
getOptionList().openGui(getGuiContainer(), false);
getOptionList().openGui(getGuiContainer());
}
return true;
}
@@ -803,6 +823,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!isEnabled()) return;
GridOverlapScanner.requestScan();
AutoFocusWidget.instance.guiTick();
for (Widget widget : drawWidgets) widget.update();
@@ -61,6 +61,9 @@ public class NEICPH implements IClientPacketHandler {
case S2C.CACHE_BOOKMARK_DATA:
handleCacheBookmarkData(packet);
break;
case S2C.SYNC:
ServerSync.onReply(packet.readInt());
break;
}
}
@@ -9,13 +9,13 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.world.storage.SaveFormatComparator;
import net.minecraftforge.common.MinecraftForge;
@@ -37,7 +37,6 @@ import codechicken.lib.config.ConfigTag;
import codechicken.lib.config.ConfigTagParent;
import codechicken.nei.api.API;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.api.NEIInfo;
import codechicken.nei.config.ConfigSet;
@@ -45,7 +44,9 @@ import codechicken.nei.config.GuiHighlightTips;
import codechicken.nei.config.GuiNEIOptionList;
import codechicken.nei.config.GuiOptionList;
import codechicken.nei.config.GuiPanelSettings;
import codechicken.nei.config.OptionButton;
import codechicken.nei.config.OptionCycled;
import codechicken.nei.config.OptionExportConfig;
import codechicken.nei.config.OptionGamemodes;
import codechicken.nei.config.OptionIntegerField;
import codechicken.nei.config.OptionList;
@@ -56,7 +57,6 @@ import codechicken.nei.config.OptionToggleButtonBoubs;
import codechicken.nei.config.OptionUtilities;
import codechicken.nei.config.preset.GuiPresetList;
import codechicken.nei.event.NEIConfigsLoadedEvent;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeCatalysts;
@@ -69,6 +69,7 @@ public class NEIClientConfig {
private static boolean pluginNEIConfigLoaded;
private static boolean enabledOverride;
private static String worldPath;
private static final String CHEAT_MODE_KEY = "cheatmode";
public static Logger logger = LogManager.getLogger("NotEnoughItems");
public static File configDir = new File(CommonUtils.getMinecraftDir(), "config/NEI/");
@@ -157,6 +158,16 @@ public class NEIClientConfig {
.getIntValue(-1);
API.addOption(new OptionCycled("inventory.cheatmode", 3) {
@Override
public int value() {
return getCheatMode();
}
@Override
public void setValue(int value) {
setCheatMode(value);
}
@Override
public boolean optionValid(int index) {
return getLockedMode() == -1 || getLockedMode() == index && NEIInfo.isValidMode(index);
@@ -245,10 +256,6 @@ public class NEIClientConfig {
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.cycledIngredientsTooltip", true));
tag.getTag("inventory.guirecipe.creativeTabStyle").setComment("Creative or JEI style tabs")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.creativeTabStyle", true));
tag.getTag("inventory.guirecipe.itemPresenceOverlay").setComment("Item presence overlay on ?-hover")
.getIntValue(1);
API.addOption(new OptionCycled("inventory.guirecipe.itemPresenceOverlay", 3, true));
@@ -491,10 +498,6 @@ public class NEIClientConfig {
tag.getTag("inventory.showItemQuantityWidget").setComment("Show Item Quantity Widget").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.showItemQuantityWidget", true));
tag.getTag("inventory.optimize_gui_overlap_computation").setComment("Optimize computation for GUI overlap")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.optimize_gui_overlap_computation", true));
tag.getTag("inventory.autocrafting").setComment("Autocrafting").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.autocrafting", true));
@@ -516,29 +519,32 @@ public class NEIClientConfig {
});
tag.getTag("tools.handler_load_from_config").setComment("ADVANCED: Load handlers from config")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("tools.handler_load_from_config", true) {
API.addOption(
new OptionButton(
"tools.export_configs.all",
"tools.export_configs.all",
null,
"tools.export_configs.all.tip") {
@Override
public boolean onClick(int button) {
super.onClick(button);
GuiRecipeTab.loadHandlerInfo();
return true;
}
});
@Override
public String getButtonText() {
return translateN("tools.export_configs.button");
}
tag.getTag("tools.catalyst_load_from_config").setComment("ADVANCED: Load catalysts from config")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("tools.catalyst_load_from_config", true) {
@Override
public boolean onClick(int button) {
int exported = 0;
@Override
public boolean onClick(int button) {
super.onClick(button);
RecipeCatalysts.loadCatalystInfo();
return true;
}
});
for (String resource : ClientHandler.DEFAULT_CONFIGS) {
if (ClientHandler.exportDefaultConfig(resource)) exported++;
}
NEIClientUtils.printChatMessage(
new ChatComponentTranslation("nei.options.tools.export_configs.all.done", exported));
return true;
}
});
ClientHandler.DEFAULT_CONFIGS.forEach(fileName -> API.addOption(new OptionExportConfig(fileName)));
setDefaultKeyBindings();
}
@@ -547,23 +553,18 @@ public class NEIClientConfig {
OptionList.setOptionList(new OptionList("nei.options") {
@Override
public ConfigSet globalConfigSet() {
public ConfigSet configSet() {
return global;
}
@Override
public ConfigSet worldConfigSet() {
return world;
}
@Override
public OptionList configBase() {
return this;
}
@Override
public GuiOptionList getGui(GuiScreen parent, OptionList list, boolean world) {
return new GuiNEIOptionList(parent, list, world);
public GuiOptionList getGui(GuiScreen parent, OptionList list) {
return new GuiNEIOptionList(parent, list);
}
});
}
@@ -930,15 +931,15 @@ public class NEIClientConfig {
specificDir.mkdirs();
}
world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg")));
world = new ConfigSet(new File(specificDir, "NEI.dat"));
if (newWorld && Minecraft.getMinecraft().isSingleplayer() && getLockedMode() == -1) {
final int globalMode = global.config.getTag("inventory.cheatmode").getIntValue();
final int globalMode = getCheatMode();
final int worldMode = Math
.min(globalMode, NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
if (worldMode != globalMode) {
world.config.getTag("inventory.cheatmode").setIntValue(worldMode);
setCheatMode(worldMode);
}
}
@@ -952,9 +953,6 @@ public class NEIClientConfig {
}
private static void onWorldLoad() {
world.config.setComment(
"World based configuration of NEI.\nMost of these options can be changed ingame.\nDeleting any element will restore it to it's default value");
setWorldDefaults();
creativeInv = new ItemStack[54];
LayoutManager.searchField.setText(getSearchExpression());
@@ -1026,20 +1024,11 @@ public class NEIClientConfig {
@Override
public void run() {
final Stream<Class<?>> stream = NEIClientConfig.getBooleanSetting("loadPluginsInParallel")
? NEIClientConfig.pluginsList.parallelStream()
: NEIClientConfig.pluginsList.stream();
stream.forEach(clazz -> {
try {
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
config.loadConfig();
NEIModContainer.plugins.add(config);
logger.debug("Loaded {}", clazz.getName());
} catch (Throwable e) {
logger.error("Failed to Load {}", clazz.getName(), e);
}
});
PluginLoader.load(
NEIClientConfig.pluginsList,
NEIClientConfig.getBooleanSetting("loadPluginsInParallel"),
NEIModContainer.plugins,
logger);
KeyManager.requestOptionsReload();
RecipeCatalysts.loadCatalystInfo();
@@ -1069,14 +1058,8 @@ public class NEIClientConfig {
}
public static boolean isWorldSpecific(String setting) {
if (world == null) return false;
ConfigTag tag = world.config.getTag(setting, false);
return tag != null && tag.value != null;
}
public static ConfigTag getSetting(String s) {
return isWorldSpecific(s) ? world.config.getTag(s) : global.config.getTag(s);
return global.config.getTag(s);
}
public static boolean getBooleanSetting(String s) {
@@ -1165,10 +1148,6 @@ public class NEIClientConfig {
return getIntSetting("inventory.guirecipe.jeiStyleRecipeCatalyst");
}
public static boolean useCreativeTabStyle() {
return getBooleanSetting("inventory.guirecipe.creativeTabStyle");
}
public static boolean ignorePotionOverlap() {
return getBooleanSetting("inventory.bookmarks.ignorePotionOverlap");
}
@@ -1177,10 +1156,6 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.bookmarks.craftingTreeZoomWithoutModifier");
}
public static boolean optimizeGuiOverlapComputation() {
return getBooleanSetting("inventory.optimize_gui_overlap_computation");
}
public static boolean showCycledIngredientsTooltip() {
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
}
@@ -1201,14 +1176,6 @@ public class NEIClientConfig {
return mainNEIConfigLoaded && pluginNEIConfigLoaded;
}
public static boolean loadHandlersFromJar() {
return !getBooleanSetting("tools.handler_load_from_config");
}
public static boolean loadCatalystsFromJar() {
return !getBooleanSetting("tools.catalyst_load_from_config");
}
public static boolean isProfileRecipeEnabled() {
return NEIClientConfig.getBooleanSetting("inventory.guirecipe.profile");
}
@@ -1222,12 +1189,18 @@ public class NEIClientConfig {
}
public static int getCheatMode() {
if (world != null && world.nbt.hasKey(CHEAT_MODE_KEY)) return world.nbt.getInteger(CHEAT_MODE_KEY);
return getIntSetting("inventory.cheatmode");
}
public static void setCheatMode(int mode) {
if (world != null) world.nbt.setInteger(CHEAT_MODE_KEY, mode);
else setIntSetting("inventory.cheatmode", mode);
}
public static boolean canChangeCheatMode() {
if (getLockedMode() != -1) {
setIntSetting("inventory.cheatmode", getLockedMode());
setCheatMode(getLockedMode());
return false;
}
@@ -9,9 +9,12 @@ import static codechicken.nei.NEIClientConfig.invCreativeMode;
import static codechicken.nei.NEIClientConfig.world;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
@@ -26,6 +29,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
@@ -64,6 +68,8 @@ import codechicken.nei.util.NEIKeyboardUtils;
public class NEIClientUtils extends NEIServerUtils {
private static final Set<String> REPORTED = ConcurrentHashMap.newKeySet();
public static class Alignment {
public final int x, y;
@@ -639,4 +645,29 @@ public class NEIClientUtils extends NEIServerUtils {
if (!isAlphaTest) GL11.glDisable(GL11.GL_ALPHA_TEST);
}
public static void errorOnce(String key, String message, Throwable error) {
if (REPORTED.add(key)) {
NEIClientConfig.logger.error(message, error);
}
}
public static void warnOnce(String key, String message) {
if (REPORTED.add(key)) {
NEIClientConfig.logger.warn(message);
}
}
public static boolean backupFile(File file) {
final File backup = new File(file.getAbsolutePath() + ".backup-" + System.currentTimeMillis());
try {
Files.copy(file.toPath(), backup.toPath());
NEIClientConfig.logger.info("Backed up file {} to {}", file, backup);
return true;
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to backup file {} to {}", file, backup, e);
return false;
}
}
}
@@ -92,9 +92,17 @@ public class NEISPH implements IServerPacketHandler {
case C2S.SEND_CHAT_ITEM_LINK:
handleSendChatItemLink(sender, packet);
break;
case C2S.SYNC:
handleSync(sender, packet.readInt());
break;
}
}
private void handleSync(EntityPlayerMP sender, int id) {
sender.sendContainerAndContentsToPlayer(sender.openContainer, sender.openContainer.getInventory());
new PacketCustom(channel, S2C.SYNC).writeInt(id).sendToPlayer(sender);
}
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
NEIServerUtils.sendChatItemLink(sender, payloadTag);
@@ -4,12 +4,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.zip.ZipException;
@@ -509,20 +506,6 @@ public class NEIServerUtils {
return sb;
}
public static void logOnce(Throwable t, Set<String> stackTraces, String message) {
logOnce(t, stackTraces, message, "");
}
public static void logOnce(Throwable t, Set<String> stackTraces, String message, String identifier) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
String stackTrace = identifier + sw.toString();
if (!stackTraces.contains(stackTrace)) {
NEIServerConfig.logger.error(message, t);
stackTraces.add(stackTrace);
}
}
public static ItemStack getModdedItem(String itemId, String nbtString) {
int meta = -1;
ItemStack itemStack;
@@ -24,6 +24,7 @@ public final class PacketIDs {
public static final int REQUEST_POTION_GUI = 24;
public static final int SET_DUMMY_SLOT = 25;
public static final int SEND_CHAT_ITEM_LINK = 26;
public static final int SYNC = 27;
}
public static final class S2C {
@@ -38,5 +39,6 @@ public final class PacketIDs {
public static final int SET_CREATIVE_PLUS_MODE = 23;
public static final int OPEN_POTION_GUI = 24;
public static final int CACHE_BOOKMARK_DATA = 25;
public static final int SYNC = 26;
}
}
+32 -21
View File
@@ -29,6 +29,9 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
protected T grid;
private int headerPaddingLeft;
private int headerPaddingRight;
public ArrayList<ItemStack> getItems() {
return this.grid.getItems();
}
@@ -105,26 +108,15 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
protected int resizeHeader(GuiContainer gui) {
final int BUTTON_SIZE = 16;
int paddingLeft = 0;
int paddingRight = 0;
while (paddingLeft < w && GuiInfo.hideItemPanelSlot(
gui,
new Rectangle4i(x + paddingLeft, y, ItemsGrid.SLOT_SIZE, ItemsGrid.SLOT_SIZE))) {
paddingLeft += ItemsGrid.SLOT_SIZE;
if (GridOverlapScanner.isScanFrame()) {
updateHeaderPadding(gui);
}
while (paddingRight < w && GuiInfo.hideItemPanelSlot(
gui,
new Rectangle4i(
x + w - paddingRight - ItemsGrid.SLOT_SIZE,
y,
ItemsGrid.SLOT_SIZE,
ItemsGrid.SLOT_SIZE))) {
paddingRight += ItemsGrid.SLOT_SIZE;
}
final int paddingLeft = this.headerPaddingLeft;
final int paddingRight = this.headerPaddingRight;
if (paddingLeft + paddingRight >= w) {
if (paddingLeft + paddingRight >= this.w) {
return 0;
}
@@ -132,16 +124,35 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
pagePrev.h = pageNext.h = BUTTON_SIZE;
pagePrev.y = pageNext.y = y;
pagePrev.x = x + paddingLeft;
pageNext.x = x + w - pageNext.w - paddingRight;
pagePrev.x = this.x + paddingLeft;
pageNext.x = this.x + this.w - pageNext.w - paddingRight;
pageLabel.x = x + paddingLeft + (w - paddingLeft - paddingRight) / 2;
pageLabel.y = y + 5;
pageLabel.x = this.x + paddingLeft + (this.w - paddingLeft - paddingRight) / 2;
pageLabel.y = this.y + 5;
pageLabel.text = getLabelText();
return BUTTON_SIZE + 2;
}
private void updateHeaderPadding(GuiContainer gui) {
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
final Rectangle4i slot = new Rectangle4i(this.x, this.y, ItemsGrid.SLOT_SIZE, ItemsGrid.SLOT_SIZE);
this.headerPaddingLeft = 0;
this.headerPaddingRight = 0;
while (this.headerPaddingLeft < this.w && GuiInfo.hideItemPanelSlot(gui, slot, handlers)) {
this.headerPaddingLeft += ItemsGrid.SLOT_SIZE;
slot.x += ItemsGrid.SLOT_SIZE;
}
slot.x = this.x + this.w - ItemsGrid.SLOT_SIZE;
while (this.headerPaddingRight < this.w && GuiInfo.hideItemPanelSlot(gui, slot, handlers)) {
this.headerPaddingRight += ItemsGrid.SLOT_SIZE;
slot.x -= ItemsGrid.SLOT_SIZE;
}
}
protected abstract int resizeFooter(GuiContainer gui);
public void setVisible() {
@@ -305,7 +316,7 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
}
@Override
public boolean handleKeyPress(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
final Point mouse = GuiDraw.getMousePosition();
if (!contains(mouse.x, mouse.y)) return false;
@@ -0,0 +1,66 @@
package codechicken.nei;
import java.util.Collection;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import org.apache.logging.log4j.Logger;
import codechicken.nei.api.IConfigureNEI;
final class PluginLoader {
private PluginLoader() {}
static void load(Collection<Class<?>> plugins, boolean parallel, Collection<IConfigureNEI> loadedPlugins,
Logger logger) {
if (parallel && canSetContextClassLoader()) {
// Common pool workers use the system loader, which cannot see FML-only classes (old Log4j then throws
// while logging a plugin's stack frames), and under a SecurityManager they forbid changing it.
final ClassLoader classLoader = IConfigureNEI.class.getClassLoader();
final ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), owner -> {
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(owner);
worker.setContextClassLoader(classLoader);
return worker;
}, null, false);
try {
pool.submit(() -> plugins.parallelStream().forEach(clazz -> load(clazz, loadedPlugins, logger))).join();
} finally {
pool.shutdown();
}
} else {
if (parallel) {
logger.warn("Context class loader cannot be changed, loading plugins sequentially");
}
plugins.forEach(clazz -> load(clazz, loadedPlugins, logger));
}
}
private static void load(Class<?> clazz, Collection<IConfigureNEI> loadedPlugins, Logger logger) {
try {
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
config.loadConfig();
loadedPlugins.add(config);
logger.debug("Loaded {}", clazz.getName());
} catch (Throwable e) {
logger.error("Failed to Load {}", clazz.getName(), e);
}
}
private static boolean canSetContextClassLoader() {
final SecurityManager securityManager = System.getSecurityManager();
if (securityManager == null) {
return true;
}
try {
securityManager.checkPermission(new RuntimePermission("setContextClassLoader"));
return true;
} catch (SecurityException e) {
return false;
}
}
}
@@ -23,6 +23,11 @@ public class PopupInputHandler implements IContainerInputHandler {
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isHashDown("gui.enchant") && canPerformAction("enchant")) {
NEICPH.sendOpenEnchantmentWindow();
@@ -14,12 +14,8 @@ import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.oredict.OreDictionary;
import org.lwjgl.opengl.GL11;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.item.FluidDrawer;
import codechicken.nei.item.FluidDrawer.FillDirection;
@@ -70,31 +66,11 @@ public class PositionedStack implements Cloneable {
public void generatePermutations() {
if (permutated) return;
List<ItemStack> stacks = new ArrayList<>();
for (ItemStack item : items) {
if (item == null || item.getItem() == null) continue;
items = ItemList.expandPermutations(items);
if (item.getItemDamage() == Short.MAX_VALUE) {
List<ItemStack> permutations = ItemList.itemMap.get(item.getItem());
if (!permutations.isEmpty()) {
for (ItemStack stack : permutations) {
ItemStack toAdd = stack.copy();
toAdd.stackSize = item.stackSize;
stacks.add(toAdd);
}
} else {
ItemStack base = new ItemStack(item.getItem(), item.stackSize);
base.stackTagCompound = item.stackTagCompound;
stacks.add(base);
}
continue;
}
stacks.add(item.copy());
if (items.length == 0) {
items = new ItemStack[] { new ItemStack(Blocks.fire) };
}
items = stacks.toArray(new ItemStack[0]);
if (items.length == 0) items = new ItemStack[] { new ItemStack(Blocks.fire) };
permutated = true;
setPermutationToRender(0);
@@ -263,7 +239,6 @@ public class PositionedStack implements Cloneable {
protected int capacity = 0;
protected boolean flowingTexture = false;
protected FillDirection fillDirection = FillDirection.AUTO;
protected DrawableResource overlay;
private ItemStack cachedFluidItem;
private FluidStack cachedFluidStack;
@@ -310,19 +285,6 @@ public class PositionedStack implements Cloneable {
return this;
}
public DrawableResource getOverlay() {
return this.overlay;
}
public Tank setOverlay(String resourceLocation, int u, int v, int width, int height) {
return setOverlay(new DrawableBuilder(resourceLocation, u, v, width, height).build());
}
public Tank setOverlay(DrawableResource overlay) {
this.overlay = overlay;
return this;
}
protected FluidStack getFluidStack() {
if (this.item != this.cachedFluidItem) {
@@ -339,7 +301,7 @@ public class PositionedStack implements Cloneable {
final List<String> customTooltip = super.getTooltip();
final FluidStack fluidStack = getFluidStack();
if (fluidStack != null) {
if (fluidStack != null && this.capacity > 0) {
tooltip.add(
EnumChatFormatting.GRAY + NEIClientUtils
.translate("recipe.fluid.tank.amount", NEIClientUtils.formatFluid(fluidStack.amount)));
@@ -370,15 +332,6 @@ public class PositionedStack implements Cloneable {
fluidStack,
this.flowingTexture,
this.fillDirection);
if (this.overlay != null) {
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_LIGHTING);
this.overlay.draw(this.relx, this.rely);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_BLEND);
}
}
}
}
@@ -89,19 +89,19 @@ public class PresetsList {
return false;
}
final PositionedStack result = handler.getResultStack(recipeIndex);
final List<PositionedStack> outputs = handler.getResultStacks(recipeIndex);
if (result != null && matchPositionedStack(result)) {
if (matchPositionedStack(outputs, true)) {
return true;
}
final List<PositionedStack> others = handler.getOtherStacks(recipeIndex);
final List<PositionedStack> auxiliaries = handler.getAuxiliaryStacks(recipeIndex);
if (!others.isEmpty() && matchPositionedStack(others, true)) {
if (matchPositionedStack(auxiliaries, true)) {
return true;
}
return result == null && others.isEmpty();
return outputs.isEmpty() && auxiliaries.isEmpty();
}
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
@@ -24,7 +24,13 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
field.setMaxStringLength(maxSearchLength);
field.setCursorPositionZero();
field.setSkipRecipeTokens(true);
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
field.setFormatter(new SearchTextFormatter(SearchField.searchParser) {
@Override
protected String getContextToken() {
return this.searchParser.getRecipeContextTokens();
}
});
this.field = field;
}
@@ -48,15 +54,19 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -227,18 +227,24 @@ public class SearchField extends TextField implements ItemFilterProvider {
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (isVisible() && KeyManager.isHashDown("gui.search")) {
setFocus(true);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -20,6 +20,10 @@ public class SearchTextFormatter implements TextFormatter {
this.contextToken = contextToken;
}
protected String getContextToken() {
return this.contextToken;
}
@Override
public String format(String text) {
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
@@ -32,13 +36,14 @@ public class SearchTextFormatter implements TextFormatter {
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
} else {
final StringBuilder formattedText = new StringBuilder();
final String contextToken = getContextToken();
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
final String filterText = orQuery[1];
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) {
for (String[] contextPart : searchParser.splitByDelimiters(filterText, contextToken, false)) {
if (!contextPart[0].isEmpty()) {
formattedText.append(
@@ -74,7 +79,11 @@ public class SearchTextFormatter implements TextFormatter {
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
if (!token.rawText.isEmpty()) {
if (patternMode == 1 && !token.quotes && isRegexToken(token.rawText)) {
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.REGEX + token.rawText.substring(0, 2))
.append(tokenColor + token.rawText.substring(2, token.rawText.length() - 1))
.append(SearchExpressionUtils.HIGHLIGHTS.REGEX + "/");
} else if (!token.rawText.isEmpty()) {
formattedText.append(tokenColor + token.rawText);
}
@@ -93,4 +102,8 @@ public class SearchTextFormatter implements TextFormatter {
return formattedText.toString();
}
}
private static boolean isRegexToken(String search) {
return search.length() >= 3 && search.regionMatches(true, 0, "r/", 0, 2) && search.endsWith("/");
}
}
@@ -194,6 +194,7 @@ public class SearchTokenParser {
if (patternMode != 3) {
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
final List<ItemFilter> searchTokens = new ArrayList<>();
final String contextTokens = skipRecipeTokens ? getRecipeContextTokens() : "";
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
@@ -203,9 +204,10 @@ public class SearchTokenParser {
final List<ItemFilter> tokens = new ArrayList<>();
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
for (String[] contextQuery : splitByDelimiters(subQuery[1], contextTokens, false)) {
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")
|| contextQuery[0].startsWith("^")) {
continue;
}
@@ -277,7 +279,7 @@ public class SearchTokenParser {
final List<IRecipeFilter> contextList = new ArrayList<>();
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], getRecipeContextTokens(), false)) {
if (contextQuery[1].isEmpty()) {
continue;
@@ -436,7 +438,9 @@ public class SearchTokenParser {
final List<String[]> tokens = new ArrayList<>();
final int length = input.length();
final boolean regexTokens = NEIClientConfig.getIntSetting("inventory.search.patternMode") == 1;
boolean insideQuotes = false;
boolean insideRegex = false;
String token = "";
int lastEnd = 0;
int index = 0;
@@ -444,6 +448,15 @@ public class SearchTokenParser {
while (index < length) {
final char ch = input.charAt(index);
if (insideRegex) {
if (ch == '/' && input.charAt(index - 1) != '\\') {
insideRegex = false;
}
index++;
continue;
}
if (!insideQuotes && delimiters.indexOf(ch) >= 0
&& (!space || index == 0 || input.charAt(index - 1) == ' ' || lastEnd == index)) {
@@ -464,6 +477,9 @@ public class SearchTokenParser {
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
insideQuotes = !insideQuotes;
} else if (regexTokens && !insideQuotes && isRegexStart(input, index)) {
insideRegex = true;
index++;
}
index++;
@@ -474,6 +490,12 @@ public class SearchTokenParser {
return tokens;
}
private static boolean isRegexStart(String input, int index) {
return Character.toLowerCase(input.charAt(index)) == 'r' && index + 1 < input.length()
&& input.charAt(index + 1) == '/'
&& (index == 0 || !Character.isLetterOrDigit(input.charAt(index - 1)));
}
public String getPrefixes() {
if (this.prefixes == null) {
final StringBuilder builder = new StringBuilder();
@@ -490,6 +512,11 @@ public class SearchTokenParser {
return this.prefixes;
}
public String getRecipeContextTokens() {
// '^' is the start anchor and the negated class in regex mode
return NEIClientConfig.getIntSetting("inventory.search.patternMode") == 2 ? "<>" : "<>^";
}
public char getRedefinedPrefix(char prefix) {
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
}
@@ -0,0 +1,36 @@
package codechicken.nei;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.PacketIDs.C2S;
/**
* Waits until the server has processed everything the client sent before. The server handles slot clicks and NEI
* packets in the order they came and answers after it has sent the slot updates, so once the answer is here the client
* container holds what the server computed.
*/
public final class ServerSync {
private static int lastId;
private static volatile int awaiting;
private ServerSync() {}
public static void request() {
if (!NEIClientConfig.hasSMPCounterPart()) {
awaiting = 0;
return;
}
awaiting = ++lastId;
new PacketCustom(NEICPH.channel, C2S.SYNC).writeInt(awaiting).sendToServer();
}
public static boolean pending() {
return awaiting != 0;
}
static void onReply(int id) {
if (id == awaiting) awaiting = 0;
}
}
@@ -29,6 +29,10 @@ public abstract class Widget {
public void lastKeyTyped(int keyID, char keyChar) {}
public boolean onHotkeyPressed() {
return false;
}
public boolean handleClickExt(int mx, int my, int button) {
return false;
}
@@ -51,6 +55,10 @@ public abstract class Widget {
return bounds().contains(px, py);
}
public boolean isVisible() {
return true;
}
public ItemStack getStackMouseOver(int mx, int my) {
return null;
}
@@ -56,7 +56,7 @@ public class WidgetContainer extends Widget {
final Rectangle4i bounds = bounds();
for (Widget widget : this.widgets) {
if (widget.bounds().intersects(bounds)) {
if (widget.isVisible() && widget.bounds().intersects(bounds)) {
widget.draw(mousex, mousey);
}
}
@@ -80,7 +80,7 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.contains(mousex, mousey)) {
if (widget.isVisible() && widget.contains(mousex, mousey)) {
return widget;
}
}
@@ -113,7 +113,8 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (!widget.contains(mousex, mousey) && widget.handleClickExt(mousex, mousey, button)) {
if (widget.isVisible() && !widget.contains(mousex, mousey)
&& widget.handleClickExt(mousex, mousey, button)) {
return true;
}
}
@@ -126,7 +127,7 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.mouseDragged(mousex, mousey, button, heldTime);
if (widget.isVisible()) widget.mouseDragged(mousex, mousey, button, heldTime);
}
}
@@ -135,7 +136,7 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.mouseUp(mousex, mousey, button);
if (widget.isVisible()) widget.mouseUp(mousex, mousey, button);
}
}
@@ -144,7 +145,7 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.handleKeyPress(keyCode, keyChar)) {
if (widget.isVisible() && widget.handleKeyPress(keyCode, keyChar)) {
return true;
}
}
@@ -157,11 +158,24 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.lastKeyTyped(keyID, keyChar);
if (widget.isVisible()) widget.lastKeyTyped(keyID, keyChar);
}
}
@Override
public boolean onHotkeyPressed() {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.onHotkeyPressed()) {
return true;
}
}
return false;
}
@Override
public boolean isFocused() {
return this.isFocused;
@@ -214,7 +228,7 @@ public class WidgetContainer extends Widget {
public void postDraw(int mousex, int mousey) {
for (Widget widget : this.widgets) {
widget.postDraw(mousex, mousey);
if (widget.isVisible()) widget.postDraw(mousex, mousey);
}
}
@@ -224,7 +238,7 @@ public class WidgetContainer extends Widget {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.onGuiClick(mousex, mousey);
if (widget.isVisible()) widget.onGuiClick(mousex, mousey);
}
}
@@ -240,6 +254,17 @@ public class WidgetContainer extends Widget {
return tooltip;
}
@Override
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> tooltip) {
final Widget widget = getWidgetUnderMouse(mousex, mousey);
if (widget != null) {
return widget.handleItemTooltip(itemstack, mousex, mousey, tooltip);
}
return tooltip;
}
@Override
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
final Widget widget = getWidgetUnderMouse(mousex, mousey);
@@ -19,6 +19,7 @@ import org.lwjgl.input.Keyboard;
import com.google.common.collect.Sets;
import codechicken.nei.BookmarkContainerInfo;
import codechicken.nei.CollapsibleItems;
import codechicken.nei.ItemList;
import codechicken.nei.ItemSorter;
import codechicken.nei.ItemStackSet;
@@ -341,6 +342,30 @@ public class API {
SubsetWidget.addTag(tag);
}
/**
* Registers a group of items that is collapsed into a single entry in the item panel. Groups registered here have
* the lowest priority: an item that matches a preset or a collapsibleitems.cfg group stays there. They are active
* only while the Collapsible Items option is enabled. Must be called while plugins are loading.
*
* @param filter A search string for matching items that belong to this group. Also identifies the group when its
* expanded state is saved, so changing it resets the saved state.
* @return A handle to optionally set the name shown for the group
*/
public static CollapsibleItems.ApiGroup addCollapsibleGroup(String filter) {
return CollapsibleItems.registerGroup(filter);
}
/**
* Same as {@link #addCollapsibleGroup(String)}, but the group is given as a list of item stacks. The stacks
* identify the group when its expanded state is saved, so changing them resets the saved state.
*
* @param items Item stacks that belong to this group
* @return A handle to optionally set the name shown for the group
*/
public static CollapsibleItems.ApiGroup addCollapsibleGroup(Iterable<ItemStack> items) {
return CollapsibleItems.registerGroup(items);
}
@Deprecated
public static void addSearchProvider(ISearchProvider provider) {}
@@ -59,6 +59,7 @@ import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.config.ArrayDumper;
import codechicken.nei.config.HandlerDumper;
import codechicken.nei.config.ItemPanelDumper;
import codechicken.nei.config.RecipeDumper;
import codechicken.nei.config.RegistryDumper;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.item.FluidDisplayRenderer;
@@ -284,6 +285,7 @@ public class ItemInfo {
});
API.addOption(new ItemPanelDumper("tools.dump.itempanel"));
API.addOption(new HandlerDumper("tools.dump.handlers"));
API.addOption(new RecipeDumper("tools.dump.recipes"));
}
@SuppressWarnings("unchecked")
@@ -333,12 +335,6 @@ public class ItemInfo {
}
private static void addVanillaBlockProperties() {
API.setOverrideName(new ItemStack(Blocks.flowing_water), "Water Source");
API.setOverrideName(new ItemStack(Blocks.water), "Water Still");
API.setOverrideName(new ItemStack(Blocks.flowing_lava), "Lava Source");
API.setOverrideName(new ItemStack(Blocks.lava), "Lava Still");
API.setOverrideName(new ItemStack(Blocks.end_portal), "End Portal");
API.setOverrideName(new ItemStack(Blocks.end_portal_frame), "End Portal Frame");
API.hideItem(new ItemStack(Blocks.double_stone_slab, 1, OreDictionary.WILDCARD_VALUE));
API.hideItem(new ItemStack(Blocks.double_wooden_slab, 1, OreDictionary.WILDCARD_VALUE));
API.hideItem(new ItemStack(Blocks.carrots));
@@ -5,7 +5,6 @@ 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 {
@@ -14,11 +13,7 @@ public class NEIInfo {
public static void load(World world) {
OptionCycled modeOption = (OptionCycled) NEIClientConfig.getOptionList().getOption("inventory.cheatmode");
final boolean worldConfig = NEIClientConfig.isWorldSpecific("inventory.cheatmode");
// kept alive so the WeakReference in Option.slotRef isn't GC'd before copyGlobals/cycle use it
GuiOptionList env = modeOption.parent.synthesizeEnvironment(worldConfig);
if (!modeOption.optionValid(modeOption.value())) {
modeOption.copyGlobals();
modeOption.cycle();
}
}
@@ -343,7 +343,6 @@ public abstract class ShortcutInputHandler {
if (handlerRef != null) {
final GuiFavoriteButton button = new GuiFavoriteButton(handlerRef, 0, 0);
button.saveRecipeInBookmark();
return true;
}
return true;
@@ -452,8 +452,9 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (!this.isEmpty() && this.gridGenerator.maxAbsoluteSlotIndex > 0) {
final GroupingItem groupingItem = ItemPanels.bookmarkPanel.groupingItem;
final int sortableGroupId = ItemPanels.bookmarkPanel.sortableGroup != null
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
&& ItemPanels.bookmarkPanel.sortableGroup.grid == this
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
final int rowShift = this.page * this.rows;
Map<Integer, Integer> groups = this.gridGenerator.rowToGroupId;
@@ -795,8 +796,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
this.bookmarkItems.add(item);
}
sortableGroup.grid = this;
sortableGroup.groupId = groupId;
sortableGroup.update(this, groupId);
this.onItemsChanged();
this.getMask();// force generate grid
overGroupId = getRowGroupId(overRowIndex);
@@ -1285,7 +1285,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarksGridSlot slot : getMask()) {
Rectangle4i rect = getSlotRect(slot.slotIndex);
if (sortableGroup != null && slot.getGroupId() == sortableGroup.groupId) {
if (sortableGroup != null && sortableGroup.matches(this, slot.getGroupId())) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
} else if (sortableItem != null && sortableItem.items.contains(slot.itemIndex)) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
@@ -1314,7 +1314,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
slot.afterDraw(getSlotRect(slot.slotIndex), mouseContext);
}
@@ -1331,7 +1331,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
final BookmarkItem item = slot.getBookmarkItem();
@@ -7,7 +7,6 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemSorter;
@@ -132,7 +131,7 @@ public class BookmarkGridGenerator {
itemIndex++;
}
} else if (group.crafting != null && group.crafting.itemToRecipe.values().contains(bookmarkItem.recipeId)) {
} else if (group.crafting != null && group.crafting.recipeToItems.containsKey(bookmarkItem.recipeId)) {
absoluteSlotIndex = generateCollapsedRecipe(prevBookmarkItem, itemIndex, absoluteSlotIndex, borderGrid);
prevBookmarkItem = bookmarkItem;
@@ -218,8 +217,7 @@ public class BookmarkGridGenerator {
}
private List<Integer> getShadows(BookmarkGroup group, RecipeId recipeId) {
return group.crafting.itemToRecipe.entrySet().stream().filter(entry -> recipeId.equals(entry.getValue()))
.map(entry -> entry.getKey()).collect(Collectors.toList());
return new ArrayList<>(group.crafting.recipeToItems.getOrDefault(recipeId, Collections.emptyList()));
}
private int nextSlotIndex(int index, BookmarkItem previousMeta, BookmarkItem meta, BookmarkViewMode viewMode) {
@@ -340,7 +338,8 @@ public class BookmarkGridGenerator {
0,
chainItem.getItem(),
null,
group);
group,
this.grid);
} else {
return new BookmarksGridSlot(
slotIndex,
@@ -350,7 +349,8 @@ public class BookmarkGridGenerator {
chainItem.getCalculatedMultiplier(),
chainItem.getItem(),
chainItem.calculatedType,
group);
group,
this.grid);
}
}
@@ -14,7 +14,6 @@ import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.io.IOUtils;
import com.google.common.io.Files;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -23,7 +22,9 @@ import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.util.NBTJson;
@@ -79,10 +80,21 @@ public class BookmarkStorage {
public int getNamespaceSize() {
if (this.namespaces.get(this.namespaces.size() - 1).size() > 0) {
this.namespaces.add(new BookmarkGrid());
} else if (this.activeNamespaceIndex == this.namespaces.size() - 2 && getActiveGrid().size() == 0) {
this.namespaces.remove(this.namespaces.size() - 1);
final int lastIndex = this.namespaces.size() - 1;
final BookmarkGrid lastGrid = this.namespaces.get(lastIndex);
final SortableGroup sortableGroup = ItemPanels.bookmarkPanel.sortableGroup;
if (lastGrid.size() > 0) {
if (sortableGroup == null || sortableGroup.grid != lastGrid || !sortableGroup.aloneInGrid) {
this.namespaces.add(new BookmarkGrid());
}
} else if (lastIndex > 0 && this.activeNamespaceIndex != lastIndex) {
final BookmarkGrid prevGrid = this.namespaces.get(lastIndex - 1);
if (sortableGroup != null && sortableGroup.grid == prevGrid && sortableGroup.aloneInGrid
|| prevGrid == getActiveGrid() && prevGrid.size() == 0) {
this.namespaces.remove(lastIndex);
}
}
return this.namespaces.size();
@@ -218,14 +230,7 @@ public class BookmarkStorage {
}
if (wasError) {
try {
Files.copy(
bookmarkFile,
new File(bookmarkFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
NEIClientConfig.logger.info("Backed up invalid bookmark file to {}", bookmarkFile);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to backup invalid bookmark file to {}", bookmarkFile, e);
}
NEIClientUtils.backupFile(bookmarkFile);
}
for (BookmarkGrid gr : namespaces) {
@@ -8,7 +8,6 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.NEIClientConfig;
@@ -65,7 +64,8 @@ public class BookmarksGridSlot extends ItemsGridSlot {
protected final int rowIndex;
public BookmarksGridSlot(int slotIndex, int itemIndex, long realMultiplier, long shiftAmount,
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) {
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group,
BookmarkGrid grid) {
super(
slotIndex,
itemIndex,
@@ -96,7 +96,6 @@ public class BookmarksGridSlot extends ItemsGridSlot {
: CalculatedType.RESULT;
}
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
final BookmarkItem prevItem = grid.getCalculatedItem(
grid.gridGenerator.slotToItem.getOrDefault(grid.gridGenerator.itemToSlot.get(this.itemIndex) - 1, -1));
this.isFirstOutput = this.itemIndex >= 0 && this.bookmarkItem.type == BookmarkItemType.RESULT
@@ -111,6 +111,7 @@ public class RecipeChainDetails {
public final Map<Integer, BookmarkChainItem> calculatedItems = new HashMap<>();
public final Map<Integer, RecipeId> itemToRecipe = new HashMap<>();
public final Map<RecipeId, List<Integer>> recipeToItems = new HashMap<>();
public final Set<RecipeId> outputRecipes = new HashSet<>();
public final Set<RecipeId> recipeInMiddle = new HashSet<>();
@@ -124,6 +125,7 @@ public class RecipeChainDetails {
}
this.itemToRecipe.clear();
this.recipeToItems.clear();
this.outputRecipes.clear();
this.recipeInMiddle.clear();
this.calculatedItems.clear();
@@ -180,6 +182,10 @@ public class RecipeChainDetails {
}
for (Map.Entry<Integer, RecipeId> entry : this.itemToRecipe.entrySet()) {
this.recipeToItems.computeIfAbsent(entry.getValue(), recipeId -> new ArrayList<>()).add(entry.getKey());
}
for (Map.Entry<Integer, BookmarkItem> entry : chainItems.entrySet()) {
final int itemIndex = entry.getKey();
if (itemIndex >= 0 && this.calculatedItems.containsKey(itemIndex)) {
@@ -13,13 +13,23 @@ public class SortableGroup {
public int groupId;
public BookmarkGrid grid;
public boolean aloneInGrid;
public int shiftX = 0;
public int shiftY = 0;
public SortableGroup(BookmarkGrid grid, int groupId) {
update(grid, groupId);
}
public void update(BookmarkGrid grid, int groupId) {
this.grid = grid;
this.groupId = groupId;
this.aloneInGrid = grid.bookmarkItems.stream().allMatch(item -> item.groupId == groupId);
}
public boolean matches(BookmarkGrid grid, int groupId) {
return this.grid == grid && this.groupId == groupId;
}
public void postDraw(int mousex, int mousey) {
@@ -37,8 +37,8 @@ public class SortableItem {
&& (group.viewMode == BookmarkViewMode.TODO_LIST || group.crafting != null);
this.crossGroup = !group.collapsed && group.viewMode == BookmarkViewMode.TODO_LIST
&& bookmarkItem.type != BookmarkItemType.INGREDIENT;
this.isCollapsedRecipe = group.crafting != null && group.crafting.itemToRecipe.values().stream()
.anyMatch(recipeId -> this.bookmarkItem.equalsRecipe(recipeId, this.bookmarkItem.groupId));
this.isCollapsedRecipe = group.crafting != null && this.bookmarkItem.recipeId != null
&& group.crafting.recipeToItems.containsKey(this.bookmarkItem.recipeId);
this.update();
}
@@ -2,6 +2,8 @@ package codechicken.nei.bookmark.tree;
import java.awt.geom.Point2D;
import org.lwjgl.input.Mouse;
class Camera {
private static final float ZOOM_MIN = 0.1f;
@@ -39,23 +41,34 @@ class Camera {
&& y < this.widgetY + this.widgetH;
}
public void startDrag(int mouseX, int mouseY) {
public void startDrag(Point2D mouse) {
this.dragging = true;
this.lastDragX = mouseX;
this.lastDragY = mouseY;
this.lastDragX = (float) mouse.getX();
this.lastDragY = (float) mouse.getY();
}
public void drag(int mouseX, int mouseY) {
public void drag(Point2D mouse) {
if (!this.dragging) {
return;
}
final float mouseX = (float) mouse.getX();
final float mouseY = (float) mouse.getY();
this.scrollX -= (mouseX - this.lastDragX) / this.zoomLevel;
this.scrollY -= (mouseY - this.lastDragY) / this.zoomLevel;
this.lastDragX = mouseX;
this.lastDragY = mouseY;
}
public void update(Point2D mouse, int treeWidth, int treeHeight) {
if (this.dragging && Mouse.isButtonDown(0)) {
drag(mouse);
}
clampToBounds(treeWidth, treeHeight);
}
public void endDrag() {
this.dragging = false;
}
@@ -79,7 +92,6 @@ class Camera {
} else {
this.scrollY -= direction * this.widgetH / (SCROLL_STEP_DIVISOR * this.zoomLevel);
}
}
public void centerOn(float worldX, float worldY) {
@@ -169,12 +169,12 @@ class CraftingTreeCanvas {
: -FIT_MARGIN;
}
public void startDrag(int mouseX, int mouseY) {
this.camera.startDrag(mouseX, mouseY);
public void startDrag(Point2D mouse) {
this.camera.startDrag(mouse);
}
public void drag(int mouseX, int mouseY) {
this.camera.drag(mouseX, mouseY);
public void drag(Point2D mouse) {
this.camera.drag(mouse);
}
public void endDrag() {
@@ -259,7 +259,7 @@ class CraftingTreeCanvas {
public void draw(Point2D mouse, Set<BookmarkItem> collapsedItems, SearchResult searchResult,
boolean useInventorySnapshot, ItemTreeSlot hoveredNode, ItemTreeSlot deleteHoverNode) {
this.camera.clampToBounds(this.treeWidth, this.treeHeight);
this.camera.update(mouse, this.treeWidth, this.treeHeight);
final float zScrollX = this.camera.scrollX * this.camera.zoomLevel;
final float zScrollY = this.camera.scrollY * this.camera.zoomLevel;
@@ -58,6 +58,7 @@ import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.guihook.IGuiClientSide;
import codechicken.nei.guihook.IGuiHandleMouseWheel;
import codechicken.nei.guihook.IGuiHotkeyListener;
import codechicken.nei.recipe.AcceptsFollowingTooltipLineHandler;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
@@ -77,7 +78,7 @@ import codechicken.nei.util.SlotInaccessible;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGuiClientSide, IGuiHandleMouseWheel,
IContainerTooltipHandler, IGuiContainerOverlay {
IGuiHotkeyListener, IContainerTooltipHandler, IGuiContainerOverlay {
public static class EventHandler {
@@ -102,20 +103,20 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
}
}
private static final int MARGIN = 4;
private static final int PADDING = 9;
private static final int PADDING_TOP = 22;
private static final DrawableResource BG_TEXTURE = new DrawableBuilder(
"nei:textures/gui/craftingtree.png",
0,
0,
64,
64).build();
64).setSlices(PADDING, PADDING, PADDING_TOP, PADDING).build();
private static final DateTimeFormatter SCREENSHOT_DATE_FORMAT = DateTimeFormatter
.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
private static final int MARGIN = 4;
private static final int PADDING = 9;
private static final int PADDING_TOP = 22;
private final LRUCache<String, String> handlerTitleCache = new LRUCache<>(100);
private GuiScreen prevGui;
@@ -338,15 +339,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
this.hoveredNode = canHover && hoveredBadgeHandler == null ? this.canvas.getNodeMouseOver(getMousePosition())
: null;
BG_TEXTURE.draw(
MARGIN,
MARGIN,
this.width - MARGIN * 2,
this.height - MARGIN * 2,
PADDING,
PADDING,
PADDING_TOP,
PADDING);
BG_TEXTURE.draw(MARGIN, MARGIN, this.width - MARGIN * 2, this.height - MARGIN * 2);
if (this.math.outputRecipes.isEmpty()) {
drawCenteredString(
@@ -765,7 +758,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
return true;
}
this.canvas.startDrag(mouseX, mouseY);
this.canvas.startDrag(getMousePosition());
}
return false;
@@ -786,7 +779,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
}
this.canvas.drag(mouseX, mouseY);
this.canvas.drag(getMousePosition());
}
@Override
@@ -919,6 +912,12 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
return;
}
super.keyTyped(typedChar, keyCode);
}
@Override
public boolean onHotkeyPressed() {
if (this.statsPanelVisible && KeyManager.isKeyDown("recipe.recipe")) {
final Point2D mouse = getMousePosition();
final Widget widget = this.statsPanel.getWidgetUnderMouse((int) mouse.getX(), (int) mouse.getY());
@@ -926,11 +925,11 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
if (widget != null && widget instanceof StatsPanel.ItemStatsSlot itemStatsSlot
&& itemStatsSlot.recipeId != null) {
GuiCraftingRecipe.openRecipeGui("recipeId", itemStatsSlot.recipeId.getResult(), itemStatsSlot.recipeId);
return;
return true;
}
}
super.keyTyped(typedChar, keyCode);
return false;
}
private String getHandlerTitle(String handlerName) {
@@ -242,7 +242,7 @@ class StatsPanel extends ScrollContainer {
26,
84,
46,
16).build();
16).setSlices(0, 0, 2, 0).build();
private static final DrawableResource SLOT_DEFAULT = new DrawableBuilder(
"nei:textures/gui/craftingtree.png",
@@ -290,8 +290,8 @@ class StatsPanel extends ScrollContainer {
public StatsPanel() {
final ScrollBar scrollbar = new ScrollBar().setTrackWidth(4)
.setThumbTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 100, 24, 4).build(), 0, 0)
.setTrackTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 104, 24, 4).build(), 0, 0)
.setThumbTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 100, 24, 4).build())
.setTrackTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 104, 24, 4).build())
.setTrackPadding(1, -5, 1, 0).setOverflowType(ScrollBar.OverflowType.AUTO);
setHorizontalScroll(scrollbar);
@@ -589,7 +589,7 @@ class StatsPanel extends ScrollContainer {
RenderHelper.enableGUIStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
BG_TEXTURE.draw(this.x, this.y, this.w, this.h, 0, 0, 2, 0);
BG_TEXTURE.draw(this.x, this.y, this.w, this.h);
super.draw(mouseX, mouseY);
@@ -13,6 +13,10 @@ public class ConfigSet {
public NBTTagCompound nbt;
public final ConfigTagParent config;
public ConfigSet(File nbtFile) {
this(nbtFile, null);
}
public ConfigSet(File nbtFile, ConfigTagParent config) {
this.nbtFile = nbtFile;
this.config = config;
@@ -110,7 +110,7 @@ public abstract class DataDumper extends Option {
}
public int getMode() {
return renderTag().getIntValue(0);
return getTag().getIntValue(0);
}
public void drawButton(int mousex, int mousey, Rectangle4i b, String text) {
@@ -29,7 +29,6 @@ public class GuiHighlightTips extends GuiScreenWidget {
private GuiCCButton toggleButton;
private final Option opt;
private final GuiOptionList parentGui;
private final boolean world;
private Point dragDown;
public GuiHighlightTips(Option opt) {
@@ -37,7 +36,6 @@ public class GuiHighlightTips extends GuiScreenWidget {
this.opt = opt;
name = opt.name;
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
this.world = parentGui.worldConfig();
}
@Override
@@ -63,15 +61,11 @@ public class GuiHighlightTips extends GuiScreenWidget {
}
private boolean show() {
return renderTag(name).getBooleanValue();
return getTag(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);
return opt.configSet().config.getTag(s);
}
@Override
@@ -95,7 +89,7 @@ public class GuiHighlightTips extends GuiScreenWidget {
}
public Point getPos() {
return new Point(renderTag(name + ".x").getIntValue(), renderTag(name + ".y").getIntValue());
return new Point(getTag(name + ".x").getIntValue(), getTag(name + ".y").getIntValue());
}
public Dimension sampleSize() // copied from HUDManager when running with the sample for this gui
@@ -161,7 +155,6 @@ public class GuiHighlightTips extends GuiScreenWidget {
}
private void setPos(Point p) {
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);
}
@@ -52,7 +52,6 @@ public class GuiItemSorter extends GuiOptionPane {
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;
@@ -62,8 +61,7 @@ public class GuiItemSorter extends GuiOptionPane {
public GuiItemSorter(Option opt) {
this.opt = opt;
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
this.world = parentGui.worldConfig();
list = ItemSorter.fromSaveString(renderTag(opt.configName()).getValue());
list = ItemSorter.fromSaveString(getTag(opt.configName()).getValue());
for (SortEntry e : list) slots.add(new SortItem(e));
}
@@ -122,10 +120,7 @@ public class GuiItemSorter extends GuiOptionPane {
list.add(nslot, dragged.e);
getTag(opt.configName()).setValue(ItemSorter.getSaveString(list));
if (opt.activeTag() == getTag(opt.configName())) {
ItemSorter.list = new ArrayList<>(list);
}
ItemSorter.list = new ArrayList<>(list);
ItemList.refreshItems.restart();
}
@@ -133,11 +128,7 @@ public class GuiItemSorter extends GuiOptionPane {
}
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);
return opt.configSet().config.getTag(s);
}
public SortItem itemAt(int mx, int my) {
@@ -5,8 +5,8 @@ import net.minecraft.client.gui.GuiYesNoCallback;
public class GuiNEIOptionList extends GuiOptionList implements GuiYesNoCallback {
public GuiNEIOptionList(GuiScreen parent, OptionList optionList, boolean world) {
super(parent, optionList, world);
public GuiNEIOptionList(GuiScreen parent, OptionList optionList) {
super(parent, optionList);
}
@Override
@@ -1,7 +1,6 @@
package codechicken.nei.config;
import static codechicken.lib.gui.GuiDraw.drawMultilineTip;
import static codechicken.lib.gui.GuiDraw.drawStringC;
import static codechicken.lib.gui.GuiDraw.fontRenderer;
import static codechicken.lib.gui.GuiDraw.getMousePosition;
import static net.minecraft.util.StatCollector.translateToLocal;
@@ -10,7 +9,6 @@ import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
@@ -23,8 +21,6 @@ import org.lwjgl.opengl.GL11;
import codechicken.core.gui.GuiCCButton;
import codechicken.core.gui.GuiScreenWidget;
import codechicken.core.gui.GuiScrollSlot;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.LayoutManager;
public class GuiOptionList extends GuiScreenWidget {
@@ -66,25 +62,11 @@ public class GuiOptionList extends GuiScreenWidget {
protected void drawSlot(int slot, int x, int y, int mx, int my, float frame) {
Option o = options.get(slot);
GL11.glTranslatef(x, y, 0);
if (o.showWorldSelector() && world) drawWorldSelector(o, mx, my);
GL11.glTranslatef(24, 2, 0);
o.draw(mx - 24, my - 2, frame);
GL11.glTranslatef(-x - 24, -y - 2, 0);
}
public Rectangle4i worldButtonSize() {
return new Rectangle4i(0, 2, 20, 20);
}
private void drawWorldSelector(Option o, int mousex, int mousey) {
Rectangle4i b = worldButtonSize();
boolean set = o.hasWorldOverride();
boolean mouseover = b.contains(mousex, mousey);
GL11.glColor4f(1, 1, 1, 1);
LayoutManager.drawButtonBackground(b.x, b.y, b.w, b.h, true, !set ? 0 : mouseover ? 2 : 1);
drawStringC("W", b.x, b.y, b.w, b.h, -1);
}
@Override
public void drawBackground(float frame) {
Rectangle sbar = scrollbarBounds();
@@ -116,16 +98,7 @@ public class GuiOptionList extends GuiScreenWidget {
@Override
protected void slotClicked(int slot, int button, int mx, int my, int count) {
Option o = options.get(slot);
if (world && o.showWorldSelector() && worldButtonSize().contains(mx, my)) {
if (button == 1 && o.hasWorldOverride()) {
o.useGlobals();
Option.playClickSound();
} else if (button == 0 && !o.hasWorldOverride()) {
o.copyGlobals();
Option.playClickSound();
}
} else if (slotBounds(slot).contains(mx, my)) {
if (slotBounds(slot).contains(mx, my)) {
options.get(slot).mouseClicked(mx - 24, my - 2, button);
}
}
@@ -167,26 +140,20 @@ public class GuiOptionList extends GuiScreenWidget {
}
private List<String> handleTooltip(int slot, int mx, int my, List<String> tooltip) {
Option o = options.get(slot);
if (world && o.showWorldSelector() && worldButtonSize().contains(mx, my))
tooltip.add(translateToLocal("nei.options.wbutton.tip." + (o.hasWorldOverride() ? "1" : "0")));
if (slotBounds(slot).contains(mx, my)) return o.handleTooltip(mx - 24, my - 2, tooltip);
if (slotBounds(slot).contains(mx, my)) return options.get(slot).handleTooltip(mx - 24, my - 2, tooltip);
return tooltip;
}
}
protected final GuiScreen parent;
protected final OptionList optionList;
protected boolean world;
protected OptionScrollSlot slot;
protected GuiCCButton backButton;
protected GuiCCButton worldButton;
public GuiOptionList(GuiScreen parent, OptionList optionList, boolean world) {
public GuiOptionList(GuiScreen parent, OptionList optionList) {
this.parent = parent;
this.optionList = optionList;
this.world = world;
}
@Override
@@ -202,29 +169,18 @@ public class GuiOptionList extends GuiScreenWidget {
backButton.width = Math.min(200, width - 40);
backButton.x = (width - backButton.width) / 2;
backButton.y = height - 25;
worldButton.width = 60;
worldButton.x = width - worldButton.width - 15;
}
@Override
public void addWidgets() {
add(slot = new OptionScrollSlot());
add(backButton = new GuiCCButton(0, 0, 0, 20, translateToLocal("nei.options.back")).setActionCommand("back"));
add(worldButton = new GuiCCButton(0, 2, 0, 16, worldButtonName()).setActionCommand("world"));
}
private String worldButtonName() {
return translateToLocal("nei.options." + (world ? "world" : "global"));
}
@Override
public void actionPerformed(String ident, Object... params) {
if (ident.equals("back")) {
if (parent instanceof GuiOptionList) ((GuiOptionList) parent).world = world;
Minecraft.getMinecraft().displayGuiScreen(parent);
} else if (ident.equals("world")) {
world = !world;
worldButton.text = worldButtonName();
}
}
@@ -242,9 +198,6 @@ public class GuiOptionList extends GuiScreenWidget {
private void drawTooltip() {
List<String> tooltip = new LinkedList<>();
Point mouse = getMousePosition();
if (worldButton.pointInside(mouse.x, mouse.y))
tooltip.addAll(Arrays.asList(translateToLocal("nei.options.global.tip." + (world ? "1" : "0")).split(":")));
tooltip = slot.handleTooltip(mouse.x, mouse.y, tooltip);
drawMultilineTip(mouse.x + 12, mouse.y - 12, tooltip);
}
@@ -265,8 +218,4 @@ public class GuiOptionList extends GuiScreenWidget {
super.keyTyped(c, keycode);
}
}
public boolean worldConfig() {
return world;
}
}
@@ -55,7 +55,6 @@ 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;
@@ -69,10 +68,9 @@ 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, boolean world) {
public PanelPlaceholder(Option opt, String name) {
this.opt = opt;
this.name = name;
this.world = world;
}
public void resize(Rectangle4i margin) {
@@ -163,7 +161,7 @@ public class GuiPanelSettings extends GuiScreenWidget {
}
private ConfigTag getTag(String s) {
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
return opt.configSet().config.getTag(s);
}
protected void drawItems() {
@@ -241,9 +239,8 @@ public class GuiPanelSettings extends GuiScreenWidget {
super(176, 198);
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
boolean world = parentGui.worldConfig();
bookmarksPanel = new PanelPlaceholder(opt, opt.name + ".bookmarks", world);
itemsPanel = new PanelPlaceholder(opt, opt.name + ".items", world);
bookmarksPanel = new PanelPlaceholder(opt, opt.name + ".bookmarks");
itemsPanel = new PanelPlaceholder(opt, opt.name + ".items");
}
@Override
@@ -74,7 +74,11 @@ public class IMCHandler {
tag.getInteger("imageX"),
tag.getInteger("imageY"),
tag.getInteger("imageWidth"),
tag.getInteger("imageHeight"));
tag.getInteger("imageHeight"),
tag.hasKey("imageTextureWidth") ? tag.getInteger("imageTextureWidth")
: HandlerInfo.DEFAULT_IMAGE_TEXTURE_SIZE,
tag.hasKey("imageTextureHeight") ? tag.getInteger("imageTextureHeight")
: HandlerInfo.DEFAULT_IMAGE_TEXTURE_SIZE);
}
if (!info.hasImageOrItem()) {
final String itemName = tag.getString("itemName");
@@ -58,7 +58,7 @@ public class ItemPanelDumper extends DataDumper {
}
public int getRes() {
int i = renderTag(name + ".res").getIntValue(0);
int i = getTag(name + ".res").getIntValue(0);
int size = 16 << i;
if (size >= getSmallerScreenAxisSize() || i < 0) {
getTag(name + ".res").setIntValue(0);
@@ -95,7 +95,7 @@ public class ItemPanelDumper extends DataDumper {
}
if (resButtonSize().contains(mousex, mousey)) {
NEIClientUtils.playClickSound();
int current = renderTag(name + ".res").getIntValue(0);
int current = getTag(name + ".res").getIntValue(0);
int next;
// 31 - numberOfLeadingZeros(x) = floor(log2(x))
int max = 31 - Integer.numberOfLeadingZeros(getSmallerScreenAxisSize()) - 3;
@@ -59,12 +59,8 @@ public abstract class Option {
return elem;
}
public ConfigSet globalConfigSet() {
return assertParentOverride(parent.globalConfigSet());
}
public ConfigSet worldConfigSet() {
return assertParentOverride(parent.worldConfigSet());
public ConfigSet configSet() {
return assertParentOverride(parent.configSet());
}
public OptionList configBase() {
@@ -72,127 +68,19 @@ public abstract class Option {
}
/**
* @return true if the world config contains a tag with this name
*/
public boolean worldSpecific() {
return worldSpecific(configName());
}
/**
* @return true if the world config contains a tag with this name
*/
public boolean worldSpecific(String s) {
ConfigTag tag = worldConfigSet().config.getTag(s, false);
return tag != null && tag.value != null;
}
/**
* @return The currently config set for the gui based on the world/global button
*/
public ConfigSet configSet() {
return worldConfig() ? worldConfigSet() : globalConfigSet();
}
/**
* @return The tag to render, world if world override, otherwise global
*/
public ConfigTag renderTag() {
return renderTag(configName());
}
/**
* @return The tag to render, world if world override, otherwise global
*/
public ConfigTag renderTag(String s) {
return (worldConfig() && worldSpecific(s) ? worldConfigSet() : globalConfigSet()).config.getTag(s);
}
/**
* @return The tag with this name from the current configSet
* @return The tag with this name from the config set
*/
public ConfigTag getTag() {
return getTag(configName());
}
/**
* @return The tag with this name from the current configSet
* @return The tag with this name from the config set
*/
public ConfigTag getTag(String s) {
return configSet().config.getTag(s);
}
/**
* @return The tag currently being used ingame, world if a world override exists, otherwise global
*/
public ConfigTag activeTag() {
return activeTag(configName());
}
/**
* @return The tag currently being used ingame, world if a world override exists, otherwise global
*/
public ConfigTag activeTag(String s) {
return (worldSpecific() ? worldConfigSet() : globalConfigSet()).config.getTag(s);
}
/**
* @return true if the gui is in world config mode
*/
public boolean worldConfig() {
return getSlot().getGui().worldConfig();
}
/**
* @return true if in world mode, but this tag has no world override. Changes should not be made in this mode
*/
public boolean defaulting() {
return defaulting(configName());
}
/**
* @return true if in world mode, but this tag has no world override. Changes should not be made in this mode
*/
public boolean defaulting(String s) {
return worldConfig() && !worldSpecific(s);
}
/**
* Called when world specific is disabled for this option. Should delete all world specific overrides
*/
public void useGlobals() {
useGlobal(configName());
}
/**
* Deletes a specific named tag from the worldConfig
*/
public void useGlobal(String s) {
if (worldConfig()) worldConfigSet().config.removeTag(s);
}
/**
* Called when world specific is activated for this option. Should copy the global value into a world specific
* override
*/
public void copyGlobals() {
copyGlobal(configName(), true);
}
/**
* Copies the global value into a world specific override
*/
public void copyGlobal(String s) {
copyGlobal(s, false);
}
public void copyGlobal(String s, boolean recursive) {
if (!worldConfig()) return;
ConfigTag tag = globalConfigSet().config.getTag(s);
worldConfigSet().config.getTag(s).setValue(tag.getValue());
if (recursive) for (String s2 : tag.childTagMap().keySet()) copyGlobal(s + "." + s2);
}
public void onAdded(OptionScrollSlot slot) {
this.slotRef = new WeakReference<>(slot);
}
@@ -217,12 +105,4 @@ public abstract class Option {
public List<String> handleTooltip(int mx, int my, List<String> currenttip) {
return currenttip;
}
public boolean showWorldSelector() {
return true;
}
public boolean hasWorldOverride() {
return worldSpecific();
}
}
@@ -16,7 +16,11 @@ public class OptionCycled extends OptionButton {
}
public int value() {
return renderTag().getIntValue();
return getTag().getIntValue();
}
public void setValue(int value) {
getTag().setIntValue(value);
}
@Override
@@ -52,7 +56,7 @@ public class OptionCycled extends OptionButton {
if (next == value()) return false;
getTag().setIntValue(next);
setValue(next);
return true;
}
@@ -0,0 +1,42 @@
package codechicken.nei.config;
import net.minecraft.util.ChatComponentTranslation;
import codechicken.nei.ClientHandler;
import codechicken.nei.NEIClientUtils;
public class OptionExportConfig extends OptionButton {
private final String fileName;
public OptionExportConfig(String fileName) {
super("tools.export_configs." + fileName.substring(0, fileName.lastIndexOf('.')));
this.fileName = fileName;
}
@Override
public String getPrefix() {
return fileName;
}
@Override
public String getButtonText() {
return translateN("tools.export_configs.button");
}
@Override
public String getTooltip() {
return null;
}
@Override
public boolean onClick(int button) {
final boolean exported = ClientHandler.exportDefaultConfig(fileName);
NEIClientUtils.printChatMessage(
new ChatComponentTranslation(
exported ? "nei.options.tools.export_configs.done" : "nei.options.tools.export_configs.skipped",
fileName));
return true;
}
}
@@ -22,7 +22,7 @@ public class OptionIntegerField extends OptionTextField {
@Override
protected int getMaxInputWidth() {
if (renderTag().getValue().startsWith("0x")) {
if (getTag().getValue().startsWith("0x")) {
return GuiDraw.getStringWidth("0x" + Long.toHexString(this.max).toUpperCase()) + 32;
}
@@ -20,12 +20,7 @@ public class OptionList extends OptionButton {
}
@Override
public ConfigSet globalConfigSet() {
return null;
}
@Override
public ConfigSet worldConfigSet() {
public ConfigSet configSet() {
return null;
}
@@ -35,8 +30,8 @@ public class OptionList extends OptionButton {
}
@Override
public GuiOptionList getGui(GuiScreen parent, OptionList list, boolean world) {
return new GuiOptionList(parent, list, world);
public GuiOptionList getGui(GuiScreen parent, OptionList list) {
return new GuiOptionList(parent, list);
}
}
@@ -118,41 +113,25 @@ public class OptionList extends OptionButton {
*
* @param parent The parent screen for the back button
* @param list The option list to be displayed in the gui
* @param world true if in world config mode, false for global
*/
public GuiOptionList getGui(GuiScreen parent, OptionList list, boolean world) {
return this.parent.getGui(parent, list, world);
public GuiOptionList getGui(GuiScreen parent, OptionList list) {
return this.parent.getGui(parent, list);
}
public void openGui(GuiScreen parent, boolean world) {
Minecraft.getMinecraft().displayGuiScreen(getGui(parent, this, world));
}
/**
* Adds this option to a temporary slot and gui for internal setting manipulation.
*/
public GuiOptionList synthesizeEnvironment(boolean world) {
GuiOptionList gui = getGui(null, this, world);
gui.addWidgets();
return gui;
public void openGui(GuiScreen parent) {
Minecraft.getMinecraft().displayGuiScreen(getGui(parent, this));
}
@Override
public boolean onClick(int button) {
openGui(getSlot().getGui(), getSlot().getGui().worldConfig());
openGui(getSlot().getGui());
return true;
}
@Override
public boolean showWorldSelector() {
return false;
}
@Override
public void onAdded(OptionScrollSlot slot) {
super.onAdded(slot);
globalConfigSet().config.getTag(configName()).useBraces();
worldConfigSet().config.getTag(configName()).useBraces();
configSet().config.getTag(configName()).useBraces();
}
}
@@ -127,7 +127,7 @@ public abstract class OptionStringSet extends Option {
}
public List<String> values() {
return Arrays.asList(renderTag().getValue().replace(" ", "").split(","));
return Arrays.asList(getTag().getValue().replace(" ", "").split(","));
}
public int buttonX() {
@@ -16,8 +16,7 @@ public class OptionTextField extends Option {
@Override
public void onTextChange(String oldText) {
// don't override global if text hasn't changed
if (focused() && isValidValue(text()) && (!defaulting() || !text().equals(getTag().getValue()))) {
if (focused() && isValidValue(text())) {
getTag().setValue(text());
OptionTextField.this.onTextChange(text());
}
@@ -25,7 +24,7 @@ public class OptionTextField extends Option {
@Override
public void setFocus(boolean focus) {
if (!focus && !isValidValue(text())) setText(renderTag().getValue());
if (!focus && !isValidValue(text())) setText(getTag().getValue());
super.setFocus(focus);
}
@@ -45,7 +44,7 @@ public class OptionTextField extends Option {
@Override
public void update() {
textField.update();
if (!textField.focused()) textField.setText(renderTag().getValue());
if (!textField.focused()) textField.setText(getTag().getValue());
}
public String getPrefix() {
@@ -14,7 +14,7 @@ public class OptionToggleButton extends OptionButton {
}
public boolean state() {
return renderTag().getBooleanValue();
return getTag().getBooleanValue();
}
@Override
@@ -34,7 +34,7 @@ public class OptionToggleButtonBoubs extends OptionButton {
}
public boolean state() {
return renderTag().getBooleanValue();
return getTag().getBooleanValue();
}
@Override
@@ -0,0 +1,54 @@
package codechicken.nei.config;
import java.util.Collections;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.recipe.RecipeJsonWriter;
public class RecipeDumper extends DataDumper {
public RecipeDumper(String name) {
super(name);
}
@Override
public String[] header() {
return new String[0];
}
@Override
public Iterable<String[]> dump(int mode) {
return Collections.emptyList();
}
@Override
public String renderName() {
return translateN(name);
}
public Rectangle4i separateButtonSize() {
return dumpButtonSize();
}
public Rectangle4i combinedButtonSize() {
return modeButtonSize();
}
@Override
public void draw(int mousex, int mousey, float frame) {
drawPrefix();
drawButton(mousex, mousey, combinedButtonSize(), translateN(name + ".combined"));
drawButton(mousex, mousey, separateButtonSize(), translateN(name + ".separate"));
}
@Override
public void mouseClicked(int mousex, int mousey, int button) {
final boolean combined = combinedButtonSize().contains(mousex, mousey);
if (combined || separateButtonSize().contains(mousex, mousey)) {
NEIClientUtils.playClickSound();
RecipeJsonWriter.dumpAll(!combined);
}
}
}
@@ -79,19 +79,24 @@ public class RightPanel extends GuiWidget {
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (!focused() && KeyManager.isHashDown("gui.search")) {
setFocus(true);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -15,13 +15,21 @@ public class DrawableBuilder {
private int paddingBottom = 0;
private int paddingLeft = 0;
private int paddingRight = 0;
private int sliceLeft = 0;
private int sliceRight = 0;
private int sliceTop = 0;
private int sliceBottom = 0;
public DrawableBuilder(String resourceLocation, int u, int v, int width, int height) {
this(new ResourceLocation(resourceLocation), u, v, width, height);
}
public DrawableBuilder(ResourceLocation resource, int u, int v, int width, int height) {
this.u = u;
this.v = v;
this.width = width;
this.height = height;
this.resourceLocation = new ResourceLocation(resourceLocation);
this.resourceLocation = resource;
}
public DrawableBuilder setTextureSize(int width, int height) {
@@ -38,6 +46,14 @@ public class DrawableBuilder {
return this;
}
public DrawableBuilder setSlices(int sliceLeft, int sliceRight, int sliceTop, int sliceBottom) {
this.sliceLeft = sliceLeft;
this.sliceRight = sliceRight;
this.sliceTop = sliceTop;
this.sliceBottom = sliceBottom;
return this;
}
public DrawableBuilder trim(int trimTop, int trimBottom, int trimLeft, int trimRight) {
this.u += trimLeft;
this.v += trimTop;
@@ -58,6 +74,10 @@ public class DrawableBuilder {
paddingLeft,
paddingRight,
textureWidth,
textureHeight);
textureHeight,
sliceLeft,
sliceRight,
sliceTop,
sliceBottom);
}
}
@@ -1,7 +1,7 @@
package codechicken.nei.drawable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
@@ -19,9 +19,42 @@ public class DrawableResource extends Image {
private final int paddingLeft;
private final int paddingRight;
private final int sliceLeft;
private final int sliceRight;
private final int sliceTop;
private final int sliceBottom;
public DrawableResource(ResourceLocation resourceLocation, int u, int v, int width, int height, int paddingTop,
int paddingBottom, int paddingLeft, int paddingRight, int textureWidth, int textureHeight) {
this(
resourceLocation,
u,
v,
width,
height,
paddingTop,
paddingBottom,
paddingLeft,
paddingRight,
textureWidth,
textureHeight,
0,
0,
0,
0);
}
// negative padding lets the texture overflow the box it is drawn into
public DrawableResource(ResourceLocation resourceLocation, int u, int v, int width, int height, int paddingTop,
int paddingBottom, int paddingLeft, int paddingRight, int textureWidth, int textureHeight, int sliceLeft,
int sliceRight, int sliceTop, int sliceBottom) {
super(u, v, width, height);
this.sliceLeft = sliceLeft;
this.sliceRight = sliceRight;
this.sliceTop = sliceTop;
this.sliceBottom = sliceBottom;
this.resourceLocation = resourceLocation;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
@@ -40,6 +73,14 @@ public class DrawableResource extends Image {
return height + paddingTop + paddingBottom;
}
public int getTextureWidth() {
return this.textureWidth;
}
public int getTextureHeight() {
return this.textureHeight;
}
public void bindTexture() {
Minecraft.getMinecraft().getTextureManager().bindTexture(this.resourceLocation);
}
@@ -55,18 +96,28 @@ public class DrawableResource extends Image {
GL11.glEnable(GL11.GL_TEXTURE_2D);
bindTexture();
drawModalRectWithCustomSizedTexture(xOffset + maskLeft, yOffset + maskTop, maskLeft, maskTop, width, height);
final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
addQuad(tessellator, xOffset + maskLeft, yOffset + maskTop, maskLeft, maskTop, width, height);
tessellator.draw();
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
}
public void draw(int xOffset, int yOffset, int width, int height, int sliceLeft, int sliceRight, int sliceTop,
int sliceBottom) {
final int textureWidth = getWidth();
final int textureHeight = getHeight();
final int middleWidth = textureWidth - sliceLeft - sliceRight;
final int middleHeight = textureHeight - sliceTop - sliceBottom;
final int tileWidth = width - sliceLeft - sliceRight;
final int tileHeight = height - sliceTop - sliceBottom;
public void draw(int xOffset, int yOffset, int width, int height) {
if (width == getWidth() && height == getHeight()) {
draw(xOffset, yOffset);
return;
}
final int sliceLeft = this.sliceLeft;
final int sliceRight = this.sliceRight;
final int sliceTop = this.sliceTop;
final int sliceBottom = this.sliceBottom;
final int middleWidth = this.width - sliceLeft - sliceRight;
final int middleHeight = this.height - sliceTop - sliceBottom;
final int tileWidth = width - this.paddingLeft - this.paddingRight - sliceLeft - sliceRight;
final int tileHeight = height - this.paddingTop - this.paddingBottom - sliceTop - sliceBottom;
if (middleWidth > 0 && middleHeight > 0 && tileWidth > 0 && tileHeight > 0) {
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
@@ -78,14 +129,30 @@ public class DrawableResource extends Image {
GL11.glEnable(GL11.GL_TEXTURE_2D);
bindTexture();
// middle parts
final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
final int x = xOffset + sliceLeft + tileW * middleWidth;
// border top
addQuad(tessellator, x, yOffset, sliceLeft, 0, tileWidthSize, sliceTop);
// border bottom
addQuad(
tessellator,
x,
yOffset + sliceTop + tileHeight,
sliceLeft,
sliceTop + middleHeight,
tileWidthSize,
sliceBottom);
// middle parts
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
drawModalRectWithCustomSizedTexture(
addQuad(
tessellator,
x,
yOffset + sliceTop + tileH * middleHeight,
sliceLeft,
@@ -95,114 +162,75 @@ public class DrawableResource extends Image {
}
}
// border top
if (sliceTop > 0) {
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileW * middleWidth,
yOffset,
sliceLeft,
0,
tileWidthSize,
sliceTop);
}
}
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
final int y = yOffset + sliceTop + tileH * middleHeight;
// border bottom
if (sliceBottom > 0) {
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileW * middleWidth,
yOffset + sliceTop + tileHeight,
sliceLeft,
sliceTop + middleHeight,
tileWidthSize,
sliceBottom);
}
}
// border left
if (sliceLeft > 0) {
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
drawModalRectWithCustomSizedTexture(
xOffset,
yOffset + sliceTop + tileH * middleHeight,
0,
sliceTop,
sliceLeft,
tileHeightSize);
}
}
// border right
if (sliceRight > 0) {
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileWidth,
yOffset + sliceTop + tileH * middleHeight,
sliceLeft + middleWidth,
sliceTop,
sliceRight,
tileHeightSize);
}
// border left
addQuad(tessellator, xOffset, y, 0, sliceTop, sliceLeft, tileHeightSize);
// border right
addQuad(
tessellator,
xOffset + sliceLeft + tileWidth,
y,
sliceLeft + middleWidth,
sliceTop,
sliceRight,
tileHeightSize);
}
// angle left-top
if (sliceLeft > 0 && sliceTop > 0) {
drawModalRectWithCustomSizedTexture(xOffset, yOffset, 0, 0, sliceLeft, sliceTop);
}
addQuad(tessellator, xOffset, yOffset, 0, 0, sliceLeft, sliceTop);
// angle right-top
if (sliceRight > 0 && sliceTop > 0) {
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileWidth,
yOffset,
sliceLeft + middleWidth,
0,
sliceRight,
sliceTop);
}
addQuad(
tessellator,
xOffset + sliceLeft + tileWidth,
yOffset,
sliceLeft + middleWidth,
0,
sliceRight,
sliceTop);
// angle left-bottom
if (sliceLeft > 0 && sliceBottom > 0) {
drawModalRectWithCustomSizedTexture(
xOffset,
yOffset + sliceTop + tileHeight,
0,
sliceTop + middleHeight,
sliceLeft,
sliceBottom);
}
addQuad(
tessellator,
xOffset,
yOffset + sliceTop + tileHeight,
0,
sliceTop + middleHeight,
sliceLeft,
sliceBottom);
// angle right-bottom
if (sliceRight > 0 && sliceBottom > 0) {
drawModalRectWithCustomSizedTexture(
xOffset + sliceLeft + tileWidth,
yOffset + sliceTop + tileHeight,
sliceLeft + middleWidth,
sliceTop + middleHeight,
sliceRight,
sliceBottom);
}
addQuad(
tessellator,
xOffset + sliceLeft + tileWidth,
yOffset + sliceTop + tileHeight,
sliceLeft + middleWidth,
sliceTop + middleHeight,
sliceRight,
sliceBottom);
tessellator.draw();
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
}
}
private void drawModalRectWithCustomSizedTexture(int xOffset, int yOffset, int u, int v, int width, int height) {
Gui.func_146110_a(
xOffset + this.paddingLeft,
yOffset + this.paddingTop,
this.x + u,
this.y + v,
width,
height,
this.textureWidth,
this.textureHeight);
private void addQuad(Tessellator tessellator, int xOffset, int yOffset, int u, int v, int width, int height) {
if (width <= 0 || height <= 0) return;
final float scaleU = 1.0F / this.textureWidth;
final float scaleV = 1.0F / this.textureHeight;
final int x = xOffset + this.paddingLeft;
final int y = yOffset + this.paddingTop;
final float u0 = (this.x + u) * scaleU;
final float u1 = (this.x + u + width) * scaleU;
final float v0 = (this.y + v) * scaleV;
final float v1 = (this.y + v + height) * scaleV;
tessellator.addVertexWithUV(x, y + height, 0.0D, u0, v1);
tessellator.addVertexWithUV(x + width, y + height, 0.0D, u1, v1);
tessellator.addVertexWithUV(x + width, y, 0.0D, u1, v0);
tessellator.addVertexWithUV(x, y, 0.0D, u0, v0);
}
}
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.recipe.IRecipeHandler;
@@ -30,7 +30,10 @@ public class AllMultiRecipeFilter implements IRecipeFilter {
try {
if (filter != null && !filter.matches(handler, recipeIndex)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
NEIClientUtils.errorOnce(
"filter:" + handler.getHandlerId() + ":" + filter.getClass().getName(),
"Exception filtering " + handler.getHandlerId() + " with " + filter,
e);
}
}
return true;
@@ -1,44 +0,0 @@
package codechicken.nei.filter;
import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class AllOthersRecipeFilter implements IRecipeFilter {
private final ItemFilter filter;
public AllOthersRecipeFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
if (!match(pStack)) {
return false;
}
}
return true;
}
protected boolean match(PositionedStack pStack) {
if (pStack == null) {
return false;
}
for (ItemStack stack : pStack.items) {
if (filter.matches(stack)) {
return true;
}
}
return false;
}
}
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.recipe.IRecipeHandler;
@@ -30,7 +30,10 @@ public class AnyMultiRecipeFilter implements IRecipeFilter {
try {
if (filter != null && filter.matches(handler, recipeIndex)) return true;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
NEIClientUtils.errorOnce(
"filter:" + handler.getHandlerId() + ":" + filter.getClass().getName(),
"Exception filtering " + handler.getHandlerId() + " with " + filter,
e);
}
}
@@ -1,44 +0,0 @@
package codechicken.nei.filter;
import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.recipe.IRecipeHandler;
public class AnyOthersRecipeFilter implements IRecipeFilter {
private final ItemFilter filter;
public AnyOthersRecipeFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
if (match(pStack)) {
return true;
}
}
return false;
}
protected boolean match(PositionedStack pStack) {
if (pStack == null) {
return false;
}
for (ItemStack stack : pStack.items) {
if (filter.matches(stack)) {
return true;
}
}
return false;
}
}
@@ -16,7 +16,8 @@ public class RecipeFilter implements IRecipeFilter {
ANY,
INPUT,
OUTPUT;
OUTPUT,
AUXILIARY;
public static FilterContext fromChar(char c) {
switch (c) {
@@ -24,6 +25,8 @@ public class RecipeFilter implements IRecipeFilter {
return INPUT;
case '>':
return OUTPUT;
case '^':
return AUXILIARY;
default:
return ANY;
}
@@ -48,17 +51,14 @@ public class RecipeFilter implements IRecipeFilter {
return this.anyMatch;
}
if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) {
final PositionedStack result = handler.getResultStack(recipeIndex);
if (result != null && matchPositionedStack(result) == this.anyMatch) {
return this.anyMatch;
}
if (matchPositionedStack(handler.getOtherStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
if ((this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT)
&& matchPositionedStack(handler.getResultStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
if ((this.context == FilterContext.ANY || this.context == FilterContext.AUXILIARY)
&& matchPositionedStack(handler.getAuxiliaryStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
return !this.anyMatch;
@@ -449,6 +449,7 @@ public class GuiContainerManager {
}
private int clickHandled = 0;
private int hotkeyHandled = 0;
public final List<IContainerTooltipHandler> instanceTooltipHandlers;
public GuiContainerManager(GuiContainer screen) {
@@ -483,6 +484,7 @@ public class GuiContainerManager {
public void load() {
clickHandled = 0;
hotkeyHandled = 0;
for (IContainerObjectHandler objectHandler : objectHandlers) objectHandler.load(window);
}
@@ -499,9 +501,60 @@ public class GuiContainerManager {
public boolean lastKeyTyped(int keyID, char keyChar) {
if (keyID == 1) return false;
// hotkeys go before lastKeyTyped, otherwise a third-party handler that consumes every key blocks them
if (handleHotkeys()) return true;
for (IContainerInputHandler inputhander : inputHandlers)
if (inputhander.lastKeyTyped(window, keyChar, keyID)) return true;
return handleScreenHotkeys();
}
public boolean onHotkeyPressed() {
return handleHotkeys() || handleScreenHotkeys();
}
private boolean handleHotkeys() {
if (window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onHotkeyPressed()) {
return true;
}
for (IContainerInputHandler inputhander : inputHandlers) {
if (inputhander.onHotkeyPressed(window)) {
return true;
}
}
return false;
}
private boolean handleScreenHotkeys() {
return window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onScreenHotkeyPressed();
}
public boolean handleMouseHotkey() {
final int button = Mouse.getEventButton();
if (button < 0) {
return false;
}
if (!Mouse.getEventButtonState()) {
// the press has been consumed, so the release must not reach the screen either
if ((hotkeyHandled & 1 << button) == 0) {
return false;
}
hotkeyHandled &= ~(1 << button);
return true;
}
if (KeyManager.handleMouseHotkey(button, this::onHotkeyPressed)) {
hotkeyHandled |= 1 << button;
return true;
}
return false;
}
@@ -36,6 +36,18 @@ public interface IContainerInputHandler {
*/
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID);
/**
* Called when a key or a mouse button is pressed and the event has not been handled by the first pass. Use this for
* key bindings that work globally and check them with {@link codechicken.nei.KeyManager#isKeyDown}, so they work
* the same for keyboard keys and mouse buttons.
*
* @param gui An instance of the currentscreen
* @return true to terminate further processing of this event.
*/
default boolean onHotkeyPressed(GuiContainer gui) {
return false;
}
/**
* Called when the mouse is clicked in the gui
*
@@ -0,0 +1,27 @@
package codechicken.nei.guihook;
/**
* Interface for GuiContainers to receive NEI compatible hotkey events from both keyboard keys and mouse buttons
*/
public interface IGuiHotkeyListener {
/**
* Called before the input handlers when a key or a mouse button is pressed, check key bindings with
* {@link codechicken.nei.KeyManager#isKeyDown}
*
* @return true to terminate further processing of this event.
*/
default boolean onHotkeyPressed() {
return false;
}
/**
* Called after the input handlers for screen-wide hotkeys that do not depend on the hovered object, so they never
* take priority over more specific actions
*
* @return true to terminate further processing of this event.
*/
default boolean onScreenHotkeyPressed() {
return false;
}
}
@@ -5,13 +5,18 @@ import java.util.List;
import java.util.Map;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.inventory.SlotCrafting;
import net.minecraft.item.ItemStack;
import codechicken.nei.FastTransferManager;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemStackAmount;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.NEIServerUtils;
import codechicken.nei.RestartableTask;
import codechicken.nei.ServerSync;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.chain.RecipeChainIterator;
@@ -26,6 +31,7 @@ public class AutoCraftingManager {
@Override
public void execute() {
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
final ItemStack[] layout = snapshotLayout(guiContainer);
final List<BookmarkItem> initialItems = prepareInitialItems(math, getInventoryItems(guiContainer));
boolean processed = false;
boolean changed = false;
@@ -69,6 +75,11 @@ public class AutoCraftingManager {
} while (changed && !interrupted(guiContainer));
if (!interrupted(guiContainer)) {
restoreLayout(guiContainer, layout);
ServerSync.request();
}
} finally {
StackInfo.pauseItemDamageSound(false);
}
@@ -134,4 +145,70 @@ public class AutoCraftingManager {
return inventory;
}
private static ItemStack[] snapshotLayout(GuiContainer guiContainer) {
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final ItemStack[] layout = new ItemStack[slots.size()];
for (int index = 0; index < slots.size(); index++) {
final Slot slot = slots.get(index);
if (slot.getHasStack() && !(slot instanceof SlotCrafting)) {
layout[index] = slot.getStack().copy();
}
}
return layout;
}
private static void restoreLayout(GuiContainer guiContainer, ItemStack[] layout) {
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final EntityPlayer player = guiContainer.mc.thePlayer;
for (int index = 0; index < slots.size() && player.inventory.getItemStack() == null
&& guiContainer.mc.currentScreen == guiContainer; index++) {
final ItemStack expected = layout[index];
final Slot target = slots.get(index);
if (expected == null || inPlace(slots, layout, index)
|| !target.isItemValid(expected)
|| target.getHasStack() && !target.canTakeStack(player)) {
continue;
}
final Slot source = findMisplaced(slots, layout, expected, player);
if (source == null) continue;
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
FastTransferManager.clickSlot(guiContainer, target.slotNumber);
if (player.inventory.getItemStack() != null) {
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
}
}
FastTransferManager.dropHeldItem(guiContainer);
}
private static boolean inPlace(List<Slot> slots, ItemStack[] layout, int index) {
final ItemStack stack = slots.get(index).getStack();
return stack != null && layout[index] != null
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(layout[index], stack);
}
private static Slot findMisplaced(List<Slot> slots, ItemStack[] layout, ItemStack expected, EntityPlayer player) {
for (int index = 0; index < slots.size(); index++) {
final Slot slot = slots.get(index);
if (slot.getHasStack() && !(slot instanceof SlotCrafting)
&& !inPlace(slots, layout, index)
&& slot.canTakeStack(player)
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(expected, slot.getStack())) {
return slot;
}
}
return null;
}
}
@@ -1,6 +1,7 @@
package codechicken.nei.recipe;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -20,10 +21,13 @@ import codechicken.lib.inventory.InventoryUtils;
import codechicken.nei.FastTransferManager;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.ServerSync;
import codechicken.nei.api.IOverlayHandler;
public class DefaultOverlayHandler implements IOverlayHandler {
private static final long SYNC_TIMEOUT_MILLIS = 5000;
public static class DistributedIngred {
public DistributedIngred(ItemStack item) {
@@ -113,7 +117,8 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
boolean craft = false;
while (multiplier > 0) {
while (multiplier > 0 && firstGui.mc.currentScreen == firstGui
&& presenceOverlay(firstGui, handler, recipeIndex).stream().allMatch(s -> s.isPresent())) {
final int transfer = transferRecipe(firstGui, handler, recipeIndex, multiplier);
if (transfer <= 0) {
@@ -122,6 +127,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
multiplier -= transfer;
if (!craftingSlot.getHasStack() && !awaitServer(firstGui)) {
break;
}
if (craftingSlot.getHasStack() && craftingSlot.canTakeStack(firstGui.mc.thePlayer)) {
FastTransferManager.clickSlot(firstGui, craftingSlot.slotNumber, 0, 1);
@@ -139,9 +148,30 @@ public class DefaultOverlayHandler implements IOverlayHandler {
return craft;
}
private boolean awaitServer(GuiContainer gui) {
final long deadline = System.currentTimeMillis() + SYNC_TIMEOUT_MILLIS;
ServerSync.request();
while (ServerSync.pending()) {
if (gui.mc.currentScreen != gui || System.currentTimeMillis() > deadline) {
return false;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
return false;
}
}
return true;
}
protected boolean clearIngredients(GuiContainer gui, IRecipeHandler handler) {
for (Slot slot : getCraftMatrixSlots(gui, handler)) {
if (gui.mc.currentScreen != gui) return false;
if (slot.canTakeStack(gui.mc.thePlayer)) {
FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1);
if (slot.getHasStack()) return false;
@@ -206,7 +236,9 @@ public class DefaultOverlayHandler implements IOverlayHandler {
protected void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
final Slot craftingSlot = getCraftingResultSlot(gui);
for (Slot slot : gui.inventorySlots.inventorySlots) {
for (Slot slot : getSourceSlots(gui)) {
if (gui.mc.currentScreen != gui) return;
if (slot == craftingSlot || !slot.getHasStack()
|| !canMoveFrom(slot, gui)
|| !slot.canTakeStack(gui.mc.thePlayer))
@@ -240,6 +272,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
FastTransferManager.clickSlot(gui, slot.slotNumber);
}
// A right click with an empty or foreign cursor would take half of the dest stack
final ItemStack held = gui.mc.thePlayer.inventory.getItemStack();
if (held == null || !canStack(distrib.permutation, held)) break;
FastTransferManager.clickSlot(gui, dest.slotNumber, 1);
stackSize--;
}
@@ -370,7 +406,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
protected void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
for (Slot slot : gui.inventorySlots.inventorySlots) /* work out how much we have to go round */ {
for (Slot slot : getSourceSlots(gui)) /* work out how much we have to go round */ {
if (slot.getHasStack() && canMoveFrom(slot, gui)) {
final ItemStack pstack = slot.getStack();
final DistributedIngred istack = findIngred(ingredStacks, pstack);
@@ -408,6 +444,25 @@ public class DefaultOverlayHandler implements IOverlayHandler {
}
}
protected List<Slot> getSourceSlots(GuiContainer gui) {
final List<Slot> slots = new ArrayList<>(gui.inventorySlots.inventorySlots);
slots.sort(Comparator.comparingLong(slot -> getRemainingDurability(slot.getStack())));
return slots;
}
private static long getRemainingDurability(ItemStack stack) {
if (stack == null || stack.getMaxStackSize() != 1) return Long.MAX_VALUE;
final NBTTagCompound tagCompound = stack.getTagCompound();
if (tagCompound != null && tagCompound.hasKey("GT.ToolStats")) {
final NBTTagCompound toolStats = tagCompound.getCompoundTag("GT.ToolStats");
return toolStats.getLong("MaxDamage") - toolStats.getLong("Damage");
}
return stack.isItemStackDamageable() ? stack.getMaxDamage() - stack.getItemDamage() : Long.MAX_VALUE;
}
private int getGTToolDamagePerContainerCraft(ItemStack aStack) {
try {
@@ -122,18 +122,25 @@ public class GuiFavoriteButton extends GuiRecipeButton {
return Recipe.of(this.handlerRef);
}
public void lastKeyTyped(char keyChar, int keyID) {
@Override
public void lastKeyTyped(char keyChar, int keyID) {}
@Override
public boolean onHotkeyPressed() {
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
saveRecipeInBookmark();
return true;
}
if (KeyManager.isKeyDown("bookmark.open_crafting_tree") && NEIClientUtils.shiftKey()
&& saveRecipeInBookmark()) {
final int groupId = ItemPanels.bookmarkPanel.getGrid().getNextGroupId() - 1;
GuiCraftingTree.openCraftingTreeGui(ItemPanels.bookmarkPanel.getGrid(), groupId);
return true;
}
return false;
}
@Override
@@ -177,12 +177,20 @@ public class GuiOverlayButton extends GuiRecipeButton {
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGrid"));
hotkeys.put(
NEIClientUtils.getKeyName(
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH,
NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGridOnce"));
}
} else if (canFillCraftingGrid()) {
hotkeys.put(
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGrid"));
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("recipe.overlay.fillCraftingGridOnce"));
}
hotkeys.put(
@@ -329,7 +337,7 @@ public class GuiOverlayButton extends GuiRecipeButton {
if (!requireShiftForOverlayRecipe() || shift) {
if (this.canFillCraftingGrid) {
this.handlerRef.fillCraftingGrid(this.firstGui, 0);
this.handlerRef.fillCraftingGrid(this.firstGui, NEIClientUtils.controlKey() ? 1 : 0);
}
} else if (hasOverlay() && useOverlayRenderer()) {
this.handlerRef.useOverlayRenderer(this.firstGui);
@@ -338,14 +346,21 @@ public class GuiOverlayButton extends GuiRecipeButton {
}
@Override
public void lastKeyTyped(char keyChar, int keyID) {
public void lastKeyTyped(char keyChar, int keyID) {}
@Override
public boolean onHotkeyPressed() {
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
saveRecipeInBookmark();
return true;
}
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey() && NEIClientUtils.shiftKey()) {
sendRecipeChatLink();
return true;
}
return false;
}
public void saveRecipeInBookmark() {
@@ -51,6 +51,7 @@ import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.guihook.IGuiClientSide;
import codechicken.nei.guihook.IGuiHandleMouseWheel;
import codechicken.nei.guihook.IGuiHotkeyListener;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.widget.RecipeWidget;
import codechicken.nei.scroll.ScrollBar;
@@ -62,7 +63,7 @@ import codechicken.nei.util.EmptyContainer;
import codechicken.nei.util.SlotInaccessible;
public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer implements IGuiContainerOverlay,
IGuiClientSide, IGuiHandleMouseWheel, IContainerTooltipHandler, INEIGuiHandler {
IGuiClientSide, IGuiHandleMouseWheel, IGuiHotkeyListener, IContainerTooltipHandler, INEIGuiHandler {
private static final int BORDER_PADDING = 5;
private static final int TRANSPARENCY_BORDER = 4;
@@ -71,15 +72,23 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
0,
0,
176 + TRANSPARENCY_BORDER * 2,
166 + TRANSPARENCY_BORDER * 2).build();
166 + TRANSPARENCY_BORDER * 2)
.setSlices(
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER)
.build();
private static final int BUTTON_WIDTH = 13;
private static final int BUTTON_HEIGHT = 12;
protected static final ScrollBar VERTICAL_SCROLLBAR = new ScrollBar().setTrackWidth(14)
.setOverflowType(OverflowType.AUTO).setScrollPlace(ScrollPlace.END)
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).build(), 9, 9)
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).build(), 3, 2)
.setTrackTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).setSlices(0, 0, 9, 9).build())
.setThumbTexture(
new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).setSlices(0, 0, 3, 2).build())
.setThumbPadding(1, 6, 0, 6);
public static final List<IRecipeFilterProvider> recipeFilterers = new LinkedList<>();
@@ -191,6 +200,22 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
"recipe.search.tooltip.output",
SearchExpressionUtils.HIGHLIGHTS.RECIPE + ">"));
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode != 2) {
tooltip.add(
NEIClientUtils.translate(
"recipe.search.tooltip.auxiliary",
SearchExpressionUtils.HIGHLIGHTS.RECIPE + "^"));
}
if (patternMode == 3) {
tooltip.add(
NEIClientUtils.translate(
"recipe.search.tooltip.all",
SearchExpressionUtils.HIGHLIGHTS.RECIPE + "<&"));
}
for (ISearchParserProvider provider : SearchField.searchParser.getProviders()) {
if (provider.getSearchMode() == SearchMode.PREFIX) {
addPrefixTooltip(tooltip, provider);
@@ -213,6 +238,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
"recipe.search.tooltip.exact",
SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"",
SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\""));
if (patternMode == 1 || patternMode == 3) {
tooltip.add(
NEIClientUtils.translate(
"recipe.search.tooltip.regex",
SearchExpressionUtils.HIGHLIGHTS.REGEX + "r/",
SearchExpressionUtils.HIGHLIGHTS.REGEX + "/"));
}
} else {
tooltip.add(NEIClientUtils.translate("recipe.search.tooltip.shift"));
}
@@ -673,6 +706,10 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
return this.handlerPages.getRecipeIndices();
}
public List<Integer> getFilteredRecipes() {
return this.handler.getFilteredRecipes();
}
@Override
public void keyTyped(char c, int i) {
@@ -689,6 +726,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.focused()) {
GuiRecipe.searchField.lastKeyTyped(i, c);
GuiRecipe.searchField.onHotkeyPressed();
return;
}
@@ -713,7 +751,24 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
if (i == mc.gameSettings.keyBindInventory.getKeyCode()) {
this.mc.displayGuiScreen(getFirstScreenGeneral());
NEICPH.sendRequestContainer();
} else if (KeyManager.isKeyDown("recipe.back")) {
}
}
@Override
public boolean onHotkeyPressed() {
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.onHotkeyPressed()
|| this.recipeCatalyst.isShowWidget() && this.recipeCatalyst.onHotkeyPressed()
|| this.container.onHotkeyPressed()) {
return true;
}
return false;
}
@Override
public boolean onScreenHotkeyPressed() {
if (KeyManager.isKeyDown("recipe.back")) {
this.mc.displayGuiScreen(this.prevGui);
} else if (KeyManager.isKeyDown("recipe.prev_machine")) {
prevType();
@@ -723,7 +778,11 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
prevPage();
} else if (KeyManager.isKeyDown("recipe.next_recipe")) {
nextPage();
} else {
return false;
}
return true;
}
@Override
@@ -958,11 +1017,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.guiLeft - TRANSPARENCY_BORDER,
this.guiTop - TRANSPARENCY_BORDER,
this.xSize + TRANSPARENCY_BORDER * 2,
this.ySize + TRANSPARENCY_BORDER * 2,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER);
this.ySize + TRANSPARENCY_BORDER * 2);
drawJEITabs(mouseX, mouseY);
}
@@ -64,6 +64,10 @@ public abstract class GuiRecipeButton extends GuiNEIButton {
public abstract void lastKeyTyped(char keyChar, int keyID);
public boolean onHotkeyPressed() {
return false;
}
public abstract void drawItemOverlay();
public void update() {
@@ -55,11 +55,17 @@ public class GuiRecipeCatalyst extends ScrollContainer {
0,
0,
28 + TRANSPARENCY_BORDER * 2,
28 + TRANSPARENCY_BORDER * 2).setTextureSize(28 + TRANSPARENCY_BORDER * 2, 28 + TRANSPARENCY_BORDER * 2)
28 + TRANSPARENCY_BORDER * 2)
.setTextureSize(28 + TRANSPARENCY_BORDER * 2, 28 + TRANSPARENCY_BORDER * 2)
.setSlices(
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER)
.build();
private static final DrawableResource FG_TEXTURE = new DrawableBuilder("nei:textures/slot.png", 0, 0, 18, 18)
.setTextureSize(18, 18).build();
.setTextureSize(18, 18).setSlices(1, 1, 1, 1).build();
private static final ScrollBar VERTICAL_SCROLLBAR = ScrollBar.defaultVerticalBar()
.setOverflowType(ScrollBar.OverflowType.AUTO).setTrackPadding(0, BORDER_PADDING, -13, BORDER_PADDING)
@@ -155,20 +161,12 @@ public class GuiRecipeCatalyst extends ScrollContainer {
this.x - TRANSPARENCY_BORDER,
this.y - TRANSPARENCY_BORDER,
this.w + TRANSPARENCY_BORDER * 2,
this.h + TRANSPARENCY_BORDER * 2,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER,
BORDER_PADDING + TRANSPARENCY_BORDER);
this.h + TRANSPARENCY_BORDER * 2);
FG_TEXTURE.draw(
this.x + this.paddingInlineStart - 1,
this.y + this.paddingBlockStart - 1,
this.w - this.paddingInlineStart - this.paddingInlineEnd + 2,
this.h - this.paddingBlockStart - this.paddingBlockEnd + 2,
1,
1,
1,
1);
this.h - this.paddingBlockStart - this.paddingBlockEnd + 2);
super.draw(mx, my);
}
@@ -1,15 +1,10 @@
package codechicken.nei.recipe;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -31,6 +26,7 @@ import codechicken.nei.ColorUtils;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.Widget;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.event.NEIRegisterHandlerInfosEvent;
import codechicken.nei.guihook.GuiContainerManager;
@@ -48,26 +44,29 @@ public abstract class GuiRecipeTab extends Widget {
protected final String handlerName;
protected final String handlerID;
public static final int TAB_WIDTH = 24;
public static final int TAB_HEIGHT = 24;
private static final DrawableResource selectedTabImage = new DrawableBuilder(
"nei:textures/nei_tabbed_sprites.png",
0,
16,
24,
24).build();
private static final DrawableResource unselectedTabImage = new DrawableBuilder(
"nei:textures/nei_tabbed_sprites.png",
24,
16,
24,
24).build();
private boolean selected;
public abstract int getWidth();
public abstract int getHeight();
public abstract DrawableResource getSelectedTabImage();
public abstract DrawableResource getUnselectedTabImage();
protected abstract int getForegroundIconX();
protected abstract int getForegroundIconY();
public GuiRecipeTab(IRecipeHandler handler, int x, int y) {
super();
this.x = x;
this.y = y;
this.w = getWidth();
this.h = getHeight();
this.w = TAB_WIDTH;
this.h = TAB_HEIGHT;
this.handler = handler;
this.handlerName = handler.getHandlerId();
this.selected = false;
@@ -88,21 +87,16 @@ public abstract class GuiRecipeTab extends Widget {
public void drawBackground(int mouseX, int mouseY) {
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glColor4f(1, 1, 1, 1);
final DrawableResource image;
if (selected) image = getSelectedTabImage();
else image = getUnselectedTabImage();
final int iconX = x + (w - image.getWidth()) / 2;
final int iconY = y + (h - image.getHeight()) / 2;
image.draw(iconX, iconY);
final DrawableResource image = this.selected ? selectedTabImage : unselectedTabImage;
image.draw(this.x, this.y);
}
public void drawForeground(int mouseX, int mouseY) {
final int iconX = getForegroundIconX();
final int iconY = getForegroundIconY();
final int iconX = this.x + 4;
final int iconY = this.y + 4;
final FontRenderer fontRenderer = GuiDraw.fontRenderer;
final HandlerInfo handlerInfo = getHandlerInfo(handlerName, handlerID);
final HandlerInfo handlerInfo = getHandlerInfo(this.handlerName, this.handlerID);
final DrawableResource icon = handlerInfo != null ? handlerInfo.getImage() : null;
final ItemStack itemStack = handlerInfo != null ? handlerInfo.getItemStack() : null;
@@ -117,7 +111,7 @@ public abstract class GuiRecipeTab extends Widget {
if (!isEnabled) GL11.glDisable(GL12.GL_RESCALE_NORMAL);
} else {
// Text fallback
String text = handler.getRecipeName();
String text = this.handler.getRecipeName();
if (text != null && text.length() > 0) {
if (text.length() > 2) {
text = text.substring(0, 2);
@@ -126,9 +120,10 @@ public abstract class GuiRecipeTab extends Widget {
text = "??";
}
int textCenterX = x + (int) (getWidth() / 2f);
int textCenterY = y + (int) (getHeight() / 2f) - 3;
int color = selected ? ColorUtils.buttonLabelHover.getColor() : ColorUtils.buttonLabelNormal.getColor();
int textCenterX = this.x + (int) (this.w / 2f);
int textCenterY = this.y + (int) (this.h / 2f) - 3;
int color = this.selected ? ColorUtils.buttonLabelHover.getColor()
: ColorUtils.buttonLabelNormal.getColor();
fontRenderer.drawStringWithShadow(
text,
textCenterX - (int) (fontRenderer.getStringWidth(text) / 2f),
@@ -207,41 +202,27 @@ public abstract class GuiRecipeTab extends Widget {
}
public static void loadHandlerInfo() {
final boolean fromJar = NEIClientConfig.loadHandlersFromJar();
final boolean fromJar = !NEIClientConfig.handlerFile.exists();
NEIClientConfig.logger.info("Loading handler info from " + (fromJar ? "JAR" : "Config"));
handlerMap.clear();
URL handlerUrl = GuiRecipeTab.class.getResource("/assets/nei/csv/handlers.csv");
URL url;
if (fromJar) {
url = handlerUrl;
if (url == null) {
NEIClientConfig.logger.info("Invalid URL for handlers csv.");
return;
}
} else {
File handlerFile = NEIClientConfig.handlerFile;
if (!handlerFile.exists()) {
NEIClientConfig.logger.info("Config file doesn't exist, creating");
try (FileOutputStream fileOutputStream = new FileOutputStream(handlerFile.getAbsoluteFile())) {
assert handlerUrl != null;
ReadableByteChannel readableByteChannel = Channels.newChannel(handlerUrl.openStream());
FileChannel fileChannel = fileOutputStream.getChannel();
fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
} catch (IOException e) {
e.printStackTrace();
return;
}
}
try {
url = NEIClientConfig.handlerFile.toURI().toURL();
} catch (MalformedURLException e) {
NEIClientConfig.logger.info("Invalid URL for handlers csv (via config).");
e.printStackTrace();
return;
}
try {
url = fromJar ? GuiRecipeTab.class.getResource("/assets/nei/csv/handlers.csv")
: NEIClientConfig.handlerFile.toURI().toURL();
} catch (MalformedURLException e) {
NEIClientConfig.logger.info("Invalid URL for handlers csv (via config).");
e.printStackTrace();
return;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
if (url == null) {
NEIClientConfig.logger.info("Invalid URL for handlers csv.");
return;
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
CSVParser csvParser = CSVFormat.EXCEL.builder().setHeader().setSkipHeaderRecord(true).build().parse(reader);
for (CSVRecord record : csvParser) {
final String handler = record.get("handler");
@@ -282,13 +263,12 @@ public abstract class GuiRecipeTab extends Widget {
NEIClientConfig.logger.info("Error setting handler dimensions for " + handler);
}
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);
final boolean allowOverflow = booleanOrDefault(record, "useCustomScroll", false);
info.setAllowOverflowX(booleanOrDefault(record, "allowOverflowX", true));
info.setAllowOverflowY(booleanOrDefault(record, "allowOverflowY", allowOverflow));
info.setShowFavoritesButton(booleanOrDefault(record, "showFavorites", true));
info.setShowOverlayButton(booleanOrDefault(record, "showOverlay", true));
info.setShowBadge(booleanOrDefault(record, "showBadge", false));
handlerMap.put(handler, info);
NEIClientConfig.logger.info("Loaded " + handler);
}
@@ -325,6 +305,11 @@ public abstract class GuiRecipeTab extends Widget {
return info;
}
private static boolean booleanOrDefault(CSVRecord record, String name, boolean defaultValue) {
final String str = record.isSet(name) ? record.get(name) : null;
return str == null || str.isEmpty() ? defaultValue : Boolean.parseBoolean(str);
}
private static int intOrDefault(String str, int defaultValue) {
if (str == null || str.equals("")) return defaultValue;
try {
@@ -1,56 +0,0 @@
package codechicken.nei.recipe;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
public abstract class GuiRecipeTabCreative extends GuiRecipeTab {
public static final int TAB_WIDTH = 28;
public static final int TAB_HEIGHT = 31;
private static final DrawableResource selectedTabImage = new DrawableBuilder(
"minecraft:textures/gui/container/creative_inventory/tabs.png",
28,
32,
28,
32).build();
private static final DrawableResource unselectedTabImage = new DrawableBuilder(
"minecraft:textures/gui/container/creative_inventory/tabs.png",
28,
0,
28,
30).build();
public GuiRecipeTabCreative(IRecipeHandler handler, int x, int y) {
super(handler, x, y);
}
@Override
public int getWidth() {
return TAB_WIDTH;
}
@Override
public int getHeight() {
return TAB_HEIGHT;
}
@Override
protected int getForegroundIconX() {
return x + (w - 16) / 2;
}
@Override
protected int getForegroundIconY() {
return y + h - 22;
}
@Override
public DrawableResource getSelectedTabImage() {
return selectedTabImage;
}
@Override
public DrawableResource getUnselectedTabImage() {
return unselectedTabImage;
}
}
@@ -1,56 +0,0 @@
package codechicken.nei.recipe;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
public abstract class GuiRecipeTabJEI extends GuiRecipeTab {
public static final int TAB_WIDTH = 24;
public static final int TAB_HEIGHT = 24;
private static final DrawableResource selectedTabImage = new DrawableBuilder(
"nei:textures/nei_tabbed_sprites.png",
0,
16,
24,
24).build();
private static final DrawableResource unselectedTabImage = new DrawableBuilder(
"nei:textures/nei_tabbed_sprites.png",
24,
16,
24,
24).build();
public GuiRecipeTabJEI(IRecipeHandler handler, int x, int y) {
super(handler, x, y);
}
@Override
public int getWidth() {
return TAB_WIDTH;
}
@Override
public int getHeight() {
return TAB_HEIGHT;
}
@Override
protected int getForegroundIconX() {
return x + 4;
}
@Override
protected int getForegroundIconY() {
return y + 4;
}
@Override
public DrawableResource getSelectedTabImage() {
return selectedTabImage;
}
@Override
public DrawableResource getUnselectedTabImage() {
return unselectedTabImage;
}
}
@@ -47,11 +47,11 @@ public class GuiRecipeTabs {
};
public static int getTabWidth() {
return NEIClientConfig.useCreativeTabStyle() ? GuiRecipeTabCreative.TAB_WIDTH : GuiRecipeTabJEI.TAB_WIDTH;
return GuiRecipeTab.TAB_WIDTH;
}
public static int getTabHeight() {
return NEIClientConfig.useCreativeTabStyle() ? GuiRecipeTabCreative.TAB_HEIGHT : GuiRecipeTabJEI.TAB_HEIGHT;
return GuiRecipeTab.TAB_HEIGHT;
}
public void update(GuiRecipe<?> guiRecipe) {
@@ -85,27 +85,15 @@ public class GuiRecipeTabs {
final IRecipeHandler handler = this.currenthandlers.get(index);
final int tabX = this.area.x + (i * tabWidth);
if (NEIClientConfig.useCreativeTabStyle()) {
tabs.add(new GuiRecipeTabCreative(handler, tabX, this.area.y) {
this.tabs.add(new GuiRecipeTab(handler, tabX, this.area.y) {
@Override
public boolean onButtonPress(boolean rightclick) {
setRecipePage(index);
return true;
}
@Override
public boolean onButtonPress(boolean rightclick) {
setRecipePage(index);
return true;
}
});
} else {
tabs.add(new GuiRecipeTabJEI(handler, tabX, this.area.y) {
@Override
public boolean onButtonPress(boolean rightclick) {
setRecipePage(index);
return true;
}
});
}
});
}
}
@@ -12,6 +12,7 @@ public class HandlerInfo {
public static int DEFAULT_HEIGHT = 65;
public static int DEFAULT_WIDTH = 166;
public static final int DEFAULT_IMAGE_TEXTURE_SIZE = 256;
@Deprecated
public static int DEFAULT_MAX_PER_PAGE = 1;
@@ -70,9 +71,22 @@ public class HandlerInfo {
}
public boolean setImage(String resourceLocation, int imageX, int imageY, int imageWidth, int imageHeight) {
return setImage(
resourceLocation,
imageX,
imageY,
imageWidth,
imageHeight,
DEFAULT_IMAGE_TEXTURE_SIZE,
DEFAULT_IMAGE_TEXTURE_SIZE);
}
public boolean setImage(String resourceLocation, int imageX, int imageY, int imageWidth, int imageHeight,
int textureWidth, int textureHeight) {
if (hasImageOrItem()) return false;
this.image = new DrawableBuilder(resourceLocation, imageX, imageY, imageWidth, imageHeight).build();
this.image = new DrawableBuilder(resourceLocation, imageX, imageY, imageWidth, imageHeight)
.setTextureSize(textureWidth, textureHeight).build();
return true;
}
@@ -211,6 +225,20 @@ public class HandlerInfo {
return this;
}
public Builder setDisplayImage(ResourceLocation location, int imageX, int imageY, int imageWidth,
int imageHeight, int textureWidth, int textureHeight) {
this.info.itemStack = null;
this.info.setImage(
location.toString(),
imageX,
imageY,
imageWidth,
imageHeight,
textureWidth,
textureHeight);
return this;
}
@Deprecated
public Builder setUseCustomScroll(boolean useCustomScroll) {
this.info.setUseCustomScroll(useCustomScroll);
@@ -1,5 +1,6 @@
package codechicken.nei.recipe;
import java.util.Collections;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
@@ -77,19 +78,48 @@ public interface IRecipeHandler {
List<PositionedStack> getIngredientStacks(int recipe);
/**
* Callers should read {@link #getResultStacks(int)} and {@link #getAuxiliaryStacks(int)} instead.
*
* @param recipe The recipe index to get items for.
* @return A list of the other {@link PositionedStack}s in this recipe relative to the top left corner of your
* recipe drawing space. For example fuel in furnaces.
* @return The fuel or catalysts when {@link #getResultStack(int)} is present, otherwise all the results of the
* recipe. Relative to the top left corner of your recipe drawing space.
*/
List<PositionedStack> getOtherStacks(int recipe);
default List<PositionedStack> getOtherStacks(int recipe) {
return Collections.emptyList();
}
/**
* Callers should read {@link #getResultStacks(int)} instead.
*
* @param recipe The recipe index to get the result for.
* @return The recipe result {@link PositionedStack} relative to the top left corner of your recipe drawing space.
* @return The single result, or {@code null} when the recipe has several results and returns them from
* {@link #getOtherStacks(int)}. Relative to the top left corner of your recipe drawing space.
*/
PositionedStack getResultStack(int recipe);
default PositionedStack getResultStack(int recipe) {
return null;
}
/**
* Built from {@link #getResultStack(int)} and {@link #getOtherStacks(int)}. Don't override it, return the results
* from those methods so mods that still read them see the same results.
*
* @return The results: the result stack when present, otherwise the other stacks.
*/
default List<PositionedStack> getResultStacks(int recipe) {
final PositionedStack result = getResultStack(recipe);
return result != null ? Collections.singletonList(result) : getOtherStacks(recipe);
}
/**
* Override it only when the recipe has several results and also fuel or catalysts: the results then go to
* {@link #getOtherStacks(int)} and the catalysts can't be returned from the old methods.
*
* @return The stacks that are neither ingredients nor results (fuel, catalysts): the other stacks when a result
* stack is present.
*/
default List<PositionedStack> getAuxiliaryStacks(int recipe) {
return getResultStack(recipe) != null ? getOtherStacks(recipe) : Collections.emptyList();
}
/**
* @param recipe The recipe index to get the group id for.
@@ -7,6 +7,7 @@ import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.item.Item;
@@ -65,19 +66,11 @@ public class Recipe {
public static RecipeId of(IRecipeHandler handler, int recipeIndex, List<PositionedStack> ingredients) {
final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
PositionedStack pStackResult = handler.getResultStack(recipeIndex);
final PositionedStack result = getMainResult(
handler.getResultStacks(recipeIndex),
pStack -> pStack.items[0]);
if (pStackResult == null) {
for (PositionedStack otherStack : handler.getOtherStacks(recipeIndex)) {
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|| StackInfo.getFluid(otherStack.items[0]) != null) {
pStackResult = otherStack;
break;
}
}
}
return new RecipeId(extractItem(pStackResult), handlerName, extractIngredients(ingredients));
return new RecipeId(extractItem(result), handlerName, extractIngredients(ingredients));
}
public static RecipeId of(JsonObject json) {
@@ -404,12 +397,8 @@ public class Recipe {
ingredients.add(RecipeIngredient.of(positionedStack));
}
if (handler.getResultStack(recipeIndex) != null) {
results.add(RecipeIngredient.of(handler.getResultStack(recipeIndex)));
} else {
for (PositionedStack positionedStack : handler.getOtherStacks(recipeIndex)) {
results.add(RecipeIngredient.of(positionedStack));
}
for (PositionedStack positionedStack : handler.getResultStacks(recipeIndex)) {
results.add(RecipeIngredient.of(positionedStack));
}
return new Recipe(handlerName, ingredients, results);
@@ -494,18 +483,8 @@ public class Recipe {
return null;
}
if (stack == null && this.results.length > 1) {
for (RecipeIngredient ingr : this.results) {
if (!FluidContainerRegistry.isContainer(ingr.getItemStack())
|| StackInfo.getFluid(ingr.getItemStack()) != null) {
stack = ingr.getItemStack();
break;
}
}
}
if (stack == null) {
stack = this.results[0].getItemStack();
stack = getMainResult(getResults(), RecipeIngredient::getItemStack).getItemStack();
}
int stackSize = 0;
@@ -519,6 +498,19 @@ public class Recipe {
return StackInfo.withAmount(stack, stackSize);
}
public static <T> T getMainResult(List<T> results, Function<T, ItemStack> stackOf) {
for (T result : results) {
final ItemStack stack = stackOf.apply(result);
if (!FluidContainerRegistry.isContainer(stack) || StackInfo.getFluid(stack) != null) {
return result;
}
}
return results.isEmpty() ? null : results.get(0);
}
public RecipeId getRecipeId() {
if (this.recipeId == null) {
@@ -2,14 +2,10 @@ package codechicken.nei.recipe;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -97,43 +93,30 @@ public class RecipeCatalysts {
}
public static void loadCatalystInfo() {
final boolean fromJar = NEIClientConfig.loadCatalystsFromJar();
final URL handlerUrl = RecipeCatalysts.class.getResource("/assets/nei/csv/catalysts.csv");
final File catalystFile = NEIClientConfig.catalystFile;
final boolean fromJar = !catalystFile.exists();
final Map<String, CatalystInfoList> calculatedCatalysts = new HashMap<>();
NEIClientConfig.logger.info("Loading catalyst info from " + (fromJar ? "JAR" : "Config"));
resolveAdderQueue(calculatedCatalysts);
URL url;
if (fromJar) {
url = handlerUrl;
if (url == null) {
NEIClientConfig.logger.warn("Invalid URL for catalysts csv.");
return;
}
} else {
File catalystFile = NEIClientConfig.catalystFile;
if (!catalystFile.exists()) {
NEIClientConfig.logger.info("Config file doesn't exist, creating");
try (FileOutputStream fileOutputStream = new FileOutputStream(catalystFile.getAbsoluteFile())) {
assert handlerUrl != null;
ReadableByteChannel readableByteChannel = Channels.newChannel(handlerUrl.openStream());
FileChannel fileChannel = fileOutputStream.getChannel();
fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
} catch (IOException e) {
e.printStackTrace();
return;
}
}
try {
url = NEIClientConfig.catalystFile.toURI().toURL();
} catch (MalformedURLException e) {
NEIClientConfig.logger.warn("Invalid URL for catalysts csv (via config).");
e.printStackTrace();
return;
}
final URL url;
try {
url = fromJar ? RecipeCatalysts.class.getResource("/assets/nei/csv/catalysts.csv")
: catalystFile.toURI().toURL();
} catch (MalformedURLException e) {
NEIClientConfig.logger.warn("Invalid URL for catalysts csv (via config).");
e.printStackTrace();
return;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
if (url == null) {
NEIClientConfig.logger.warn("Invalid URL for catalysts csv.");
return;
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
CSVParser csvParser = CSVFormat.EXCEL.builder().setHeader().setSkipHeaderRecord(true).build().parse(reader);
for (CSVRecord record : csvParser) {
final String handler = record.get("handler");
@@ -141,10 +124,6 @@ public class RecipeCatalysts {
final boolean requiresMod = Boolean.parseBoolean(record.get("modRequired"));
final String excludedModId = record.get("excludedModId");
final int priority = intOrDefault(record.get("priority"), 0);
// todo
final String minVersion = record.get("minVersion");
final String maxVersion = record.get("maxVersion");
//
final boolean forceClassName = Boolean.parseBoolean(record.get("forceClassName"));
if (requiresMod && !Loader.isModLoaded(modId)) continue;
@@ -1,5 +1,6 @@
package codechicken.nei.recipe;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
@@ -167,6 +168,8 @@ public class RecipeHandlerRef {
return transferAlghoritmCache.computeIfAbsent(overlayHandler.getClass(), clazz -> {
try {
final Method transferRecipe = overlayHandler.getClass()
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class);
if (overlayHandler instanceof DefaultOverlayHandler) {
final Class<?> overlayRecipeClazz = overlayHandler.getClass()
@@ -178,14 +181,11 @@ public class RecipeHandlerRef {
boolean.class)
.getDeclaringClass();
if (overlayRecipeClazz.equals(DefaultOverlayHandler.class)) {
return true;
}
return overlayRecipeClazz.equals(DefaultOverlayHandler.class)
|| !transferRecipe.getDeclaringClass().equals(DefaultOverlayHandler.class);
}
return !overlayHandler.getClass()
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class)
.isDefault();
return !transferRecipe.isDefault();
} catch (NoSuchMethodException e) {}
return false;
@@ -19,16 +19,30 @@ import codechicken.nei.recipe.Recipe.RecipeId;
public class RecipeItemInputHandler implements IContainerInputHandler, IContainerTooltipHandler {
public static final RecipeItemInputHandler instance = new RecipeItemInputHandler();
protected RecipeTooltipLineHandler recipeTooltipLineHandler = null;
protected RecipeItemInputHandler() {
}
public static void load() {
RecipeItemInputHandler recipeHandler = new RecipeItemInputHandler();
GuiContainerManager.addInputHandler(recipeHandler);
GuiContainerManager.addTooltipHandler(recipeHandler);
GuiContainerManager.addInputHandler(instance);
GuiContainerManager.addTooltipHandler(instance);
}
public void clearTooltipCache() {
this.recipeTooltipLineHandler = null;
}
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
return ShortcutInputHandler.handleKeyEvent(GuiContainerManager.getStackMouseOver(gui));
}
@@ -59,16 +73,17 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
: null;
if (recipeId == null) {
recipeTooltipLineHandler = null;
} else if (recipeTooltipLineHandler == null || !recipeTooltipLineHandler.getRecipeId().equals(recipeId)) {
recipeTooltipLineHandler = new RecipeTooltipLineHandler(recipeId);
}
clearTooltipCache();
} else if (this.recipeTooltipLineHandler == null
|| !this.recipeTooltipLineHandler.getRecipeId().equals(recipeId)) {
this.recipeTooltipLineHandler = new RecipeTooltipLineHandler(recipeId);
}
if (recipeTooltipLineHandler != null) {
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(recipeTooltipLineHandler));
if (this.recipeTooltipLineHandler != null) {
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeTooltipLineHandler));
}
} else {
recipeTooltipLineHandler = null;
clearTooltipCache();
}
return currenttip;
@@ -0,0 +1,303 @@
package codechicken.nei.recipe;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import net.minecraft.event.ClickEvent;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import com.google.common.base.Stopwatch;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import codechicken.core.CommonUtils;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.config.GuiItemIconDumper;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.util.NBTJson;
import cpw.mods.fml.relauncher.ReflectionHelper;
/**
* Writes recipes as JSON, one object per line, with stacks in the format of {@link StackInfo#itemStackToNBT}.
*/
public class RecipeJsonWriter {
protected static Field mEUt = null;
protected static Field mDuration = null;
protected static Field mRecipe = null;
static {
try {
final ClassLoader loader = RecipeJsonWriter.class.getClassLoader();
final Class<?> gtRecipe = ReflectionHelper
.getClass(loader, "gregtech.api.util.GTRecipe", "gregtech.api.util.GT_Recipe");
final Class<?> cachedDefaultRecipe = ReflectionHelper.getClass(
loader,
"gregtech.nei.GTNEIDefaultHandler$CachedDefaultRecipe",
"gregtech.nei.GT_NEI_DefaultHandler$CachedDefaultRecipe");
mEUt = gtRecipe.getField("mEUt");
mDuration = gtRecipe.getField("mDuration");
mRecipe = cachedDefaultRecipe.getField("mRecipe");
} catch (Exception ignored) {}
}
private static final File DUMP_DIR = new File(CommonUtils.getMinecraftDir(), "dumps/recipes");
// display names by stack NBT without Count, built once per dump
private final Map<String, String> names = new HashMap<>();
private int dumped = 0;
private int skipped = 0;
public static void dump(IRecipeHandler handler, List<Integer> recipeIndices) {
final RecipeJsonWriter dumper = new RecipeJsonWriter();
final File file = new File(DUMP_DIR, fileName(handler) + ".jsonl");
try {
final Stopwatch stopwatch = Stopwatch.createStarted();
dumper.writeFile(file, Collections.singletonList(handler), h -> recipeIndices);
NEIClientConfig.logger.info("Writing " + dumper.dumped + " recipes took " + stopwatch.stop());
dumper.printDumped(file);
} catch (Exception e) {
printFailed(e);
}
}
/**
* Loads all recipes of every crafting handler and writes them either all to {@code dumps/recipes.jsonl} or each
* handler to its own file in {@code dumps/recipes}.
*/
public static void dumpAll(boolean separate) {
final RecipeJsonWriter dumper = new RecipeJsonWriter();
try {
final Stopwatch stopwatch = Stopwatch.createStarted();
final List<ICraftingHandler> handlers = GuiCraftingRecipe.getCraftingHandlers("all");
NEIClientConfig.logger.info("Loading " + handlers.size() + " recipe handlers took " + stopwatch.stop());
stopwatch.reset().start();
if (separate) {
if (DUMP_DIR.exists()) {
for (File f : DUMP_DIR.listFiles()) if (f.isFile()) f.delete();
} else DUMP_DIR.mkdirs();
for (ICraftingHandler handler : handlers) {
final String name = fileName(handler);
File file = new File(DUMP_DIR, name + ".jsonl");
for (int i = 2; file.exists(); i++) file = new File(DUMP_DIR, name + '_' + i + ".jsonl");
dumper.writeFile(file, Collections.singletonList(handler), RecipeJsonWriter::filteredRecipes);
}
NEIClientConfig.logger.info("Writing " + dumper.dumped + " recipes took " + stopwatch.stop());
dumper.printDumped(DUMP_DIR);
} else {
final File file = new File(CommonUtils.getMinecraftDir(), "dumps/recipes.jsonl");
dumper.writeFile(file, handlers, RecipeJsonWriter::filteredRecipes);
NEIClientConfig.logger.info("Writing " + dumper.dumped + " recipes took " + stopwatch.stop());
dumper.printDumped(file);
}
} catch (Exception e) {
printFailed(e);
}
}
private static String fileName(IRecipeHandler handler) {
return GuiItemIconDumper
.cleanFileName(EnumChatFormatting.getTextWithoutFormattingCodes(handler.getRecipeName()).trim());
}
private static List<Integer> filteredRecipes(IRecipeHandler handler) {
return new SearchRecipeHandler<>(handler).getFilteredRecipes();
}
private void writeFile(File file, List<? extends IRecipeHandler> handlers,
Function<IRecipeHandler, List<Integer>> recipeIndices) throws IOException {
file.getParentFile().mkdirs();
try (PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8.name())) {
for (IRecipeHandler handler : handlers) {
write(writer, handler, recipeIndices.apply(handler));
}
}
}
private void printDumped(File file) {
final Path minecraftDir = CommonUtils.getMinecraftDir().getAbsoluteFile().toPath().normalize();
final String path = minecraftDir.relativize(file.getAbsoluteFile().toPath().normalize()).toString()
.replace('\\', '/');
final ChatComponentText chatLink = new ChatComponentText(path);
chatLink.getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, file.getAbsolutePath()));
chatLink.getChatStyle().setUnderlined(true);
NEIClientUtils.printChatMessage(
new ChatComponentTranslation("nei.options.tools.dump.recipes.dumped", this.dumped, chatLink));
if (this.skipped > 0) {
NEIClientUtils.printChatMessage(
new ChatComponentTranslation("nei.options.tools.dump.recipes.skipped", this.skipped));
}
}
private static void printFailed(Exception e) {
NEIClientConfig.logger.error("Error dumping recipes", e);
NEIClientUtils.printChatMessage(
new ChatComponentTranslation("nei.options.tools.dump.recipes.failed", e.getMessage()));
}
private void write(PrintWriter writer, IRecipeHandler handler, List<Integer> recipeIndices) {
for (int recipeIndex : recipeIndices) {
final String line;
try {
line = recipeJson(handler, recipeIndex);
} catch (Exception e) {
NEIClientConfig.logger.warn("Skipping recipe {} of {}", recipeIndex, handler.getRecipeName(), e);
this.skipped++;
continue;
}
writer.println(line);
this.dumped++;
}
}
private String recipeJson(IRecipeHandler handler, int recipeIndex) throws IllegalAccessException {
final Recipe recipe = Recipe.of(handler, recipeIndex);
final JsonObject json = new JsonObject();
json.addProperty("handler", recipe.getHandlerName());
json.add("ingredients", slotsJson(recipe.getIngredients(), handler instanceof ShapedRecipeHandler));
json.add("results", slotsJson(recipe.getResults(), false));
final JsonObject gt = gtJson(handler, recipeIndex);
if (gt != null) {
json.add("gt", gt);
}
return NBTJson.toJson(json);
}
/**
* Each slot's shown stack, with {@code anyOf} listing every accepted stack. In a crafting grid, an ingredient's
* container item (an empty bucket, a damaged tool) goes under {@code returns}.
*/
private JsonArray slotsJson(List<RecipeIngredient> slots, boolean craftingGrid) {
final JsonArray array = new JsonArray();
for (RecipeIngredient slot : slots) {
final ItemStack stack = slot.getItemStack();
final JsonObject json = stackJson(stack);
if (json == null) {
continue;
}
if (slot.getChance() != PositionedStack.CHANCE_FULL) {
json.addProperty("chance", slot.getChance());
}
if (slot.getPermutations().size() > 1) {
final JsonArray anyOf = new JsonArray();
for (ItemStack permutation : slot.getPermutations()) {
final JsonObject option = stackJson(permutation);
if (option != null) {
option.remove("Count");
anyOf.add(option);
}
}
json.add("anyOf", anyOf);
}
if (craftingGrid) {
final JsonObject container = stackJson(containerItem(stack));
if (container != null) {
json.add("returns", container);
}
}
array.add(json);
}
return array;
}
private JsonObject stackJson(ItemStack stack) {
final NBTTagCompound nbTag = stack != null ? StackInfo.itemStackToNBT(stack) : null;
if (nbTag == null) {
return null;
}
final NBTBase count = nbTag.getTag("Count");
nbTag.removeTag("Count");
final String name = this.names.computeIfAbsent(
nbTag.toString(),
key -> EnumChatFormatting
.getTextWithoutFormattingCodes(GuiContainerManager.itemDisplayNameShort(stack)));
if (count != null) {
nbTag.setTag("Count", count);
}
final JsonObject json = NBTJson.toJsonObject(nbTag).getAsJsonObject();
json.addProperty("name", name);
return json;
}
private static ItemStack containerItem(ItemStack stack) {
if (!stack.getItem().hasContainerItem(stack)) {
return null;
}
final boolean isPausedItemDamageSound = StackInfo.isPausedItemDamageSound();
try {
StackInfo.pauseItemDamageSound(true);
return stack.getItem().getContainerItem(stack);
} finally {
StackInfo.pauseItemDamageSound(isPausedItemDamageSound);
}
}
private static JsonObject gtJson(IRecipeHandler handler, int recipeIndex) throws IllegalAccessException {
if (mRecipe == null || !(handler instanceof TemplateRecipeHandler templateHandler)
|| recipeIndex >= templateHandler.arecipes.size()) {
return null;
}
final Object cachedRecipe = templateHandler.arecipes.get(recipeIndex);
if (!mRecipe.getDeclaringClass().isInstance(cachedRecipe)) {
return null;
}
final Object gtRecipe = mRecipe.get(cachedRecipe);
final long eut = ((Number) mEUt.get(gtRecipe)).longValue();
final long duration = ((Number) mDuration.get(gtRecipe)).longValue();
final JsonObject gt = new JsonObject();
gt.addProperty("eut", eut);
gt.addProperty("durationTicks", duration);
gt.addProperty("totalEU", eut * duration);
return gt;
}
}
@@ -25,11 +25,16 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
0,
0,
176 + TRANSPARENCY_BORDER * 2,
166 + TRANSPARENCY_BORDER * 2).build();
166 + TRANSPARENCY_BORDER * 2)
.setSlices(
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER)
.build();
protected long lastUpdate = System.currentTimeMillis();
protected String recipeName = "";
protected GuiRecipe<?> gui = null;
protected RecipeHandlerRef handlerRef = null;
protected RecipeWidget widget = null;
protected RecipeId recipeId = null;
@@ -105,11 +110,7 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
-TRANSPARENCY_BORDER,
-TRANSPARENCY_BORDER,
size.width + TRANSPARENCY_BORDER * 2,
size.height + TRANSPARENCY_BORDER * 2,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER,
BG_PADDING + TRANSPARENCY_BORDER);
size.height + TRANSPARENCY_BORDER * 2);
GL11.glEnable(GL11.GL_ALPHA_TEST);
@@ -0,0 +1,108 @@
package codechicken.nei.recipe;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.client.gui.inventory.GuiContainer;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.TemplateRecipeHandler.RecipeTransferRect;
public class RecipeTransferRectHandler implements IContainerInputHandler, IContainerTooltipHandler {
private static final Map<Class<? extends GuiContainer>, Set<RecipeTransferRect>> guiMap = new ConcurrentHashMap<>();
static {
final RecipeTransferRectHandler handler = new RecipeTransferRectHandler();
GuiContainerManager.addInputHandler(handler);
GuiContainerManager.addTooltipHandler(handler);
}
public static void registerRectsToGuis(List<Class<? extends GuiContainer>> classes,
List<RecipeTransferRect> rects) {
if (classes == null) return;
for (Class<? extends GuiContainer> clazz : classes) {
guiMap.computeIfAbsent(clazz, k -> new LinkedHashSet<>()).addAll(rects);
}
}
public boolean canHandle(GuiContainer gui) {
return guiMap.containsKey(gui.getClass());
}
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
return false;
}
@Override
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
if (!canHandle(gui)) return false;
if (button == 0) {
return transferRect(gui, false);
} else if (button == 1) {
return transferRect(gui, true);
}
return false;
}
private boolean transferRect(GuiContainer gui, boolean usage) {
final int[] offset = RecipeInfo.getGuiOffset(gui);
return TemplateRecipeHandler.transferRect(gui, guiMap.get(gui.getClass()), offset[0], offset[1], usage);
}
@Override
public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {}
@Override
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {}
@Override
public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {}
@Override
public boolean keyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
return false;
}
@Override
public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
if (!canHandle(gui)) return currenttip;
if (GuiContainerManager.shouldShowTooltip(gui) && currenttip.size() == 0) {
int[] offset = RecipeInfo.getGuiOffset(gui);
currenttip = TemplateRecipeHandler
.transferRectTooltip(gui, guiMap.get(gui.getClass()), offset[0], offset[1], currenttip);
}
return currenttip;
}
@Override
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
return hotkeys;
}
@Override
public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {}
}
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more