Compare commits

...
10 Commits
30 changed files with 592 additions and 701 deletions

No files matched your search

+1 -1
View File
@@ -41,7 +41,7 @@ developmentEnvironmentUserName = Developer
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = false
enableModernJavaSyntax = true
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
@@ -1764,7 +1764,12 @@ public class BookmarkPanel extends PanelWidget {
return namespaces.size();
}
public void setBookmarkFile(String worldPath) {
public void load() {
String worldPath = "global";
if (NEIClientConfig.getBooleanSetting("inventory.bookmarks.worldSpecific")) {
worldPath = NEIClientConfig.getWorldPath();
}
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
@@ -1782,16 +1787,15 @@ public class BookmarkPanel extends PanelWidget {
if (defaultBookmarks.exists()) {
try {
bookmarkFile.createNewFile();
if (bookmarkFile.createNewFile()) {
InputStream src = new FileInputStream(defaultBookmarks);
OutputStream dst = new FileOutputStream(bookmarkFile);
InputStream src = new FileInputStream(defaultBookmarks);
OutputStream dst = new FileOutputStream(bookmarkFile);
IOUtils.copy(src, dst);
src.close();
dst.close();
IOUtils.copy(src, dst);
src.close();
dst.close();
}
} catch (IOException e) {}
}
}
+70 -122
View File
@@ -1,19 +1,19 @@
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.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
@@ -30,17 +30,16 @@ import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.IOUtils;
import com.google.common.collect.Lists;
import codechicken.core.ClassDiscoverer;
import codechicken.core.ClientUtils;
import codechicken.core.GuiModListScroll;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.api.API;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.GuiRecipeTab;
@@ -148,6 +147,7 @@ public class ClientHandler {
public static void preInit() {
loadSerialHandlers();
loadHiddenItems();
loadHeightHackHandlers();
loadHiddenHandlers();
loadEnableAutoFocus();
@@ -155,100 +155,69 @@ public class ClientHandler {
StackInfo.loadGuidFilters();
}
public static void loadSerialHandlers() {
File file = NEIClientConfig.serialHandlersFile;
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);
if (defaultResource != null) {
try {
IOUtils.copy(defaultResource.openStream(), writer);
} catch (IOException e) {}
}
}, callback);
}
public static void loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
Consumer<Stream<String>> callback) {
File file = new File(NEIClientConfig.configDir, resource);
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default serial handlers list {}", file);
URL defaultSerialHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/serialhandlers.cfg");
if (defaultSerialHandlersResource != null) {
IOUtils.copy(defaultSerialHandlersResource.openStream(), writer);
}
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
createDefault.accept(file, writer);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default serial handlers list to file {}", file, e);
NEIClientConfig.logger.error("Failed to save default '{}' to file {}", resource, file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading serial handlers from file {}", file);
NEIClientConfig.serialHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#"))
.collect(Collectors.toCollection(HashSet::new));
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
callback.accept(
IOUtils.readLines(reader).stream().filter(line -> !line.startsWith("#") && !line.trim().isEmpty()));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load serial handlers from file {}", file, e);
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
}
}
public static void loadSerialHandlers() {
loadSettingsFile(
"serialhandlers.cfg",
lines -> NEIClientConfig.serialHandlers = lines.collect(Collectors.toCollection(HashSet::new)));
}
public static void loadHeightHackHandlers() {
File file = NEIClientConfig.heightHackHandlersFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default height hack handlers list {}", file);
URL defaultHeightHackHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/heighthackhandlers.cfg");
if (defaultHeightHackHandlersResource != null) {
IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default height hack handlers list to file {}", file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading height hack handlers from file {}", file);
NEIClientConfig.heightHackHandlerRegex = IOUtils.readLines(reader).stream()
.filter((line) -> !line.startsWith("#")).map(Pattern::compile)
.collect(Collectors.toCollection(HashSet::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load height hack handlers from file {}", file, e);
}
loadSettingsFile(
"heighthackhandlers.cfg",
lines -> NEIClientConfig.heightHackHandlerRegex = lines.map(Pattern::compile)
.collect(Collectors.toCollection(HashSet::new)));
}
public static void loadHiddenHandlers() {
File file = NEIClientConfig.hiddenHandlersFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default hidden handlers list {}", file);
URL defaultHeightHackHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/hiddenhandlers.cfg");
if (defaultHeightHackHandlersResource != null) {
IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default hidden handlers list to file {}", file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading hidden handlers from file {}", file);
NEIClientConfig.hiddenHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#"))
.collect(Collectors.toCollection(HashSet::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load hidden handlers from file {}", file, e);
}
loadSettingsFile(
"hiddenhandlers.cfg",
lines -> NEIClientConfig.hiddenHandlers = lines.collect(Collectors.toCollection(HashSet::new)));
}
public static void loadEnableAutoFocus() {
File file = NEIClientConfig.enableAutoFocusFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default enable auto focus list {}", file);
URL defaultEnableAutoFocusResource = ClientHandler.class
.getResource("/assets/nei/cfg/enableautofocus.cfg");
if (defaultEnableAutoFocusResource != null) {
IOUtils.copy(defaultEnableAutoFocusResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default enable auto focus list to file {}", file, e);
}
}
loadSettingsFile(
"enableautofocus.cfg",
lines -> AutoFocusWidget.enableAutoFocusPrefixes = lines
.collect(Collectors.toCollection(ArrayList::new)));
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading enable auto focus from file {}", file);
AutoFocusWidget.enableAutoFocusPrefixes = IOUtils.readLines(reader).stream()
.filter((line) -> !line.startsWith("#")).collect(Collectors.toCollection(ArrayList::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load enable auto focus from file {}", file, e);
}
public static void loadHiddenItems() {
loadSettingsFile("hiddenitems.cfg", lines -> lines.forEach(API::hideItem));
}
public static void load() {
@@ -266,54 +235,33 @@ public class ClientHandler {
public static void postInit() {
loadHandlerOrdering();
loadPluginsList();
GuiContainerManager.registerReloadResourceListener();
}
public static void loadHandlerOrdering() {
File file = NEIClientConfig.handlerOrderingFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default handler ordering CSV {}", file);
List<String> toWrite = Lists.newArrayList(defaultHandlerOrdering);
GuiRecipeTab.handlerMap.keySet().stream().sorted()
.forEach(handlerId -> toWrite.add(String.format("%s,0", handlerId)));
final String COMMA_DELIMITER = ",";
loadSettingsFile("handlerordering.csv", (file, writer) -> {
List<String> toWrite = Lists.newArrayList(defaultHandlerOrdering);
GuiRecipeTab.handlerMap.keySet().stream().sorted()
.forEach(handlerId -> toWrite.add(String.format("%s,0", handlerId)));
try {
IOUtils.writeLines(toWrite, "\n", writer);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default handler ordering to file {}", file, e);
}
}
} 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.getInteger(parts[1], 0);
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
}));
}
URL url;
try {
url = file.toURI().toURL();
} catch (MalformedURLException e) {
NEIClientConfig.logger.info("Invalid URL for handler ordering CSV.");
e.printStackTrace();
return;
}
public static void loadPluginsList() {
final ClassDiscoverer classDiscoverer = new ClassDiscoverer(
test -> test.startsWith("NEI") && test.endsWith("Config.class"),
IConfigureNEI.class);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
NEIClientConfig.logger.info("Loading handler ordering from file {}", file);
CSVParser csvParser = CSVFormat.EXCEL.withCommentMarker('#').parse(reader);
for (CSVRecord record : csvParser) {
final String handlerId = record.get(0);
int ordering;
try {
ordering = Integer.parseInt(record.get(1));
} catch (NumberFormatException e) {
NEIClientConfig.logger.error("Error parsing CSV record {}: {}", record, e);
continue;
}
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
}
} catch (Exception e) {
NEIClientConfig.logger.info("Error parsing CSV");
e.printStackTrace();
}
NEIClientConfig.pluginsList.addAll(classDiscoverer.findClasses());
}
@SubscribeEvent
@@ -1,227 +1,33 @@
package codechicken.nei;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase.NBTPrimitive;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.StatCollector;
import net.minecraftforge.oredict.OreDictionary;
import org.apache.commons.io.IOUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.PresetsList.Preset;
import codechicken.nei.PresetsList.PresetMode;
import codechicken.nei.api.ItemFilter;
import cpw.mods.fml.common.registry.GameData;
import codechicken.nei.util.ItemStackFilterParser;
public class CollapsibleItems {
protected static interface ISearchParserProvider {
public ItemFilter getFilter(String searchText);
default Predicate<String> getMatcher(String searchText) {
if (searchText.length() >= 3 && searchText.startsWith("r/") && searchText.endsWith("/")) {
try {
Pattern pattern = Pattern.compile(
searchText.substring(2, searchText.length() - 1),
Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
return value -> pattern.matcher(value).find();
} catch (PatternSyntaxException ignored) {}
} else if (!searchText.isEmpty()) {
return value -> value.toLowerCase().contains(searchText);
}
return null;
}
}
protected static class GroupTokenParser {
protected final HashMap<Character, ISearchParserProvider> searchProviders = new HashMap<>();
public void addProvider(char prefix, ISearchParserProvider provider) {
this.searchProviders.put(prefix, provider);
}
public ItemFilter getFilter(String filterText) {
final String[] parts = filterText.toLowerCase().trim().split("\\|");
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(s -> parseSearchText(s))
.filter(s -> s != null && !s.filters.isEmpty()).collect(Collectors.toCollection(ArrayList::new));
if (searchTokens.isEmpty()) {
return new EverythingItemFilter();
} else if (searchTokens.size() == 1) {
return searchTokens.get(0);
} else {
return new AnyMultiItemFilter(searchTokens);
}
}
private AllMultiItemFilter parseSearchText(String filterText) {
if (filterText.isEmpty()) {
return null;
}
final String[] tokens = filterText.split("\\s+");
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
for (String token : tokens) {
token = token.trim();
boolean ignore = token.startsWith("!");
if (ignore) {
token = token.substring(1);
}
if (token.isEmpty()) {
continue;
}
ISearchParserProvider provider = this.searchProviders.get(token.charAt(0));
if (provider != null) {
token = token.substring(1);
} else {
provider = this.searchProviders.get('\0');
}
ItemFilter filter = parseToken(ignore ? "!" + token : token, provider);
if (filter != null) {
searchTokens.filters.add(filter);
}
}
return searchTokens;
}
private ItemFilter parseToken(String token, ISearchParserProvider provider) {
final String[] parts = token.split(",");
final AnyMultiItemFilter includeFilter = new AnyMultiItemFilter();
final AnyMultiItemFilter expludeFilter = new AnyMultiItemFilter();
final AllMultiItemFilter groupFilter = new AllMultiItemFilter();
for (String part : parts) {
boolean ignore = part.startsWith("!");
if (ignore) {
part = part.substring(1);
}
ItemFilter filter = provider.getFilter(part);
if (filter == null) {
continue;
}
if (ignore) {
expludeFilter.filters.add(filter);
} else {
includeFilter.filters.add(filter);
}
}
if (!includeFilter.filters.isEmpty()) {
groupFilter.filters.add(includeFilter);
}
if (!expludeFilter.filters.isEmpty()) {
groupFilter.filters.add(new NegatedItemFilter(expludeFilter));
}
return groupFilter.filters.isEmpty() ? null : groupFilter;
}
}
protected static class IdentifierFilter implements ISearchParserProvider {
@Override
public ItemFilter getFilter(String searchText) {
if (Pattern.matches("^\\d+(-\\d+)*$", searchText)) {
final Predicate<Integer> filter = generateDamageFilter(searchText);
return (stack) -> filter.test(stack.getItemDamage());
}
Predicate<String> matcher = getMatcher(searchText);
if (matcher != null) {
return stack -> matcher.test(getStringIdentifier(stack));
}
return null;
}
protected String getStringIdentifier(ItemStack stack) {
String name = GameData.getItemRegistry().getNameForObject(stack.getItem());
return name == null || name.isEmpty() ? "Unknown:Unknown" : name;
}
protected Predicate<Integer> generateDamageFilter(String searchText) {
String[] range = searchText.split("-");
if (range.length == 1) {
final int damage = Integer.parseInt(range[0]);
return (dmg) -> dmg == damage;
} else {
final int damageStart = Integer.parseInt(range[0]);
final int damageEnd = Integer.parseInt(range[1]);
return (dmg) -> dmg >= damageStart && dmg <= damageEnd;
}
}
}
protected static class OreDictionaryFilter implements ISearchParserProvider {
@Override
public ItemFilter getFilter(String searchText) {
Predicate<String> matcher = getMatcher(searchText);
if (matcher != null) {
return stack -> matches(stack, matcher);
}
return null;
}
protected boolean matches(ItemStack stack, Predicate<String> matcher) {
return IntStream.of(OreDictionary.getOreIDs(stack))
.anyMatch(id -> matcher.test(OreDictionary.getOreName(id)));
}
}
protected static class GroupItem {
public String guid;
@@ -232,8 +38,8 @@ public class CollapsibleItems {
public GroupItem() {}
public void setFilter(String filter) {
this.filter = CollapsibleItems.groupParser.getFilter(filter.trim());
this.guid = UUID.nameUUIDFromBytes(filter.getBytes()).toString();
this.filter = ItemStackFilterParser.parse(filter.trim());
this.guid = this.filter != null ? UUID.nameUUIDFromBytes(filter.getBytes()).toString() : "";
}
public void setFilter(ItemFilter filter, String guid) {
@@ -246,17 +52,40 @@ public class CollapsibleItems {
}
}
private static final String STATE_KEY = "collapsibleitems";
protected File statesFile;
protected static final GroupTokenParser groupParser = new GroupTokenParser();
protected final List<GroupItem> groups = new ArrayList<>();
protected final Map<ItemStack, Integer> cache = new ConcurrentHashMap<>();
static {
groupParser.addProvider('\0', new IdentifierFilter());
groupParser.addProvider('$', new OreDictionaryFilter());
public void load() {
try {
if (NEIClientConfig.world.nbt.hasKey(STATE_KEY)) {
NBTTagCompound states = NEIClientConfig.world.nbt.getCompoundTag(STATE_KEY);
@SuppressWarnings("unchecked")
final Map<String, NBTPrimitive> list = (Map<String, NBTPrimitive>) states.tagMap;
final Map<String, GroupItem> mapping = new HashMap<>();
for (GroupItem group : this.groups) {
mapping.put(group.guid, group);
}
for (Map.Entry<String, NBTPrimitive> nbtEntry : list.entrySet()) {
if (mapping.containsKey(nbtEntry.getKey())) {
mapping.get(nbtEntry.getKey()).expanded = nbtEntry.getValue().func_150290_f() == 1;
}
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading collapsible items states", e);
}
reloadGroups();
}
public void reload() {
public void reloadGroups() {
this.groups.clear();
this.cache.clear();
@@ -271,49 +100,17 @@ public class CollapsibleItems {
}
if (NEIClientConfig.enableCollapsibleItems()) {
loadCollapsibleItems();
}
loadStates();
if (ItemList.loadFinished) {
LayoutManager.markItemsDirty();
ClientHandler.loadSettingsFile(
"collapsibleitems.cfg",
lines -> parseFile(lines.collect(Collectors.toCollection(ArrayList::new))));
}
}
protected void loadCollapsibleItems() {
File file = NEIClientConfig.collapsibleItemsFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default collapsible items list {}", file);
URL defaultCollapsibleItemsResource = ClientHandler.class
.getResource("/assets/nei/cfg/collapsibleitems.cfg");
if (defaultCollapsibleItemsResource != null) {
IOUtils.copy(defaultCollapsibleItemsResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default collapsible items list to file {}", file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading collapsible items from file {}", file);
parseFile(IOUtils.readLines(reader));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load collapsible items from file {}", file, e);
}
}
public void parseFile(List<String> itemStrings) {
private void parseFile(List<String> itemStrings) {
final JsonParser parser = new JsonParser();
GroupItem group = new GroupItem();
for (String itemStr : itemStrings) {
if (itemStr.startsWith("#") || itemStr.trim().isEmpty()) {
continue;
}
try {
if (itemStr.startsWith("; ")) {
@@ -324,10 +121,10 @@ public class CollapsibleItems {
}
if (settings.get("unlocalizedName") != null) {
String displayName = StatCollector
.translateToLocal(settings.get("unlocalizedName").getAsString());
String unlocalizedName = settings.get("unlocalizedName").getAsString();
String displayName = StatCollector.translateToLocal(unlocalizedName);
if (!displayName.equals(settings.get("unlocalizedName").getAsString())) {
if (!displayName.equals(unlocalizedName)) {
group.displayName = displayName;
}
}
@@ -430,7 +227,7 @@ public class CollapsibleItems {
public void toggleGroups(Boolean expanded) {
if (expanded == null) {
expanded = !this.groups.stream().filter(g -> g.expanded).findAny().isPresent();
expanded = this.groups.stream().noneMatch(g -> g.expanded);
}
for (GroupItem group : this.groups) {
@@ -440,33 +237,6 @@ public class CollapsibleItems {
saveStates();
}
private void loadStates() {
try {
if (NEIClientConfig.world.nbt.hasKey("collapsibleitems")) {
NBTTagCompound states = NEIClientConfig.world.nbt.getCompoundTag("collapsibleitems");
@SuppressWarnings("unchecked")
final Map<String, NBTPrimitive> list = (Map<String, NBTPrimitive>) states.tagMap;
final Map<String, GroupItem> mapping = new HashMap<>();
for (GroupItem group : this.groups) {
mapping.put(group.guid, group);
}
for (Map.Entry<String, NBTPrimitive> nbtEntry : list.entrySet()) {
if (mapping.containsKey(nbtEntry.getKey())) {
mapping.get(nbtEntry.getKey()).expanded = nbtEntry.getValue().func_150290_f() == 1;
}
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading collapsible items states", e);
}
}
private void saveStates() {
NBTTagCompound list = new NBTTagCompound();
@@ -474,7 +244,7 @@ public class CollapsibleItems {
list.setBoolean(group.guid, group.expanded);
}
NEIClientConfig.world.nbt.setTag("collapsibleitems", list);
NEIClientConfig.world.nbt.setTag(STATE_KEY, list);
}
}
+15 -16
View File
@@ -6,7 +6,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.regex.Pattern;
@@ -105,7 +104,7 @@ public class ItemList {
}
public AllMultiItemFilter(ItemFilter... filters) {
this(Arrays.asList(filters));
this(new LinkedList<>(Arrays.asList(filters)));
}
public AllMultiItemFilter() {
@@ -139,7 +138,7 @@ public class ItemList {
@Override
public boolean matches(ItemStack item) {
for (ItemFilter filter : filters) try {
if (filter.matches(item)) return true;
if (filter != null && filter.matches(item)) return true;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + item + " with " + filter, e);
}
@@ -156,7 +155,7 @@ public class ItemList {
public static boolean itemMatchesAll(ItemStack item, List<ItemFilter> filters) {
for (ItemFilter filter : filters) {
try {
if (!filter.matches(item)) return false;
if (filter != null && !filter.matches(item)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + item + " with " + filter, e);
}
@@ -211,17 +210,10 @@ public class ItemList {
}
}
@SuppressWarnings("unchecked")
private String getTooltip(ItemStack stack) {
try {
@SuppressWarnings("unchecked")
final List<String> namelist = stack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
final StringJoiner sb = new StringJoiner("\n");
for (String name : namelist) {
sb.add(name);
}
return sb.toString();
return String.join("\n", stack.getTooltip(Minecraft.getMinecraft().thePlayer, false));
} catch (Throwable ignored) {}
return "";
@@ -262,8 +254,8 @@ public class ItemList {
@Override
@SuppressWarnings("unchecked")
public void execute() {
// System.out.println("Executing NEI Item Loading");
ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
LayoutManager.itemsLoaded = true;
loadFinished = false;
List<ItemStack> items = new LinkedList<>();
@@ -282,14 +274,21 @@ public class ItemList {
permutations.clear();
permutations.addAll(ItemInfo.itemOverrides.get(item));
if (permutations.isEmpty()) item.getSubItems(item, null, permutations);
if (permutations.isEmpty()) {
item.getSubItems(item, null, permutations);
}
if (permutations.isEmpty()) damageSearch(item, permutations);
if (permutations.isEmpty()) {
damageSearch(item, permutations);
}
permutations.addAll(ItemInfo.itemVariants.get(item));
timer.reset();
permutations = permutations.stream().filter(stack -> !ItemInfo.isHidden(stack))
.collect(Collectors.toCollection(ArrayList::new));
items.addAll(permutations);
itemMap.putAll(item, permutations);
} catch (Throwable t) {
@@ -12,6 +12,7 @@ import static codechicken.nei.NEIClientConfig.invCreativeMode;
import static codechicken.nei.NEIClientConfig.isBookmarkPanelHidden;
import static codechicken.nei.NEIClientConfig.isEnabled;
import static codechicken.nei.NEIClientConfig.isHidden;
import static codechicken.nei.NEIClientConfig.isLoaded;
import static codechicken.nei.NEIClientConfig.showIDs;
import static codechicken.nei.NEIClientConfig.toggleBooleanSetting;
import static codechicken.nei.NEIClientUtils.cycleGamemode;
@@ -638,12 +639,11 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (isEnabled()) {
setInputFocused(null);
if (!itemsLoaded) {
if (!itemsLoaded && isLoaded()) {
ItemList.loadItems.restart();
itemsLoaded = true;
}
overlayRenderer = null;
overlayRenderer = null;
getLayoutStyle().init();
layout(gui);
AutoFocusWidget.instance.load(gui);
@@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -24,9 +25,11 @@ import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
import com.google.common.base.Objects;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import codechicken.core.CCUpdateChecker;
import codechicken.core.ClassDiscoverer;
import codechicken.core.ClientUtils;
import codechicken.core.CommonUtils;
import codechicken.lib.config.ConfigFile;
@@ -75,12 +78,6 @@ public class NEIClientConfig {
public static ConfigSet world;
public static final File handlerFile = new File(configDir, "handlers.csv");
public static final File catalystFile = new File(configDir, "catalysts.csv");
public static final File serialHandlersFile = new File(configDir, "serialhandlers.cfg");
public static final File heightHackHandlersFile = new File(configDir, "heighthackhandlers.cfg");
public static final File handlerOrderingFile = new File(configDir, "handlerordering.csv");
public static final File hiddenHandlersFile = new File(configDir, "hiddenhandlers.csv");
public static final File enableAutoFocusFile = new File(configDir, "enableautofocus.cfg");
public static final File collapsibleItemsFile = new File(configDir, "collapsibleitems.cfg");
@Deprecated
public static File bookmarkFile;
@@ -100,6 +97,8 @@ public class NEIClientConfig {
// Any handler not in the map will be assigned to 0, and negative numbers are fine.
public static HashMap<String, Integer> handlerOrdering = new HashMap<>();
public static final Set<Class<?>> pluginsList = new HashSet<>();
// Function that extracts the handler ID from a handler, with special logic for
// TemplateRecipeHandler: prefer using the overlay ID if it exists.
public static final Function<IRecipeHandler, String> HANDLER_ID_FUNCTION = handler -> Objects
@@ -238,6 +237,20 @@ public class NEIClientConfig {
});
String prefixRedefinitions = tag.getTag("inventory.search.prefixRedefinitions").setComment(
"Redefine search prefixes by providing a char-to-char map (JSON). The keys are the original prefixes, the values the new ones. Example: { \"$\": \"€\", \"#\": \"+\", \"@\": \"°\", \"%\": \"!\" }")
.getValue("{}");
try {
TypeToken<Map<Character, Character>> typeToken = new TypeToken<Map<Character, Character>>() {
private static final long serialVersionUID = 1L;
};
Map<Character, Character> charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType());
SearchField.searchParser.prefixRedefinitions.putAll(charMap);
} catch (JsonParseException e) {
logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e);
}
tag.getTag("inventory.bookmarks.enabled").setComment("Enable/Disable Bookmark Panel").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.bookmarks.enabled", true));
@@ -248,7 +261,7 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
super.onClick(button);
initBookmarkFile(worldPath);
ItemPanels.bookmarkPanel.load();
return true;
}
});
@@ -324,8 +337,10 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
super.onClick(button);
ItemList.collapsibleItems.reloadGroups();
LayoutManager.markItemsDirty();
return super.onClick(button);
return true;
}
});
@@ -428,18 +443,6 @@ public class NEIClientConfig {
tag.getTag("command.heal").setDefaultValue("");
API.addOption(new OptionTextField("command.heal"));
tag.getTag("inventory.worldSpecificPresets").setComment("Global or world specific presets")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.worldSpecificPresets", true) {
@Override
public boolean onClick(int button) {
super.onClick(button);
initPresetsFile(worldPath);
return true;
}
});
tag.getTag("inventory.showItemQuantityWidget").setComment("Show Item Quantity Widget").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.showItemQuantityWidget", true));
@@ -554,11 +557,11 @@ public class NEIClientConfig {
}
public static void loadWorld(String worldPath) {
unloadWorld();
NEIClientConfig.worldPath = worldPath;
setInternalEnabled(true);
logger.debug("Loading " + (Minecraft.getMinecraft().isSingleplayer() ? "Local" : "Remote") + " World");
bootNEI(ClientUtils.getWorld());
final File specificDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
final boolean newWorld = !specificDir.exists();
@@ -568,11 +571,15 @@ public class NEIClientConfig {
}
world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg")));
initBookmarkFile(worldPath);
initPresetsFile(worldPath);
bootNEI(ClientUtils.getWorld());
ItemPanels.bookmarkPanel.load();
onWorldLoad(newWorld);
}
public static String getWorldPath() {
return NEIClientConfig.worldPath;
}
private static void onWorldLoad(boolean newWorld) {
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");
@@ -671,25 +678,20 @@ public class NEIClientConfig {
@Override
public void run() {
logger.debug("Started NEI plugin loading");
ClassDiscoverer classDiscoverer = new ClassDiscoverer(
test -> test.startsWith("NEI") && test.endsWith("Config.class"),
IConfigureNEI.class);
classDiscoverer.findClasses();
for (Class<?> clazz : classDiscoverer.classes) {
NEIClientConfig.pluginsList.parallelStream().forEach(clazz -> {
try {
IConfigureNEI config = (IConfigureNEI) clazz.newInstance();
config.loadConfig();
NEIModContainer.plugins.add(config);
logger.debug("Loaded " + clazz.getName());
logger.debug("Loaded {}", clazz.getName());
} catch (Exception e) {
logger.error("Failed to Load " + clazz.getName(), e);
logger.error("Failed to Load {}", clazz.getName(), e);
}
}
});
RecipeCatalysts.loadCatalystInfo();
ItemSorter.loadConfig();
// Set pluginNEIConfigLoaded here before posting the NEIConfigsLoadedEvent. This used to be the other
// way around, but apparently if your modpack includes 800 mods the event poster might not return in
@@ -697,29 +699,11 @@ public class NEIClientConfig {
// cause issues in case one of the event handler calls the (non-thread-safe) NEI API. I don't expect any
// handler to do this, but who knows what modders have come up with...
pluginNEIConfigLoaded = true;
logger.debug("NEI plugin loading finished");
MinecraftForge.EVENT_BUS.post(new NEIConfigsLoadedEvent());
ItemList.loadItems.restart();
}
}.start();
ItemSorter.loadConfig();
}
private static void initBookmarkFile(String worldPath) {
if (!global.config.getTag("inventory.bookmarks.worldSpecific").getBooleanValue()) {
worldPath = "global";
}
ItemPanels.bookmarkPanel.setBookmarkFile(worldPath);
}
private static void initPresetsFile(String worldPath) {
if (!global.config.getTag("inventory.worldSpecificPresets").getBooleanValue()) {
worldPath = "global";
}
PresetsList.setPresetsFile(worldPath);
}
public static boolean isWorldSpecific(String setting) {
+11 -58
View File
@@ -4,8 +4,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
@@ -145,62 +143,20 @@ public class PresetsList {
}
public static final List<Preset> presets = new ArrayList<>();
protected static File presetsFile;
protected static RecipesFilter recipeFilter = new RecipesFilter();
protected static ItemPanelFilter itemFilter = new ItemPanelFilter();
static {
API.addItemFilter(itemFilter);
API.addRecipeFilter(recipeFilter);
ItemList.loadCallbacks.add(PresetsList::itemsLoaded);
}
public static ItemFilter getItemFilter() {
return itemFilter.getFilter();
}
public static void itemsLoaded() {
recipeFilter.cache = null;
itemFilter.cache = null;
ItemList.collapsibleItems.reload();
}
public static void setPresetsFile(String worldPath) {
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
if (!dir.exists()) {
dir.mkdirs();
}
presetsFile = new File(dir, "presets.ini");
if (!presetsFile.exists()) {
final File globalPresets = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
final File configPresets = new File(NEIClientConfig.configDir, "presets.ini");
final File defaultBookmarks = configPresets.exists() ? configPresets : globalPresets;
if (defaultBookmarks.exists()) {
try {
presetsFile.createNewFile();
InputStream src = new FileInputStream(defaultBookmarks);
OutputStream dst = new FileOutputStream(presetsFile);
IOUtils.copy(src, dst);
src.close();
dst.close();
} catch (IOException e) {}
}
}
loadPresets();
}
protected static void loadPresets() {
public static void load() {
final File presetsFile = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
if (presetsFile == null || !presetsFile.exists()) {
return;
@@ -236,15 +192,15 @@ public class PresetsList {
preset = new Preset();
}
if (settings.get("name") != null) {
if (settings.has("name")) {
preset.name = settings.get("name").getAsString();
}
if (settings.get("enabled") != null) {
if (settings.has("enabled")) {
preset.enabled = settings.get("enabled").getAsBoolean();
}
if (settings.get("mode") != null) {
if (settings.has("mode")) {
preset.mode = PresetMode.valueOf(settings.get("mode").getAsString());
}
@@ -260,17 +216,11 @@ public class PresetsList {
if (!preset.items.isEmpty()) {
presets.add(preset);
}
itemsLoaded();
}
public static void savePresets() {
if (presetsFile == null) {
return;
}
List<String> strings = new ArrayList<>();
final File presetsFile = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
final List<String> strings = new ArrayList<>();
for (Preset preset : presets) {
JsonObject settings = new JsonObject();
@@ -289,7 +239,10 @@ public class PresetsList {
NEIClientConfig.logger.error("Filed to save presets list to file {}", presetsFile, e);
}
itemsLoaded();
recipeFilter.cache = null;
itemFilter.cache = null;
ItemList.collapsibleItems.reloadGroups();
LayoutManager.markItemsDirty();
}
}
@@ -5,7 +5,6 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -20,6 +19,8 @@ import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.api.ItemFilter;
import gnu.trove.map.TCharCharMap;
import gnu.trove.map.hash.TCharCharHashMap;
public class SearchTokenParser {
@@ -30,16 +31,11 @@ public class SearchTokenParser {
NEVER;
public static SearchMode fromInt(int value) {
switch (value) {
case 0:
return ALWAYS;
case 1:
return PREFIX;
case 2:
return NEVER;
default:
return ALWAYS;
}
return switch (value) {
case 0 -> ALWAYS;
case 1 -> PREFIX;
default -> NEVER;
};
}
}
@@ -76,6 +72,7 @@ public class SearchTokenParser {
protected final List<ISearchParserProvider> searchProviders;
protected final ProvidersCache providersCache = new ProvidersCache();
protected final TCharCharMap prefixRedefinitions = new TCharCharHashMap();
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
this.searchProviders = searchProviders;
@@ -114,7 +111,9 @@ public class SearchTokenParser {
public ISearchParserProvider getProvider(char ch) {
return getProviders().stream()
.filter(provider -> provider.getSearchMode() == SearchMode.PREFIX && provider.getPrefix() == ch)
.filter(
provider -> provider.getSearchMode() == SearchMode.PREFIX
&& getRedefinedPrefix(provider.getPrefix()) == ch)
.findFirst().orElse(null);
}
@@ -133,18 +132,24 @@ public class SearchTokenParser {
}
public Pattern getSplitPattern() {
StringJoiner prefixes = new StringJoiner("");
prefixes.add(String.valueOf('\0'));
StringBuilder prefixes = new StringBuilder().append('\0');
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchMode.PREFIX) {
prefixes.add(String.valueOf(provider.getPrefix()));
prefixes.append(getRedefinedPrefix(provider.getPrefix()));
}
}
return Pattern.compile("((-*)([" + Pattern.quote(prefixes.toString()) + "]*)(\\\".*?(?:\\\"|$)|\\S+))");
}
private char getRedefinedPrefix(char prefix) {
if (this.prefixRedefinitions.containsKey(prefix)) {
return this.prefixRedefinitions.get(prefix);
}
return prefix;
}
private ItemFilter parseSearchText(String filterText) {
if (filterText.isEmpty()) {
@@ -188,7 +193,11 @@ public class SearchTokenParser {
for (ISearchParserProvider _provider : getProviders()) {
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
filters.add(_provider.getFilter(token));
ItemFilter filter = _provider.getFilter(token);
if (filter != null) {
filters.add(filter);
}
}
}
@@ -1,5 +1,6 @@
package codechicken.nei;
import static codechicken.nei.LayoutManager.searchField;
import static codechicken.nei.NEIClientUtils.translate;
import java.awt.Point;
@@ -33,7 +34,6 @@ import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.api.API;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.guihook.GuiContainerManager;
public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoadedCallback {
@@ -72,7 +72,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa
if (slot < sorted.size()) {
SubsetTag tag = sorted.get(slot);
if (NEIClientUtils.shiftKey()) {
codechicken.nei.LayoutManager.searchField.setText("%" + tag.fullname);
searchField.setText("%" + tag.fullname.replace(" ", ""));
} else if (button == 0 && count >= 2) {
SubsetWidget.showOnly(tag);
} else {
@@ -94,7 +94,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa
Rectangle4i r = new Rectangle4i(x, y, w, getSlotHeight(slot));
if (slot < sorted.size()) {
SubsetTag tag = sorted.get(slot);
codechicken.nei.LayoutManager.getLayoutStyle()
LayoutManager.getLayoutStyle()
.drawSubsetTag(tag.displayName(), x, y, r.w, r.h, tag.state.state, r.contains(mx, my));
} else {
ItemStack stack = state.items.get(slot - sorted.size());
@@ -103,8 +103,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa
int itemx = w / 2 - 8;
int itemy = 1;
codechicken.nei.LayoutManager.getLayoutStyle()
.drawSubsetTag(null, x, y, r.w, r.h, hidden ? 0 : 2, false);
LayoutManager.getLayoutStyle().drawSubsetTag(null, x, y, r.w, r.h, hidden ? 0 : 2, false);
GuiContainerManager.drawItem(x + itemx, y + itemy, stack);
if (new Rectangle4i(itemx, itemy, 16, 16).contains(mx, my)) SubsetWidget.hoverStack = stack;
@@ -642,7 +641,6 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa
if (reallocate) {
for (ItemStack item : ItemList.items) {
if (interrupted()) return;
if (ItemInfo.isHidden(item)) continue;
for (SubsetTag tag : tags) if (tag.filter.matches(item)) state.get(tag.fullname).items.add(item);
}
}
@@ -42,6 +42,7 @@ import codechicken.nei.recipe.IUsageHandler;
import codechicken.nei.recipe.RecipeCatalysts;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ItemStackFilterParser;
/**
* This is the main class that handles item property configuration. WARNING: DO NOT access this class until the world
@@ -151,6 +152,14 @@ public class API {
}
}
public static void hideItem(String rule) {
ItemFilter filter = ItemStackFilterParser.parse(rule);
if (filter != null) {
ItemInfo.hiddenItemsRules.filters.add(filter);
LayoutManager.markItemsDirty();
}
}
/**
* Add or replace the name normally shown on the item tooltip
*/
+12 -32
View File
@@ -44,12 +44,14 @@ import codechicken.core.featurehack.GameDataManipulator;
import codechicken.nei.InfiniteStackSizeHandler;
import codechicken.nei.InfiniteToolHandler;
import codechicken.nei.ItemList;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.PatternItemFilter;
import codechicken.nei.ItemMobSpawner;
import codechicken.nei.ItemStackMap;
import codechicken.nei.ItemStackSet;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.PopupInputHandler;
import codechicken.nei.PresetsList;
import codechicken.nei.SearchField.SearchParserProvider;
import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.config.ArrayDumper;
@@ -82,6 +84,7 @@ public class ItemInfo {
public static final ArrayListMultimap<Layout, IHighlightHandler> highlightHandlers = ArrayListMultimap.create();
public static final ItemStackMap<String> nameOverrides = new ItemStackMap<>();
public static final ItemStackSet hiddenItems = new ItemStackSet();
public static final AnyMultiItemFilter hiddenItemsRules = new AnyMultiItemFilter();
public static final ItemStackSet finiteItems = new ItemStackSet();
public static final ArrayListMultimap<Item, ItemStack> itemOverrides = ArrayListMultimap.create();
public static final ArrayListMultimap<Item, ItemStack> itemVariants = ArrayListMultimap.create();
@@ -93,9 +96,10 @@ public class ItemInfo {
public static final HashMap<Item, String> itemOwners = new HashMap<>();
public static boolean isHidden(ItemStack stack) {
return hiddenItems.contains(stack);
return hiddenItems.contains(stack) || hiddenItemsRules.matches(stack);
}
@Deprecated
public static boolean isHidden(Item item) {
return hiddenItems.containsAll(item);
}
@@ -130,22 +134,15 @@ public class ItemInfo {
addInfiniteHandlers();
addInputHandlers();
addIDDumps();
addHiddenItemFilter();
addSearchProviders();
PresetsList.load();
ItemList.collapsibleItems.load();
ItemList.loadCallbacks.add(TooltipFilter::populateSearchMap);
}
private static void addHiddenItemFilter() {
API.addItemFilter(() -> item -> !hiddenItems.contains(item));
}
private static void addSearchProviders() {
API.addSearchProvider(
new SearchParserProvider(
'\0',
"default",
EnumChatFormatting.RESET,
(pattern) -> new PatternItemFilter(pattern)) {
new SearchParserProvider('\0', "default", EnumChatFormatting.RESET, PatternItemFilter::new) {
@Override
public SearchMode getSearchMode() {
@@ -154,29 +151,12 @@ public class ItemInfo {
});
API.addSearchProvider(
new SearchParserProvider(
'@',
"modName",
EnumChatFormatting.LIGHT_PURPLE,
(pattern) -> new ModNameFilter(pattern)));
new SearchParserProvider('@', "modName", EnumChatFormatting.LIGHT_PURPLE, ModNameFilter::new));
API.addSearchProvider(
new SearchParserProvider(
'$',
"oreDict",
EnumChatFormatting.AQUA,
(pattern) -> new OreDictionaryFilter(pattern)));
new SearchParserProvider('$', "oreDict", EnumChatFormatting.AQUA, OreDictionaryFilter::new));
API.addSearchProvider(new SearchParserProvider('#', "tooltip", EnumChatFormatting.YELLOW, TooltipFilter::new));
API.addSearchProvider(
new SearchParserProvider(
'#',
"tooltip",
EnumChatFormatting.YELLOW,
(pattern) -> new TooltipFilter(pattern)));
API.addSearchProvider(
new SearchParserProvider(
'&',
"identifier",
EnumChatFormatting.GOLD,
(pattern) -> new IdentifierFilter(pattern)));
new SearchParserProvider('&', "identifier", EnumChatFormatting.GOLD, IdentifierFilter::new));
}
private static void addIDDumps() {
@@ -11,30 +11,29 @@ import codechicken.nei.config.GuiOptionList.OptionScrollSlot;
public class OptionTextField extends Option {
private TextField textField = new TextField("test") {
protected final TextField textField = new TextField("test") {
{
this.h = 20;
}
@Override
public void onTextChange(String oldText) {
if (focused() && isValidValue(text())) if (!defaulting() || !text().equals(getTag().getValue())) // don't
// override
// global
// if text
// hasn't
// changed
// don't override global if text hasn't changed
if (focused() && isValidValue(text()) && (!defaulting() || !text().equals(getTag().getValue()))) {
getTag().setValue(text());
}
}
@Override
public void setFocus(boolean focus) {
if (!focus && !isValidValue(text())) setText(renderTag().getValue());
super.setFocus(focus);
}
};
public OptionTextField(String name) {
super(name);
textField.h = 20;
}
@Override
@@ -13,7 +13,6 @@ import org.lwjgl.opengl.GL12;
import codechicken.core.gui.GuiWidget;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemPanel.ItemPanelSlot;
import codechicken.nei.ItemsGrid;
import codechicken.nei.Label;
@@ -22,7 +21,6 @@ import codechicken.nei.PresetsList.Preset;
import codechicken.nei.PresetsList.PresetMode;
import codechicken.nei.TextField;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
@@ -52,9 +50,7 @@ public class LeftPanel extends GuiWidget {
@Override
protected ItemFilter getFilter() {
Set<String> identifiers = preset.items;
return new AllMultiItemFilter(
item -> !ItemInfo.hiddenItems.contains(item),
item -> identifiers.contains(Preset.getIdentifier(item)));
return item -> identifiers.contains(Preset.getIdentifier(item));
}
@Override
@@ -146,6 +142,7 @@ public class LeftPanel extends GuiWidget {
grid.restartFilter();
}
@Override
public void mouseClicked(int x, int y, int button) {
grid.mouseClicked(x, y, button);
@@ -183,6 +180,7 @@ public class LeftPanel extends GuiWidget {
protected void onItemsChanges() {}
@Override
public void mouseMovedOrUp(int x, int y, int button) {
nameField.mouseUp(x, y, button);
@@ -203,6 +201,7 @@ public class LeftPanel extends GuiWidget {
}
}
@Override
public void mouseDragged(int x, int y, int button, long time) {
nameField.mouseDragged(x, y, button, time);
@@ -233,6 +232,7 @@ public class LeftPanel extends GuiWidget {
}
}
@Override
public void update() {
int CHECKBOX_WIDTH = (this.width - 12) / 3;
nameLabel.x = this.x + 2;
@@ -271,6 +271,7 @@ public class LeftPanel extends GuiWidget {
grid.refresh(null);
}
@Override
public void draw(int mousex, int mousey, float frame) {
nameLabel.draw(mousex, mousey);
nameField.draw(mousex, mousey);
@@ -305,7 +306,7 @@ public class LeftPanel extends GuiWidget {
}
}
if (tooltip.size() > 0) {
if (!tooltip.isEmpty()) {
tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); // add space after 'title'
}
@@ -320,6 +321,7 @@ public class LeftPanel extends GuiWidget {
return tooltip;
}
@Override
public void keyTyped(char c, int keycode) {
nameField.handleKeyPress(keycode, c);
}
@@ -56,7 +56,7 @@ public abstract class PresetItemsGrid extends ItemsGrid {
}
};
public PresetItemsGrid() {
protected PresetItemsGrid() {
pageLabel = new Label("0/0", true);
@@ -28,7 +28,6 @@ import codechicken.nei.PresetsList.Preset;
import codechicken.nei.SearchField.GuiSearchField;
import codechicken.nei.TextField;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.util.TextHistory;
@@ -55,11 +54,11 @@ public class RightPanel extends GuiWidget {
}
}
protected static abstract class PresetSearchField extends TextField {
protected abstract static class PresetSearchField extends TextField {
private static final TextHistory history = new TextHistory();
public PresetSearchField(String ident) {
protected PresetSearchField(String ident) {
super(ident);
}
@@ -134,9 +133,7 @@ public class RightPanel extends GuiWidget {
@Override
protected ItemFilter getFilter() {
AllMultiItemFilter filter = new AllMultiItemFilter();
filter.filters.add(item -> !ItemInfo.hiddenItems.contains(item));
filter.filters.add(searchField.getFilter());
AllMultiItemFilter filter = new AllMultiItemFilter(searchField.getFilter());
if (enabledPresets.isChecked()) {
AllMultiItemFilter andFilter = new AllMultiItemFilter();
@@ -162,7 +159,7 @@ public class RightPanel extends GuiWidget {
protected boolean isSelected(ItemStack stack) {
if (mouseSelection != null && mouseSelection.items.contains(stack)) {
return mouseSelection.append != false;
return mouseSelection.append;
}
return preset.items.contains(Preset.getIdentifier(stack));
@@ -206,6 +203,7 @@ public class RightPanel extends GuiWidget {
grid.restartFilter();
}
@Override
public void mouseClicked(int x, int y, int button) {
grid.mouseClicked(x, y, button);
@@ -231,6 +229,7 @@ public class RightPanel extends GuiWidget {
protected void onItemsChanges() {}
@Override
public void mouseMovedOrUp(int x, int y, int button) {
searchField.mouseUp(x, y, button);
@@ -300,6 +299,7 @@ public class RightPanel extends GuiWidget {
return null;
}
@Override
public void mouseDragged(int x, int y, int button, long time) {
searchField.mouseDragged(x, y, button, time);
@@ -330,6 +330,7 @@ public class RightPanel extends GuiWidget {
}
}
@Override
public void update() {
enabledPresets.w = 20;
enabledPresets.h = INPUT_HEIGHT;
@@ -345,6 +346,7 @@ public class RightPanel extends GuiWidget {
grid.refresh(null);
}
@Override
public void draw(int mousex, int mousey, float frame) {
enabledPresets.draw(mousex, mousey);
searchField.draw(mousex, mousey);
@@ -372,7 +374,7 @@ public class RightPanel extends GuiWidget {
}
}
if (tooltip.size() > 0) {
if (!tooltip.isEmpty()) {
tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); // add space after 'title'
}
@@ -383,10 +385,12 @@ public class RightPanel extends GuiWidget {
return tooltip;
}
@Override
public void keyTyped(char c, int keycode) {
searchField.handleKeyPress(keycode, c);
}
@Override
public void mouseScrolled(int x, int y, int scroll) {
if (grid.contains(x, y)) {
grid.shiftPage(-scroll);
@@ -156,13 +156,15 @@ public class GuiContainerManager {
namelist = stack.getTooltip(
Minecraft.getMinecraft().thePlayer,
includeHandlers && Minecraft.getMinecraft().gameSettings.advancedItemTooltips);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
namelist = new ArrayList<>();
}
if (namelist == null) namelist = new ArrayList<>();
if (namelist.size() == 0) namelist.add("Unnamed");
if (namelist.get(0) == null || namelist.get(0).equals("")) namelist.set(0, "Unnamed");
if (namelist.isEmpty()) {
namelist.add("Unnamed");
} else if (namelist.get(0) == null || namelist.get(0).isEmpty()) {
namelist.set(0, "Unnamed");
}
if (includeHandlers) {
for (IContainerTooltipHandler handler : tooltipHandlers) {
@@ -5,7 +5,9 @@ import static codechicken.lib.gui.GuiDraw.getMousePosition;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -24,6 +26,7 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
public static ArrayList<ICraftingHandler> craftinghandlers = new ArrayList<>();
public static ArrayList<ICraftingHandler> serialCraftingHandlers = new ArrayList<>();
private static Set<String> existingHandlers = new HashSet<>();
public static boolean openRecipeGui(String outputId, Object... results) {
return createRecipeGui(outputId, true, results) != null;
@@ -155,12 +158,24 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
public static void registerRecipeHandler(ICraftingHandler handler) {
final String handlerId = handler.getHandlerId();
if (craftinghandlers.stream().anyMatch(h -> h.getHandlerId().equals(handlerId))
|| serialCraftingHandlers.stream().anyMatch(h -> h.getHandlerId().equals(handlerId)))
if (existingHandlers.contains(handlerId)) {
return;
}
synchronized (existingHandlers) {
existingHandlers.add(handlerId);
}
if (NEIClientConfig.serialHandlers.contains(handlerId)) {
synchronized (serialCraftingHandlers) {
serialCraftingHandlers.add(handler);
}
} else {
synchronized (craftinghandlers) {
craftinghandlers.add(handler);
}
}
if (NEIClientConfig.serialHandlers.contains(handlerId)) serialCraftingHandlers.add(handler);
else craftinghandlers.add(handler);
}
@Override
@@ -2,6 +2,8 @@ package codechicken.nei.recipe;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
@@ -14,6 +16,7 @@ public class GuiUsageRecipe extends GuiRecipe<IUsageHandler> {
public static ArrayList<IUsageHandler> usagehandlers = new ArrayList<>();
public static ArrayList<IUsageHandler> serialUsageHandlers = new ArrayList<>();
private static Set<String> existingHandlers = new HashSet<>();
public static boolean openRecipeGui(String inputId, Object... ingredients) {
@@ -65,12 +68,25 @@ public class GuiUsageRecipe extends GuiRecipe<IUsageHandler> {
public static void registerUsageHandler(IUsageHandler handler) {
final String handlerId = handler.getHandlerId();
if (usagehandlers.stream().anyMatch(h -> h.getHandlerId().equals(handlerId))
|| serialUsageHandlers.stream().anyMatch(h -> h.getHandlerId().equals(handlerId)))
return;
if (NEIClientConfig.serialHandlers.contains(handlerId)) serialUsageHandlers.add(handler);
else usagehandlers.add(handler);
if (existingHandlers.contains(handlerId)) {
return;
}
synchronized (existingHandlers) {
existingHandlers.add(handlerId);
}
if (NEIClientConfig.serialHandlers.contains(handlerId)) {
synchronized (serialUsageHandlers) {
serialUsageHandlers.add(handler);
}
} else {
synchronized (usagehandlers) {
usagehandlers.add(handler);
}
}
}
private static IUsageHandler getUsageOrCatalystHandler(IUsageHandler handler, String inputId,
@@ -1,15 +1,8 @@
package codechicken.nei.recipe;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.minecraft.item.ItemStack;
@@ -20,10 +13,8 @@ import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidContainerItem;
import org.apache.commons.io.IOUtils;
import codechicken.nei.ClientHandler;
import codechicken.nei.ItemStackMap;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.api.IStackStringifyHandler;
import codechicken.nei.recipe.stackinfo.DefaultStackStringifyHandler;
import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler;
@@ -187,37 +178,9 @@ public class StackInfo {
}
public static void loadGuidFilters() {
guidfilters.clear();
final File guidFlitersFile = new File(NEIClientConfig.configDir, "guidfilters.cfg");
List<String> itemStrings = new ArrayList<>();
if (guidFlitersFile.exists()) {
try (FileReader reader = new FileReader(guidFlitersFile)) {
NEIClientConfig.logger.info("Loading guid filters from file {}", guidFlitersFile);
itemStrings = IOUtils.readLines(reader);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load bookmarks from file {}", guidFlitersFile, e);
e.printStackTrace();
}
} else {
final URL filterUrl = StackInfo.class.getResource("/assets/nei/guidfilters.cfg");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(filterUrl.openStream()))) {
itemStrings = IOUtils.readLines(reader);
} catch (IOException e) {
NEIClientConfig.logger.info("Error parsing guid filters");
e.printStackTrace();
} catch (Exception e) {
NEIClientConfig.logger.info("Error parsing guid filters");
e.printStackTrace();
}
}
for (String guidStr : itemStrings) {
ClientHandler.loadSettingsFile("guidfilters.cfg", lines -> lines.forEach(guidStr -> {
final String[] parts = guidStr.split(",");
final HashMap<String, String[]> rules = new HashMap<>();
@@ -226,7 +189,7 @@ public class StackInfo {
}
guidfilters.put(parts[0], rules);
}
}));
}
public static ItemStack getItemStackWithMinimumDamage(ItemStack[] stacks) {
@@ -432,7 +432,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
public static ItemFilter getItemFilter() {
return new AllMultiItemFilter(
item -> !ItemInfo.hiddenItems.contains(item),
item -> !ItemInfo.isHidden(item),
PresetsList.getItemFilter(),
GuiRecipe.searchField != null ? GuiRecipe.searchField.getFilter() : null);
}
@@ -1,8 +1,6 @@
package codechicken.nei.search;
import java.util.HashSet;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
@@ -58,14 +56,13 @@ public class TooltipFilter implements ItemFilter {
}
public static void populateSearchMap() {
/* Create a snapshot of the current keys in the cache */
HashSet<ItemStackKey> oldItems = new HashSet<>(itemSearchNames.keySet());
new Thread("NEI populate Tooltip Filter") {
for (ItemStack stack : ItemList.items) {
oldItems.remove(new ItemStackKey(stack));
}
itemSearchNames.keySet().removeAll(oldItems);
@Override
public void run() {
ItemList.items.parallelStream().forEach(TooltipFilter::getSearchTooltip);
}
}.start();
}
protected static String getSearchTooltip(ItemStack stack) {
@@ -74,14 +71,7 @@ public class TooltipFilter implements ItemFilter {
private static String getTooltip(ItemStack itemstack) {
final List<String> list = GuiContainerManager.itemDisplayNameMultiline(itemstack, null, true);
final StringJoiner sb = new StringJoiner("\n");
final int size = list.size();
for (int i = 1; i < size; i++) {
sb.add(list.get(i));
}
return EnumChatFormatting.getTextWithoutFormattingCodes(sb.toString());
return EnumChatFormatting.getTextWithoutFormattingCodes(String.join("\n", list.subList(1, list.size())));
}
}
@@ -0,0 +1,203 @@
package codechicken.nei.util;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.IntStream;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.oredict.OreDictionary;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.api.ItemFilter;
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
import cpw.mods.fml.common.registry.GameData;
/**
* @formatter:off
*
* 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
*/
public class ItemStackFilterParser {
private ItemStackFilterParser() {}
public static ItemFilter parse(String filterText) {
final List<ItemFilter> searchTokens = new ArrayList<>();
filterText = filterText.trim();
if (!filterText.isEmpty()) {
for (String part : filterText.split("\\s*\\|\\s*")) {
AllMultiItemFilter filter = parsePart(part);
if (!filter.filters.isEmpty()) {
searchTokens.add(filter);
}
}
}
if (searchTokens.isEmpty()) {
return null;
} else if (searchTokens.size() == 1) {
return searchTokens.get(0);
} else {
return new AnyMultiItemFilter(searchTokens);
}
}
private static AllMultiItemFilter parsePart(String part) {
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
for (String token : part.split("\\s+")) {
ItemFilter ruleFilter = parseRules(token);
if (ruleFilter != null) {
searchTokens.filters.add(ruleFilter);
}
}
return searchTokens;
}
protected static ItemFilter parseRules(String token) {
final AnyMultiItemFilter orFilter = new AnyMultiItemFilter();
final AnyMultiItemFilter orNotFilter = new AnyMultiItemFilter();
final AllMultiItemFilter ruleFilter = new AllMultiItemFilter();
for (String rule : token.split(",")) {
boolean ignore = rule.startsWith("!");
ItemFilter filter = null;
if (ignore) {
rule = rule.substring(1);
}
if (rule.startsWith("$")) {
filter = getOreDictFilter(rule.substring(1));
} else if (rule.startsWith("tag.")) {
filter = getTagFilter(rule.substring(4));
} else if (Pattern.matches("^\\d+(-\\d+)?$", rule)) {
filter = getDamageFilter(rule);
} else {
filter = getStringIdentifierFilter(rule);
}
if (filter == null) {
continue;
}
if (ignore) {
orNotFilter.filters.add(filter);
} else {
orFilter.filters.add(filter);
}
}
if (!orFilter.filters.isEmpty()) {
ruleFilter.filters.add(orFilter);
}
if (!orNotFilter.filters.isEmpty()) {
ruleFilter.filters.add(new NegatedItemFilter(orNotFilter));
}
return ruleFilter.filters.isEmpty() ? null : ruleFilter;
}
protected static Predicate<String> getMatcher(String searchText) {
if (searchText.length() >= 3 && searchText.startsWith("r/") && searchText.endsWith("/")) {
try {
Pattern pattern = Pattern.compile(
searchText.substring(2, searchText.length() - 1),
Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
return value -> pattern.matcher(value).find();
} catch (PatternSyntaxException ignored) {}
} else if (!searchText.isEmpty()) {
final String lowerCase = searchText.toLowerCase();
return value -> value.toLowerCase().contains(lowerCase);
}
return null;
}
protected static ItemFilter getOreDictFilter(String rule) {
final Predicate<String> matcher = getMatcher(rule);
if (matcher == null) {
return null;
}
return stack -> IntStream.of(OreDictionary.getOreIDs(stack))
.anyMatch(id -> matcher.test(OreDictionary.getOreName(id)));
}
protected static ItemFilter getTagFilter(String rule) {
final String[] parts = rule.split("=", 2);
final String[] path = parts[0].split("\\.");
final Predicate<String> value = getMatcher(parts[1]);
return stack -> {
Object tag = stack.getTagCompound();
for (int i = 0; i < path.length && tag != null; i++) {
if (tag instanceof NBTTagCompound) {
tag = ((NBTTagCompound) tag).getTag(path[i]);
} else if (tag instanceof NBTTagList) {
tag = ((NBTTagList) tag).tagList.get(Integer.parseInt(path[i]));
} else {
tag = null;
}
}
return tag == null ? value == null : value != null && value.test(tag.toString());
};
}
protected static ItemFilter getDamageFilter(String rule) {
final String[] range = rule.split("-");
final IntPredicate matcher;
if (range.length == 1) {
final int damage = Integer.parseInt(range[0]);
matcher = dmg -> dmg == damage;
} else {
final int damageStart = Integer.parseInt(range[0]);
final int damageEnd = Integer.parseInt(range[1]);
matcher = dmg -> dmg >= damageStart && dmg <= damageEnd;
}
return stack -> matcher.test(stack.getItemDamage());
}
protected static ItemFilter getStringIdentifierFilter(String rule) {
final FMLControlledNamespacedRegistry<Item> iItemRegistry = GameData.getItemRegistry();
final Predicate<String> matcher = getMatcher(rule);
return stack -> {
String name = iItemRegistry.getNameForObject(stack.getItem());
return name != null && !name.isEmpty() && matcher.test(name);
};
}
}
@@ -2,15 +2,22 @@
#
# One line - one group
# Item added in first valid group
# | - logical or. multi-item search (wrench|hammer)
# $ - search by ore dictionary ($ore)
# ! - logical not. exclude items that match the following expression (-@minecraft)
# r/.../ - standard java regex (@r/m\w{6}ft/ = @minecraft)
# 0 - damage filter
# 0-12 - damage range filter
# , - logical or in token
#
#
# 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
#
minecraft:spawn_egg
minecraft:mob_spawner
minecraft:record_
@@ -53,4 +53,5 @@ miscutils:particleIon,Damage
miscutils:personalHealingDevice
tectech:item.em.debugContainer,tag.symbols
tinkersdefense:Heater Shield,tag.InfiTool
tinkersdefense:Round Shield,tag.InfiTool
tinkersdefense:Round Shield,tag.InfiTool
miscutils:itemCustomMetaCover.miscutils.GtMachineCasings,Damage
@@ -0,0 +1,24 @@
# 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
#
minecraft:portal
minecraft:end_portal
minecraft:fire
minecraft:lava
minecraft:mob_spawner
minecraft:anvil !0
minecraft:flowing_water
minecraft:flowing_lava
@@ -104,6 +104,26 @@ nei.options.inventory.search.patternMode=Suchmodus
nei.options.inventory.search.patternMode.0=Gewöhnlich
nei.options.inventory.search.patternMode.1=Erweitert
nei.options.inventory.search.patternMode.2=Regex
nei.options.inventory.search.modNameSearchMode=Modname Suchmodus
nei.options.inventory.search.modNameSearchMode.0=Immer
nei.options.inventory.search.modNameSearchMode.1=Präfix: %s
nei.options.inventory.search.modNameSearchMode.2=Nie
nei.options.inventory.search.tooltipSearchMode=Tooltip Suchmodus
nei.options.inventory.search.tooltipSearchMode.0=Immer
nei.options.inventory.search.tooltipSearchMode.1=Präfix: %s
nei.options.inventory.search.tooltipSearchMode.2=Nie
nei.options.inventory.search.identifierSearchMode=Identifikator Suchmodus
nei.options.inventory.search.identifierSearchMode.0=Immer
nei.options.inventory.search.identifierSearchMode.1=Präfix: %s
nei.options.inventory.search.identifierSearchMode.2=Nie
nei.options.inventory.search.oreDictSearchMode=OreDict Suchmodus
nei.options.inventory.search.oreDictSearchMode.0=Immer
nei.options.inventory.search.oreDictSearchMode.1=Präfix: %s
nei.options.inventory.search.oreDictSearchMode.2=Nie
nei.options.inventory.search.subsetsSearchMode=Item-SubSet Suchmodus
nei.options.inventory.search.subsetsSearchMode.0=Immer
nei.options.inventory.search.subsetsSearchMode.1=Präfix: %s
nei.options.inventory.search.subsetsSearchMode.2=Nie
nei.options.inventory.utilities=Nützliches
nei.options.inventory.utilities.tip=Wähle Inhalte für den Mehrzweckmodus
nei.options.inventory.utilities.time=Zeit
@@ -190,9 +190,6 @@ nei.options.inventory.bookmarks.ignorePotionOverlap.false=No
nei.options.inventory.bookmarks.worldSpecific=World Specific Bookmarks
nei.options.inventory.bookmarks.worldSpecific.true=Yes
nei.options.inventory.bookmarks.worldSpecific.false=No
nei.options.inventory.worldSpecificPresets=World Specific Presets
nei.options.inventory.worldSpecificPresets.true=Yes
nei.options.inventory.worldSpecificPresets.false=No
nei.options.inventory.guirecipe=Recipes GUI
nei.options.inventory.guirecipe.jeiStyleTabs=JEI Style Tabs
nei.options.inventory.guirecipe.jeiStyleTabs.true=Yes
@@ -190,9 +190,6 @@ nei.options.inventory.bookmarks.ignorePotionOverlap.false=Нет
nei.options.inventory.bookmarks.worldSpecific=Закладки для конкретного мира
nei.options.inventory.bookmarks.worldSpecific.true=Да
nei.options.inventory.bookmarks.worldSpecific.false=Нет
nei.options.inventory.worldSpecificPresets=Пресеты для конкретного мира
nei.options.inventory.worldSpecificPresets.true=Да
nei.options.inventory.worldSpecificPresets.false=Нет
nei.options.inventory.guirecipe=Интерфейс рецептов
nei.options.inventory.guirecipe.jeiStyleTabs=Вкладки в стиле JEI
nei.options.inventory.guirecipe.jeiStyleTabs.true=Да
@@ -131,9 +131,6 @@ nei.options.inventory=物品栏
nei.options.inventory.bookmarks.worldSpecific=书签存储位置
nei.options.inventory.bookmarks.worldSpecific.true=存档内
nei.options.inventory.bookmarks.worldSpecific.false=全局
nei.options.inventory.worldSpecificPresets=预设存储位置
nei.options.inventory.worldSpecificPresets.true=存档内
nei.options.inventory.worldSpecificPresets.false=全局
nei.options.inventory.bookmarks.enabled=显示/隐藏书签页
nei.options.inventory.bookmarks.enabled.true=显示
nei.options.inventory.bookmarks.enabled.false=隐藏