mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-08 12:34:46 +00:00
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
411 lines
13 KiB
Java
411 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.common.io.Files;
|
|
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) {
|
|
|
|
try {
|
|
Files.copy(
|
|
favoriteFile,
|
|
new File(favoriteFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
|
|
NEIClientConfig.logger.info("Backed up invalid favorite file to {}", favoriteFile);
|
|
} catch (IOException e) {
|
|
NEIClientConfig.logger.error("Failed to backup invalid favorite file to {}", favoriteFile, e);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|