Information Handler (#695)

This commit is contained in:
koolkrafter5 authored and GitHub committed 2025-08-16 18:24:40 +07:00
1 parent ef8706ae38
commit aeb59081d9
11 files changed
+302 -59

No files matched your search

+53
View File
@@ -22,6 +22,59 @@ If you have issues with NEI outside of the GTNH modpack you may report them in t
* Cycle between Recipe, Utility, and Cheat mode by ctrl clicking on the Wrench Icon
* GT5u Tools/Items and GT6 tools should now properly work with the Overlay Recipe Transfer
### Information Page Handler
Want to add some information about a block or item without making a massive tooltip for it? You can add information about any block or item by registering it in the Information Handler.
Information page(s) are displayed when either the uses or recipes of a matching item are searched. All matching items will cycle at the top of the information page.
Your mod can call:
```java
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", nbt);
```
Where nbt is an NBTTagCompound formatted as described below.
| Tag | Type | Description |
|------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `filter` | String | A string filter to match what items should return this information page.<br/>modname:itemname, metadata, Ore Dictionary tags, and regex can all be used. The full format may be seen in `config/NEI/collapsibleitems.cfg`. |
| `page` / `pages` | String / String NBTTagList | The actual text to display. See below for differences between single and multi-page messages.<br/>Both formats will automatically translate provided strings. |
#### Single Page Format
Use the `"page"` string tag when you only want to add one page of information:
```java
NBTTagCompound tag = new NBTTagCompound();
tag.setString("filter", "minecraft:log 3");
tag.setString("page", "Exotic wood found in tropical climates.");
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", tag);
```
#### Multiple Page Format
Use the `"pages"` tag as an `NBTTagList` of strings when you want more than one page:
```java
NBTTagCompound tag = new NBTTagCompound();
tag.setString("filter", "minecraft:diamond_sword");
NBTTagList pages = new NBTTagList();
pages.appendTag(new NBTTagString("A powerful melee weapon."));
pages.appendTag(new NBTTagString("Can be enchanted for extra effects."));
tag.setTag("pages", pages);
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", tag);
```
* Each list entry is one page.
* Pages will appear in NEI with buttons to scroll through them.
#### Item Matching
All items that match `filter` will show your information page. See `config/NEI/collapsibleitems.cfg` for the full filter format.
#### 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.
## Other items of note:
* Remove TMI style
+4 -1
View File
@@ -28,6 +28,7 @@ import codechicken.nei.ThreadOperationTimer.TimeoutException;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.search.TooltipFilter;
public class ItemList {
@@ -289,7 +290,7 @@ public class ItemList {
.collect(Collectors.toList());
}
// For optimization it generate itemslist, permutations, orders & collapsibleitems
// Generate itemlist, permutations, orders, collapsibleitems, and informationhandler stacks
@Override
@SuppressWarnings("unchecked")
public void execute() {
@@ -303,6 +304,7 @@ public class ItemList {
ItemSorter.instance.ordering.clear();
CollapsibleItems.clearCache();
TooltipFilter.clearCache();
InformationHandler.clearCache();
List<ItemStack> items = new ArrayList<>();
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
@@ -329,6 +331,7 @@ public class ItemList {
CollapsibleItems.putItem(stack);
TooltipFilter.getSearchTooltip(stack);
InformationHandler.populateStacks(stack);
}
}
@@ -58,6 +58,7 @@ 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;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.util.NEIKeyboardUtils;
@@ -899,6 +900,7 @@ public class NEIClientConfig {
LayoutManager.load();
NEIController.load();
BookmarkContainerInfo.load();
InformationHandler.load();
mainNEIConfigLoaded = true;
new Thread("NEI Plugin Loader") {
@@ -1,13 +1,13 @@
package codechicken.nei.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import codechicken.core.CommonUtils;
import codechicken.nei.NEIClientConfig;
@@ -15,6 +15,7 @@ import codechicken.nei.NEIServerUtils;
import codechicken.nei.recipe.CatalystInfo;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.HandlerInfo;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeCatalysts;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader;
@@ -33,18 +34,11 @@ public class IMCHandler {
if (type == null || type.isEmpty()) continue;
if (CommonUtils.isClient()) {
switch (type) {
case "registerHandlerInfo":
handleRegisterHandlerInfo(message);
break;
case "removeHandlerInfo":
handleRemoveHandlerInfo(message);
break;
case "registerCatalystInfo":
handleRegisterCatalystInfo(message);
break;
case "removeCatalystInfo":
handleRemoveCatalystInfo(message);
break;
case "registerHandlerInfo" -> handleRegisterHandlerInfo(message);
case "removeHandlerInfo" -> handleRemoveHandlerInfo(message);
case "registerCatalystInfo" -> handleRegisterCatalystInfo(message);
case "removeCatalystInfo" -> handleRemoveCatalystInfo(message);
case "addItemInfo" -> handleAddItemInfo(message);
}
}
}
@@ -57,7 +51,7 @@ public class IMCHandler {
}
final NBTTagCompound tag = message.getNBTValue();
final String handler = tag.getString("handler");
NEIClientConfig.logger.info("Processing registerHandlerInfo `" + handler + "` from " + message.getSender());
NEIClientConfig.logger.info("Processing registerHandlerInfo `{}` from {}", handler, message.getSender());
final String modName = tag.getString("modName");
final String modId = tag.getString("modId");
@@ -100,7 +94,7 @@ public class IMCHandler {
: HandlerInfo.DEFAULT_MAX_PER_PAGE;
info.setHandlerDimensions(imageHeight, imageWidth, maxRecipesPerPage);
} catch (NumberFormatException ignored) {
NEIClientConfig.logger.info("Error setting handler dimensions for " + handler);
NEIClientConfig.logger.info("Error setting handler dimensions for {}", handler);
}
// true if not set to false
@@ -118,7 +112,7 @@ public class IMCHandler {
}
final NBTTagCompound tag = message.getNBTValue();
final String handler = tag.getString("handler");
NEIClientConfig.logger.info("Processing removeHandlerInfo `" + handler + "` from " + message.getSender());
NEIClientConfig.logger.info("Processing removeHandlerInfo `{}` from {}", handler, message.getSender());
GuiRecipeTab.handlerRemoverFromIMC.add(handler);
}
@@ -130,7 +124,7 @@ public class IMCHandler {
}
if (!processedCatalystSenders.contains(message.getSender())) {
NEIClientConfig.logger.info("Processing registerCatalystInfo from " + message.getSender());
NEIClientConfig.logger.info("Processing registerCatalystInfo from {}", message.getSender());
processedCatalystSenders.add(message.getSender());
}
final NBTTagCompound tag = message.getNBTValue();
@@ -139,23 +133,14 @@ public class IMCHandler {
NEIClientConfig.logger.warn("Missing handlerID for registerCatalystInfo!");
return;
}
final String itemName = tag.getString("itemName");
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
if (itemName.isEmpty()) {
NEIClientConfig.logger.warn(String.format("Missing itemName for registerCatalystInfo in `%s`!", handlerID));
return;
}
final ItemStack itemStack = NEIServerUtils.getModdedItem(itemName, nbtInfo);
final ItemStack itemStack = getItemStackFromIMC(message, "registerCatalystInfo");
if (itemStack == null) {
NEIClientConfig.logger.warn(String.format("Cannot find item `%s`!", itemName));
return;
}
final int priority = tag.getInteger("priority");
RecipeCatalysts
.addOrPut(RecipeCatalysts.catalystsAdderFromIMC, handlerID, new CatalystInfo(itemStack, priority));
NEIClientConfig.logger
.info(String.format("Added catalyst `%s` to handler %s", itemStack.getDisplayName(), handlerID));
NEIClientConfig.logger.info("Added catalyst `{}` to handler {}", itemStack.getDisplayName(), handlerID);
}
private static void handleRemoveCatalystInfo(IMCMessage message) {
@@ -164,33 +149,62 @@ public class IMCHandler {
return;
}
NEIClientConfig.logger.info("Processing removeCatalystInfo from " + message.getSender());
final NBTTagCompound tag = message.getNBTValue();
final String handlerID = tag.getString("handlerID");
if (handlerID.isEmpty()) {
NEIClientConfig.logger.warn("Missing handlerID for registerCatalystInfo!");
NEIClientConfig.logger.warn("Missing handlerID for removeCatalystInfo!");
return;
}
final String itemName = tag.getString("itemName");
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
if (itemName.isEmpty()) {
NEIClientConfig.logger.warn(String.format("Missing itemName for registerCatalystInfo in `%s`!", handlerID));
return;
}
final ItemStack itemStack = NEIServerUtils.getModdedItem(itemName, nbtInfo);
final ItemStack itemStack = getItemStackFromIMC(message, "removeCatalystInfo");
if (itemStack == null) {
NEIClientConfig.logger.warn(String.format("Cannot find item `%s`!", itemName));
return;
}
if (RecipeCatalysts.catalystsRemoverFromIMC.containsKey(handlerID)) {
RecipeCatalysts.catalystsRemoverFromIMC.get(handlerID).add(itemStack);
} else {
RecipeCatalysts.catalystsRemoverFromIMC
.put(handlerID, new ArrayList<>(Collections.singletonList(itemStack)));
RecipeCatalysts.catalystsRemoverFromIMC.computeIfAbsent(handlerID, k -> new ArrayList<>()).add(itemStack);
NEIClientConfig.logger.info("Removed catalyst `{}` from handler {}", itemStack.getDisplayName(), handlerID);
}
private static void handleAddItemInfo(IMCMessage message) {
if (!message.isNBTMessage()) {
logInvalidMessage(message, "NBT");
return;
}
NEIClientConfig.logger
.info(String.format("Removed catalyst `%s` from handler %s", itemStack.getDisplayName(), handlerID));
final NBTTagCompound tag = message.getNBTValue();
if (tag.hasKey("pages")) addMultipleInfoPages(message);
if (tag.hasKey("page")) addSingleInfoPage(message);
}
private static void addMultipleInfoPages(IMCMessage message) {
final NBTTagCompound tag = message.getNBTValue();
final String filter = tag.getString("filter");
final NBTTagList pages = tag.getTagList("pages", 8); // 8 = TAG_String
for (int i = 0; i < pages.tagCount(); i++) {
String page = pages.getStringTagAt(i);
InformationHandler.addInformationPage(filter, page);
}
}
private static void addSingleInfoPage(IMCMessage message) {
final NBTTagCompound tag = message.getNBTValue();
String filter = tag.getString("filter");
String page = tag.getString("page");
InformationHandler.addInformationPage(filter, page);
}
private static ItemStack getItemStackFromIMC(IMCMessage message, String logAction) {
final NBTTagCompound tag = message.getNBTValue();
final String itemName = tag.getString("itemName");
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
NEIClientConfig.logger.info("Processing {} for item `{}` from {}", logAction, itemName, message.getSender());
final ItemStack itemStack = NEIServerUtils.getModdedItem(itemName, nbtInfo);
if (itemStack == null) {
NEIClientConfig.logger.warn("Cannot find item `{}`!", itemName);
}
return itemStack;
}
private static void logInvalidMessage(FMLInterModComms.IMCMessage message, String type) {
@@ -0,0 +1,150 @@
package codechicken.nei.recipe;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.StatCollector;
import codechicken.nei.ClientHandler;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.util.ItemStackFilterParser;
public class InformationHandler extends TemplateRecipeHandler {
private static final List<InformationPage> ITEM_INFO = new ArrayList<>();
public static void addInformationPage(String filter, String description) {
if (filter.isEmpty() || description.isEmpty()) return;
ITEM_INFO.add(new InformationPage(filter, description));
}
public static void populateStacks(ItemStack stack) {
for (InformationPage page : ITEM_INFO) {
page.addItem(stack);
}
}
@Override
public void drawExtras(int recipe) {
CachedInfoPage page = (CachedInfoPage) this.arecipes.get(recipe);
drawWrappedText(StatCollector.translateToLocal(page.getPage().info).replace("\\n", "\n"), 4, 24);
}
private void drawWrappedText(String text, int x, int y) {
FontRenderer font = Minecraft.getMinecraft().fontRenderer;
List<String> lines = font.listFormattedStringToWidth(text, 156);
for (String line : lines) {
font.drawString(line, x, y, 0);
y += 10;
}
}
public static void clearCache() {
for (InformationPage page : ITEM_INFO) {
page.items.clear();
}
}
@Override
public String getRecipeName() {
return NEIClientUtils.translate("recipe.information");
}
@Override
public String getOverlayIdentifier() {
return "information";
}
@Override
public void loadCraftingRecipes(ItemStack result) {
for (InformationPage page : ITEM_INFO) {
if (page.filter.matches(result)) {
arecipes.add(new CachedInfoPage(page));
}
}
}
@Override
public void loadUsageRecipes(ItemStack ingredient) {
loadCraftingRecipes(ingredient);
}
@Override
public String getGuiTexture() {
return "nei:textures/gui/recipebg.png";
}
private class CachedInfoPage extends CachedRecipe {
private final InformationPage page;
private final PositionedStack stack;
public CachedInfoPage(InformationPage page) {
this.page = page;
stack = new PositionedStack(page.items, 75, 2);
}
@Override
public PositionedStack getResult() {
return null;
}
@Override
public List<PositionedStack> getIngredients() {
return getCycledIngredients(cycleticks / 20, Collections.singletonList(this.stack));
}
public InformationPage getPage() {
return page;
}
}
private static class InformationPage {
final List<ItemStack> items = new ArrayList<>();
final String info;
final ItemFilter filter;
public InformationPage(String filter, String info) {
this.filter = ItemStackFilterParser.parse(filter.trim());
this.info = info;
}
/**
* Adds the item stack if it matches the filter.
*/
public void addItem(ItemStack stack) {
if (filter.matches(stack)) items.add(stack);
}
}
public static void load() {
ClientHandler.loadSettingsFile(
"informationpages.cfg",
lines -> parseFile(lines.collect(Collectors.toCollection(ArrayList::new))));
}
private static void parseFile(List<String> lines) {
for (String rawLine : lines) {
String line = rawLine.trim();
int sepIndex = line.indexOf('=');
if (sepIndex == -1) {
NEIClientConfig.logger.warn("[NEI Info] Invalid line (no '=') in config: {}", line);
continue;
}
String filter = line.substring(0, sepIndex).trim();
String description = line.substring(sepIndex + 1).trim();
addInformationPage(filter, description);
}
}
}
@@ -101,6 +101,8 @@ public class RecipeInfo {
API.registerUsageHandler(new BrewingRecipeHandler());
API.registerRecipeHandler(new FuelRecipeHandler());
API.registerUsageHandler(new FuelRecipeHandler());
API.registerRecipeHandler(new InformationHandler());
API.registerUsageHandler(new InformationHandler());
API.registerGuiOverlay(GuiCrafting.class, "crafting");
API.registerGuiOverlay(GuiInventory.class, "crafting2x2", 63, 20);
@@ -1,18 +1,6 @@
# Hidden Items Filters
#
# parts:
# modname:itemid - identify
# $orename - ore dictionary
# tag.color=red - tag
# 0 or 0-12 - damage
#
# modifiers:
# ! - logical not. exclude items that match the following expression (!minecraft:portal)
# r/.../ - standard java regex (r/^m\w{6}ft$/ = minecraft)
# , - logical or in token (minecraft:potion 16384-16462,!16386)
# | - logical or multi-item search (wrench|hammer)
#
# example: minecraft:potion 16384-16462,!16386 | $oreiron | tag.color=red
# Uses the same filter format at collapsibleitems.cfg
#
minecraft:portal
minecraft:end_portal
@@ -0,0 +1,28 @@
# NEI Item Information Config
#
# One page per line, in the format:
# filter = description text
#
# The filter uses the same format as collapsibleitems.cfg.
#
# You can repeat the same filter on multiple lines to add extra pages.
# Use \n to insert line breaks inside the description. Use it multiple times for a larger gap between lines.
# Using a translation key for the description will automatically translate it.
# Only the first '=' after the filter is treated as the separator; feel free to put them in your descriptions.
# Spaces before and after '=' are removed from the description and are optional.
# The same item can match filters for multiple pages, unlike collapsibleitems.cfg only putting them in the first matching group.
#
# Empty lines and lines starting with # are ignored.
#
# Examples:
# # Jungle Wood (meta 3)
# minecraft:log 3 = Exotic wood found in tropical climates.
#
# # Diamond Sword (two pages of information)
# minecraft:diamond_sword = A powerful melee weapon. Does 8 damage total.
# minecraft:diamond_sword = Can be enchanted for extra effects.
#
# # Two different ore dictionary tags. Iron ingots match both.
# $iron = Iron is a very versatile material. It can be found below sea level in the Overworld.
# $ingot = Ingots are the refined form of metal or clay.
#
@@ -5,6 +5,7 @@ codechicken.nei.recipe.FurnaceRecipeHandler,Minecraft,minecraft:furnace,,minecra
codechicken.nei.recipe.ShapedRecipeHandler,Minecraft,minecraft:crafting_table,,minecraft,FALSE,,,65,166,5,,,,,,
codechicken.nei.recipe.ShapelessRecipeHandler,Minecraft,minecraft:crafting_table,,minecraft,FALSE,,,65,166,5,,,,,,
codechicken.nei.recipe.FuelRecipeHandler,Minecraft,,,minecraft,FALSE,,,65,166,5,nei:textures/nei_tabbed_sprites.png,80,0,14,14,
codechicken.nei.recipe.InformationHandler,Not Enough Items,,,nei,FALSE,,,65,166,1,nei:textures/nei_tabbed_sprites.png,96,0,16,16,
advsolar.client.nei.MTRecipeHandler,Advanced Solar Panels,AdvancedSolarPanel:BlockMolecularTransformer,,AdvancedSolarPanel,TRUE,,,68,,5,,,,,,
appeng.integration.modules.NEIHelpers.NEIAEShapedRecipeHandler,Applied Energistics 2,appliedenergistics2:item.ItemMultiPart:380,,appliedenergistics2,TRUE,,,65,,5,,,,,,
appeng.integration.modules.NEIHelpers.NEIAEShapelessRecipeHandler,Applied Energistics 2,appliedenergistics2:item.ItemMultiPart:380,,appliedenergistics2,TRUE,,,65,,5,,,,,,
1 handler modName itemName nbtInfo modId modRequired excludedModId yShift handlerHeight handlerWidth maxRecipesPerPage imageResource imageX imageY imageWidth imageHeight itemNotes
5 codechicken.nei.recipe.ShapedRecipeHandler Minecraft minecraft:crafting_table minecraft FALSE 65 166 5
6 codechicken.nei.recipe.ShapelessRecipeHandler Minecraft minecraft:crafting_table minecraft FALSE 65 166 5
7 codechicken.nei.recipe.FuelRecipeHandler Minecraft minecraft FALSE 65 166 5 nei:textures/nei_tabbed_sprites.png 80 0 14 14
8 codechicken.nei.recipe.InformationHandler Not Enough Items nei FALSE 65 166 1 nei:textures/nei_tabbed_sprites.png 96 0 16 16
9 advsolar.client.nei.MTRecipeHandler Advanced Solar Panels AdvancedSolarPanel:BlockMolecularTransformer AdvancedSolarPanel TRUE 68 5
10 appeng.integration.modules.NEIHelpers.NEIAEShapedRecipeHandler Applied Energistics 2 appliedenergistics2:item.ItemMultiPart:380 appliedenergistics2 TRUE 65 5
11 appeng.integration.modules.NEIHelpers.NEIAEShapelessRecipeHandler Applied Energistics 2 appliedenergistics2:item.ItemMultiPart:380 appliedenergistics2 TRUE 65 5
@@ -513,6 +513,8 @@ nei.recipe.favorite=Favorite
nei.recipe.favorite.bookmark_recipe=Save Recipe Tree
nei.recipe.favorite.change_result_index=Change Output
nei.recipe.information=Information
nei.itemsort.minecraft=Minecraft
nei.itemsort.minecraft.tip=Sorts items from vanilla minecraft first
nei.itemsort.mod=Mod
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB