Files
NotEnoughItems/src/main/java/codechicken/nei/FavoriteRecipes.java
T

403 lines
13 KiB
Java

package codechicken.nei;
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.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.io.IOUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import codechicken.core.CommonUtils;
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;
import codechicken.nei.util.FavoriteStorage;
import codechicken.nei.util.NBTJson;
public class FavoriteRecipes {
protected static final class FastKey {
public final ItemStack stack;
private int hash;
public FastKey(ItemStack stack) {
this.stack = stack;
this.hash = 31 + Item.getIdFromItem(stack.getItem());
this.hash = 31 * this.hash + stack.getItemDamage();
}
@Override
public int hashCode() {
return this.hash;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof FastKey k && this.stack.getItem() == k.stack.getItem()
&& this.stack.getItemDamage() == k.stack.getItemDamage()) {
return this.stack == k.stack || Objects.equals(this.stack.stackTagCompound, k.stack.stackTagCompound);
}
return false;
}
}
private static final RestartableTask singleRecipeFavoritesTask = new RestartableTask(
"Generate Single-Recipe Favorites") {
@Override
public void execute() {
final List<ICraftingHandler> handlers = GuiCraftingRecipe.getCraftingHandlers("all");
final Map<FastKey, RecipeHandlerRef> autogeneratedRecipes = new HashMap<>();
final Set<FastKey> whitelistItems = new HashSet<>(ItemList.items.size());
final FavoriteStorage storage = new FavoriteStorage();
for (ItemStack stack : ItemList.items) {
whitelistItems.add(new FastKey(stack));
}
if (interrupted()) return;
for (ICraftingHandler handler : handlers) {
if (!GuiRecipeTab.getHandlerInfo(handler).getShowFavoritesButton()) continue;
for (int i = 0; i < handler.numRecipes(); i++) {
RecipeHandlerRef ref = null;
for (PositionedStack result : getOutputs(handler, i)) {
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(result.items);
final FastKey key = new FastKey(stack);
if (!whitelistItems.contains(key)) continue;
if (autogeneratedRecipes.get(key) == null) {
if (ref == null) {
ref = RecipeHandlerRef.of(handler, i);
}
autogeneratedRecipes.put(key, ref);
} else {
autogeneratedRecipes.remove(key);
whitelistItems.remove(key);
}
}
}
if (interrupted()) return;
}
final Map<RecipeHandlerRef, RecipeId> recipesCache = new HashMap<>();
for (Map.Entry<FastKey, RecipeHandlerRef> entry : autogeneratedRecipes.entrySet()) {
final RecipeId recipeId = recipesCache
.computeIfAbsent(entry.getValue(), r -> RecipeId.of(r.handler, r.recipeIndex));
storage.add(entry.getKey().stack, recipeId);
}
FavoriteRecipes.autoFavorites = storage;
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";
private static final FavoriteStorage manualFavorites = new FavoriteStorage();
private static FavoriteStorage autoFavorites = new FavoriteStorage();
private static File favoriteFile;
private static int revision = 0;
static {
API.addSubset("Favorites.Manual", manualFavorites::contains);
API.addSubset("Favorites.Generated", stack -> FavoriteRecipes.autoFavorites.contains(stack));
}
private FavoriteRecipes() {}
public static void load() {
String worldPath = "global";
if (NEIClientConfig.getBooleanSetting("inventory.favorites.worldSpecific")) {
worldPath = NEIClientConfig.getWorldPath();
}
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
if (!dir.exists()) {
dir.mkdirs();
}
favoriteFile = new File(dir, "favorites.ini");
if (!favoriteFile.exists()) {
final File globalFavorites = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/favorites.ini");
final File configFavorites = new File(NEIClientConfig.configDir, "favorites.ini");
final File defaultFavorites = configFavorites.exists() ? configFavorites : globalFavorites;
if (defaultFavorites.exists()) {
try {
if (favoriteFile.createNewFile()) {
InputStream src = new FileInputStream(defaultFavorites);
OutputStream dst = new FileOutputStream(favoriteFile);
IOUtils.copy(src, dst);
src.close();
dst.close();
}
} catch (IOException e) {}
}
}
loadData();
}
private static void loadData() {
if (favoriteFile == null || !favoriteFile.exists()) {
return;
}
List<String> itemStrings;
try (FileInputStream reader = new FileInputStream(favoriteFile)) {
NEIClientConfig.logger.info("Loading favorites from file {}", favoriteFile);
itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load favorites from file {}", favoriteFile, e);
return;
}
final JsonParser parser = new JsonParser();
boolean hasErrors = false;
manualFavorites.clear();
for (String itemStr : itemStrings) {
if (itemStr.isEmpty() || itemStr.startsWith("#")) {
continue;
}
try {
JsonObject jsonObject = parser.parse(itemStr).getAsJsonObject();
if (jsonObject.get("item") != null && jsonObject.get("recipeId") != null) {
final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
final RecipeId recipeId = RecipeId.of(jsonObject.getAsJsonObject("recipeId"));
final ItemStack stack = StackInfo.loadFromNBT(itemStackNBT);
if (recipeId == null || stack == null) {
continue;
}
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
manualFavorites.add(stack, recipeId);
} else {
hasErrors = true;
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to load favorite from json string: {}", itemStr);
hasErrors = true;
}
}
if (hasErrors) {
NEIClientUtils.backupFile(favoriteFile);
save();
}
loadAutoGeneratedRecipes();
}
public static RecipeId getGeneratedFavorite(ItemStack stack) {
if (NEIClientConfig.favoritesEnabled() && stack != null) {
return autoFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getGeneratedFavorite(RecipeId recipeId) {
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
return autoFavorites.getItemStack(recipeId);
}
return null;
}
public static RecipeId getManualFavorite(ItemStack stack) {
if (NEIClientConfig.favoritesEnabled() && stack != null) {
return manualFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getManualFavorite(RecipeId recipeId) {
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
return manualFavorites.getItemStack(recipeId);
}
return null;
}
public static RecipeId getFavorite(ItemStack stack) {
if (NEIClientConfig.favoritesEnabled() && stack != null) {
final RecipeId recipeId = manualFavorites.getRecipeId(stack);
return recipeId != null ? recipeId : autoFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getFavorite(RecipeId recipeId) {
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
final ItemStack stack = manualFavorites.getItemStack(recipeId);
return stack != null ? stack : autoFavorites.getItemStack(recipeId);
}
return null;
}
public static int size() {
return manualFavorites.size() + autoFavorites.size();
}
public static void reload() {
save();
load();
}
public static boolean hasManual() {
return manualFavorites.size() > 0;
}
public static boolean containsManual(ItemStack stack) {
return getManualFavorite(stack) != null;
}
public static boolean contains(ItemStack stack) {
return getFavorite(stack) != null;
}
public static void toggleFavoriteItem(ItemStack stack) {
if (stack == null) return;
if (containsManual(stack)) {
setFavorite(stack, null);
} else {
setFavorite(
stack,
RecipeId.of(
stack,
FavoriteRecipes.FAVORITE_ITEM,
Arrays.asList(new ItemStack(Blocks.fire, 1), stack)));
}
}
public static void setFavorite(ItemStack stack, RecipeId recipeId) {
if (stack == null) return;
manualFavorites.removeItemStack(stack);
if (recipeId != null) {
manualFavorites.removeRecipeId(recipeId);
manualFavorites.add(stack, recipeId);
}
updateSubsetVisibility();
}
public static void loadAutoGeneratedRecipes() {
if (ItemList.loadFinished
&& NEIClientConfig.getBooleanSetting("inventory.favorites.generateSingleRecipeFavorites")) {
singleRecipeFavoritesTask.restart();
} else {
singleRecipeFavoritesTask.stop();
autoFavorites.clear();
updateSubsetVisibility();
}
}
protected static void updateSubsetVisibility() {
SubsetWidget.updateHiddenItems();
FavoriteRecipes.revision++;
}
public static int getRevision() {
return revision;
}
public static void save() {
if (favoriteFile == null) {
return;
}
final List<String> strings = new ArrayList<>();
for (Map.Entry<NBTTagCompound, RecipeId> entry : manualFavorites.getAllFavorites()) {
try {
final JsonObject line = new JsonObject();
line.add("item", NBTJson.toJsonObject(entry.getKey()));
line.add("recipeId", entry.getValue().toJsonObject());
strings.add(NBTJson.toJson(line));
} catch (JsonSyntaxException e) {
NEIClientConfig.logger.error("Failed to stringify favorites ItemStack to json string");
}
}
try (FileOutputStream output = new FileOutputStream(favoriteFile)) {
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save favorites list to file {}", favoriteFile, e);
}
}
}