package com.forgeessentials.economy.commands;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.minecraft.command.ICommandSender;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.item.crafting.ShapelessRecipes;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.common.config.Property.Type;
import net.minecraftforge.oredict.ShapedOreRecipe;
import net.minecraftforge.oredict.ShapelessOreRecipe;
import net.minecraftforge.permission.PermissionLevel;
import com.forgeessentials.api.APIRegistry;
import com.forgeessentials.core.ForgeEssentials;
import com.forgeessentials.core.commands.ParserCommandBase;
import com.forgeessentials.economy.ModuleEconomy;
import com.forgeessentials.util.CommandParserArgs;
import cpw.mods.fml.common.registry.GameData;
public class CommandCalculatePriceList extends ParserCommandBase
{
@Override
public String getCommandName()
{
return "calcpricelist";
}
@Override
public String getPermissionNode()
{
return ModuleEconomy.PERM_COMMAND + ".calcpricelist";
}
@Override
public PermissionLevel getPermissionLevel()
{
return PermissionLevel.OP;
}
@Override
public String getCommandUsage(ICommandSender p_71518_1_)
{
return "/calcpricelist [save]: Generate (and optionally directly apply) new price list";
}
@Override
public boolean canConsoleUseCommand()
{
return true;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static List<ItemStack> castItemStackList(List itemStackList)
{
return itemStackList;
}
public static String getItemId(Item item)
{
return GameData.getItemRegistry().getNameForObject(item);
}
@Override
public void parse(final CommandParserArgs arguments)
{
calcPriceList(arguments);
}
public static void calcPriceList(CommandParserArgs arguments)
{
/*
* Map<Item, Double> priceMap = new TreeMap<>(new Comparator<Item>() {
*
* @Override public int compare(Item a, Item b) { try { String aId =
* GameData.getItemRegistry().getNameForObject(a); String bId = GameData.getItemRegistry().getNameForObject(b);
* return aId.compareTo(bId); } catch (Exception e) { return 0; } } });
*/
Map<String, Double> priceMap = new TreeMap<>();
Map<String, Double> priceMapFull = new TreeMap<>();
File priceFile = new File(ForgeEssentials.getFEDirectory(), "prices.txt");
File allPricesFile = new File(ForgeEssentials.getFEDirectory(), "prices_all.txt");
File priceLogFile = new File(ForgeEssentials.getFEDirectory(), "prices_log.txt");
try (BufferedReader reader = new BufferedReader(new FileReader(priceFile)))
{
Pattern pattern = Pattern.compile("\\s*I:\"([^\"]+)\"\\s*=\\s*(.*)");
while (reader.ready())
{
String line = reader.readLine();
Matcher match = pattern.matcher(line);
if (!match.matches())
continue;
try
{
priceMap.put(match.group(1), Double.parseDouble(match.group(2)));
}
catch (NumberFormatException e)
{
/* do nothing */
}
}
}
catch (IOException e)
{
arguments.warn(String.format("Could not load %s. Using default values", priceFile.getName()));
initializeDefaultPrices(priceMap);
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(priceLogFile)))
{
// for (Entry<String, Double> entry : priceMap.entrySet())
// writer.write(String.format("%0$-40s = %d\n", entry.getKey(), (int) Math.floor(entry.getValue())));
// writer.write("\n");
// writer.write("\n");
int iterateCount = 0;
boolean changedAnyPrice;
do
{
iterateCount++;
if (iterateCount > 16)
{
arguments.error("WARNING: Infinite loop found in recipes. Cannot calculate prices reliably!");
return;
}
changedAnyPrice = false;
@SuppressWarnings("unchecked")
Map<ItemStack, ItemStack> furnaceRecipes = new HashMap<>(FurnaceRecipes.smelting().getSmeltingList());
@SuppressWarnings("unchecked")
List<IRecipe> recipes = new ArrayList<>(CraftingManager.getInstance().getRecipeList());
File craftRecipesFile = new File(ForgeEssentials.getFEDirectory(), "craft_recipes.txt");
try (BufferedWriter craftRecipes = new BufferedWriter(new FileWriter(craftRecipesFile)))
{
for (Iterator<IRecipe> iterator = recipes.iterator(); iterator.hasNext();)
{
IRecipe recipe = iterator.next();
if (recipe.getRecipeOutput() == null)
continue;
List<?> recipeItems = getRecipeItems(recipe);
if (recipeItems == null)
continue;
craftRecipes.write(String.format("%s:%d\n", getItemId(recipe.getRecipeOutput().getItem()), recipe.getRecipeOutput().getItemDamage()));
for (Object stacks : recipeItems)
if (stacks != null)
{
ItemStack stack = null;
if (stacks instanceof List<?>)
{
if (!((List<?>) stacks).isEmpty())
stack = (ItemStack) ((List<?>) stacks).get(0);
}
else
stack = (ItemStack) stacks;
if (stack != null)
craftRecipes.write(String.format(" %s:%d\n", getItemId(stack.getItem()), stack.getItemDamage()));
}
}
}
boolean changedPrice;
do
{
changedPrice = false;
for (Iterator<IRecipe> iterator = recipes.iterator(); iterator.hasNext();)
{
IRecipe recipe = iterator.next();
if (recipe.getRecipeOutput() == null)
continue;
double price = getRecipePrice(recipe, priceMap, priceMapFull);
if (price > 0)
{
iterator.remove();
price /= recipe.getRecipeOutput().stackSize;
Double resultPrice = priceMap.get(ModuleEconomy.getItemIdentifier(recipe.getRecipeOutput()));
if (resultPrice == null || price < resultPrice)
{
priceMap.put(ModuleEconomy.getItemIdentifier(recipe.getRecipeOutput()), price);
changedPrice = true;
String msg = String.format("%s:%d = %.0f -> %s", getItemId(recipe.getRecipeOutput().getItem()), recipe.getRecipeOutput()
.getItemDamage(), resultPrice == null ? 0 : resultPrice, (int) price);
for (Object stacks : getRecipeItems(recipe))
if (stacks != null)
{
ItemStack stack = null;
if (stacks instanceof List<?>)
{
if (!((List<?>) stacks).isEmpty())
stack = (ItemStack) ((List<?>) stacks).get(0);
}
else
stack = (ItemStack) stacks;
if (stack != null)
msg += String.format("\n %.0f - %s:%d", priceMap.get(ModuleEconomy.getItemIdentifier(stack)),
getItemId(stack.getItem()), stack.getItemDamage());
}
writer.write(msg + "\n");
}
}
}
for (Iterator<Entry<ItemStack, ItemStack>> iterator = furnaceRecipes.entrySet().iterator(); iterator.hasNext();)
{
Entry<ItemStack, ItemStack> recipe = iterator.next();
Double inPrice = priceMap.get(ModuleEconomy.getItemIdentifier(recipe.getKey()));
if (inPrice != null)
{
iterator.remove();
double outPrice = inPrice * recipe.getKey().stackSize / recipe.getValue().stackSize;
Double resultPrice = priceMap.get(ModuleEconomy.getItemIdentifier(recipe.getValue()));
if (resultPrice == null || outPrice < resultPrice)
{
priceMap.put(ModuleEconomy.getItemIdentifier(recipe.getValue()), outPrice);
writer.write(String.format("%s:%d = %.0f -> %d\n %s\n", getItemId(recipe.getValue().getItem()), recipe.getValue()
.getItemDamage(), resultPrice == null ? 0 : resultPrice, (int) outPrice, getItemId(recipe.getKey().getItem())));
changedPrice = true;
}
}
}
changedAnyPrice |= changedPrice;
}
while (changedPrice);
}
while (changedAnyPrice);
}
catch (IOException e1)
{
e1.printStackTrace();
}
writeMap(priceMap, priceFile);
for (Item item : GameData.getItemRegistry().typeSafeIterable())
{
String id = getItemId(item);
if (!priceMapFull.containsKey(id))
priceMapFull.put(id, 0.0);
}
priceMapFull.putAll(priceMap);
writeMap(priceMapFull, allPricesFile);
if (!arguments.isEmpty() && arguments.remove().equalsIgnoreCase("save"))
{
Configuration config = ForgeEssentials.getConfigManager().getConfig(ModuleEconomy.CONFIG_CATEGORY);
ConfigCategory category = config.getCategory(ModuleEconomy.CATEGORY_ITEM);
for (Entry<String, Double> entry : priceMap.entrySet())
{
category.put(entry.getKey(), new Property(entry.getKey(), Integer.toString((int) Math.floor(entry.getValue())), Type.INTEGER));
APIRegistry.perms.registerPermissionProperty(ModuleEconomy.PERM_PRICE + "." + entry.getKey(),
Integer.toString((int) Math.floor(entry.getValue())));
}
config.save();
arguments.confirm("Calculated and saved new price table");
}
else
{
arguments.confirm("Calculated new prices. Copy the prices you want to use from ./ForgeEssentials/prices.txt into Economy.cfg");
arguments.confirm("You can also use [/calcpricelist save] to directly save the calculated prices");
}
}
private static void initializeDefaultPrices(Map<String, Double> priceMap)
{
priceMap.put("minecraft:wool", 48.0);
for (int i = 1; i <= 15; i++)
priceMap.put("minecraft:wool:" + i, 48.0);
priceMap.put("minecraft:log", 32.0);
for (int i = 1; i <= 6; i++)
priceMap.put("minecraft:log:" + i, 32.0);
priceMap.put("minecraft:log2", 32.0);
for (int i = 1; i <= 6; i++)
priceMap.put("minecraft:log2:" + i, 32.0);
priceMap.put("minecraft:red_flower", 16.0);
for (int i = 1; i <= 8; i++)
priceMap.put("minecraft:red_flower:" + i, 16.0);
priceMap.put("minecraft:dye", 8.0);
for (int i = 1; i <= 15; i++)
priceMap.put("minecraft:dye:" + i, 8.0);
priceMap.put("minecraft:deadbush", 1.0);
priceMap.put("minecraft:dirt", 1.0);
priceMap.put("minecraft:grass", 1.0);
priceMap.put("minecraft:ice", 1.0);
priceMap.put("minecraft:leaves", 1.0);
priceMap.put("minecraft:leaves2", 1.0);
priceMap.put("minecraft:mycelium", 1.0);
priceMap.put("minecraft:netherrack", 1.0);
priceMap.put("minecraft:sand", 1.0);
priceMap.put("minecraft:snow", 1.0);
priceMap.put("minecraft:stone", 1.0);
priceMap.put("minecraft:tallgrass", 1.0);
priceMap.put("minecraft:end_stone", 1.0);
priceMap.put("minecraft:apple", 128.0);
priceMap.put("minecraft:beef", 64.0);
priceMap.put("minecraft:blaze_rod", 1536.0);
priceMap.put("minecraft:bone", 144.0);
priceMap.put("minecraft:brown_mushroom", 32.0);
priceMap.put("minecraft:cactus", 8.0);
priceMap.put("minecraft:chicken", 64.0);
priceMap.put("minecraft:clay_ball", 16.0);
priceMap.put("minecraft:coal", 128.0);
priceMap.put("minecraft:coal:1", 32.0);
priceMap.put("minecraft:cobblestone", 1.0);
priceMap.put("minecraft:cocoa", 128.0);
priceMap.put("minecraft:diamond", 8192.0);
priceMap.put("minecraft:dye:4", 864.0);
priceMap.put("minecraft:emerald", 8192.0);
priceMap.put("minecraft:feather", 48.0);
priceMap.put("minecraft:fish", 64.0);
priceMap.put("minecraft:flint", 4.0);
priceMap.put("minecraft:glass", 1.0);
priceMap.put("minecraft:glowstone_dust", 384.0);
priceMap.put("minecraft:gold_ingot", 225.0);
priceMap.put("minecraft:gravel", 4.0);
priceMap.put("minecraft:egg", 32.0);
priceMap.put("minecraft:ender_pearl", 1024.0);
priceMap.put("minecraft:ghast_tear", 4096.0);
priceMap.put("minecraft:gunpowder", 192.0);
priceMap.put("minecraft:iron_ingot", 256.0);
priceMap.put("minecraft:lava_bucket", 832.0);
priceMap.put("minecraft:leather", 64.0);
priceMap.put("minecraft:magma_cream", 792.0);
priceMap.put("minecraft:melon", 16.0);
priceMap.put("minecraft:melon_block", 144.0);
priceMap.put("minecraft:milk_bucket", 833.0);
priceMap.put("minecraft:obsidian", 64.0);
priceMap.put("minecraft:porkchop", 64.0);
priceMap.put("minecraft:pumpkin", 144.0);
priceMap.put("minecraft:red_mushroom", 32.0);
priceMap.put("minecraft:redstone", 64.0);
priceMap.put("minecraft:reeds", 32.0);
priceMap.put("minecraft:rotten_flesh", 24.0);
priceMap.put("minecraft:sapling", 32.0);
priceMap.put("minecraft:slime_ball", 24.0);
priceMap.put("minecraft:soul_sand", 49.0);
priceMap.put("minecraft:spider_eye", 128.0);
priceMap.put("minecraft:string", 16.0);
priceMap.put("minecraft:vine", 8.0);
priceMap.put("minecraft:water_bucket", 769.0);
priceMap.put("minecraft:waterlily", 16.0);
priceMap.put("minecraft:web", 12.0);
priceMap.put("minecraft:wheat", 24.0);
priceMap.put("minecraft:yellow_flower", 16.0);
// TODO: Prices below mainly guessed - should evaluate if these are good defaults
priceMap.put("minecraft:potato", 16.0);
priceMap.put("minecraft:carrot", 16.0);
priceMap.put("minecraft:quartz", 128.0);
priceMap.put("minecraft:sponge", 256.0);
}
private static void writeMap(Map<String, Double> priceMap, File file)
{
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file)))
{
for (Entry<String, Double> entry : priceMap.entrySet())
{
String id = "I:\"" + entry.getKey() + "\"";
while (id.length() < 50)
id = id + ' ';
writer.write(id + "=" + Integer.toString((int) Math.floor(entry.getValue())) + "\n");
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static List<?> getRecipeItems(IRecipe recipe)
{
if (recipe instanceof ShapelessRecipes)
return ((ShapelessRecipes) recipe).recipeItems;
else if (recipe instanceof ShapedRecipes)
return Arrays.asList(((ShapedRecipes) recipe).recipeItems);
else if (recipe instanceof ShapedOreRecipe)
return Arrays.asList(((ShapedOreRecipe) recipe).getInput());
else if (recipe instanceof ShapelessOreRecipe)
return ((ShapelessOreRecipe) recipe).getInput();
else
return null;
}
public static double getRecipePrice(IRecipe recipe, Map<String, Double> priceMap, Map<String, Double> priceMapFull)
{
double price = 0;
List<?> stackList = getRecipeItems(recipe);
if (stackList == null)
return 0;
for (Object stacks : stackList)
if (stacks != null)
{
Double itemPrice = null;
if (stacks instanceof Collection<?>)
{
for (Object stack : (Collection<?>) stacks)
{
String id = ModuleEconomy.getItemIdentifier((ItemStack) stack);
priceMapFull.put(id, 0.0);
Double p = priceMap.get(id);
if (p != null && (itemPrice == null || p < itemPrice))
itemPrice = p;
}
}
else
{
ItemStack stack = (ItemStack) stacks;
String id = ModuleEconomy.getItemIdentifier(stack);
priceMapFull.put(id, 0.0);
itemPrice = priceMap.get(id);
}
if (itemPrice == null)
return -1;
price += itemPrice;
}
return price;
}
}