/*
* This file is part of Matter Overdrive
* Copyright (c) 2015., Simeon Radivoev, All rights reserved.
*
* Matter Overdrive is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Matter Overdrive is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Matter Overdrive. If not, see <http://www.gnu.org/licenses>.
*/
package matteroverdrive.handler;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameData;
import cpw.mods.fml.common.registry.GameRegistry;
import matteroverdrive.MatterOverdrive;
import matteroverdrive.Reference;
import matteroverdrive.api.events.MOEventRegisterMatterEntry;
import matteroverdrive.api.matter.IMatterRegistry;
import net.minecraft.block.Block;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.item.crafting.ShapelessRecipes;
import net.minecraft.util.MathHelper;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.ShapedOreRecipe;
import net.minecraftforge.oredict.ShapelessOreRecipe;
import java.io.*;
import java.util.*;
public class MatterRegistry implements IMatterRegistry
{
private boolean REGISTRATION_DEBUG = false;
public boolean CALCULATION_DEBUG = false;
public boolean AUTOMATIC_CALCULATION = true;
public boolean CALCULATE_RECIPES = true;
public boolean CALCULATE_FURNACE = true;
public boolean hasComplitedRegistration = false;
private static final int MAX_DEPTH = 8;
public int basicEntries = 0;
private Map<String,MatterEntry> entries = new HashMap<>();
private Set<String> blacklist = Collections.synchronizedSet(new HashSet<>());
private Set<String> modBlacklist = Collections.synchronizedSet(new HashSet<String>());
public void preInit(FMLPreInitializationEvent event,ConfigurationHandler configurationHandler)
{
REGISTRATION_DEBUG = configurationHandler.getBool(ConfigurationHandler.KEY_MATTER_REGISTRATION_DEBUG,ConfigurationHandler.CATEGORY_DEBUG,false,"Enables Debug logging for Matter Registration");
CALCULATION_DEBUG = configurationHandler.getBool(ConfigurationHandler.KEY_MATTER_CALCULATION_DEBUG,ConfigurationHandler.CATEGORY_DEBUG,false,"Enables Debug logging for Matter Calculation");
CALCULATE_RECIPES = configurationHandler.getBool(ConfigurationHandler.KEY_AUTOMATIC_RECIPE_CALCULATION,ConfigurationHandler.CATEGORY_MATTER,true,"Enables Matter Calculation from recipes");
CALCULATE_FURNACE = configurationHandler.getBool(ConfigurationHandler.KEY_AUTOMATIC_FURNACE_CALCULATION,configurationHandler.CATEGORY_MATTER,true,"Enables Matter Calculation from furnace recipes");
AUTOMATIC_CALCULATION = configurationHandler.getBool("automatic_calculation",configurationHandler.CATEGORY_MATTER,true,"Should the matter registry calculation run on world start when recepie ");
}
public MatterEntry register(MatterEntry entry)
{
if (!MinecraftForge.EVENT_BUS.post(new MOEventRegisterMatterEntry(entry)))
{
debug("Registered: %1$s - %2$s kM", entry.getName(), entry.getMatter());
entries.put(entry.getName(), entry);
}
return entry;
}
public void saveToFile(String path) throws IOException {
File file = new File(path);
file.getParentFile().mkdirs();
file.createNewFile();
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(entries);
outputStream.writeUTF(Reference.VERSION);
outputStream.writeInt(CraftingManager.getInstance().getRecipeList().size());
outputStream.writeInt(basicEntries);
outputStream.writeInt(blacklist.size());
outputStream.writeInt(modBlacklist.size());
outputStream.close();
fileOutputStream.close();
}
public void loadFromFile(String path) throws IOException, ClassNotFoundException {
File file = new File(path);
if (file.exists()) {
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
entries = (HashMap<String,MatterEntry>) inputStream.readObject();
String version = inputStream.readUTF();
inputStream.close();
fileInputStream.close();
MatterOverdrive.log.info("Registry Loaded with %1$s entries, from version %2$s from: %3$s", entries.size(), version, file.getPath());
}
}
public boolean needsCalculation(String path) throws IOException, ClassNotFoundException
{
File file = new File(path);
String reason = "";
if (file.exists())
{
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
HashMap<String, MatterEntry> entires = (HashMap<String, MatterEntry>)inputStream.readObject();
String version = inputStream.readUTF();
int recipeCount = inputStream.readInt();
int basicEntries = inputStream.readInt();
int blackListSize = inputStream.readInt();
int modBlacklistSize = inputStream.readInt();
inputStream.close();
fileInputStream.close();
//checks if the saved versions differ from the current version of the mod
//and alos checks if the recipe list count has changed
if (version.equalsIgnoreCase(Reference.VERSION))
{
if (recipeCount == CraftingManager.getInstance().getRecipeList().size())
{
if (basicEntries == this.basicEntries)
{
if (blackListSize == blacklist.size() && modBlacklistSize == modBlacklist.size()) {
for (Map.Entry<String, MatterEntry> entry : entires.entrySet())
{
if (!entry.getValue().getCalculated()) {
if (entries.containsKey(entry.getKey())) {
if (!entries.get(entry.getKey()).equals(entry.getValue())) {
//if the entry is in the list but it's matter was changed
MatterOverdrive.log.warn("Matter Registry has changed! %1$s changed from %2$s to %3$s. Recalculation required!", entry.getKey(), entries.get(entry.getKey()), entry.getValue().getMatter());
return true;
}
}
}
}
return false;
}else
{
reason = "Blacklist changed";
}
}else
{
reason = "Basic Entries size changed";
}
}else
{
reason = "Recipe List Changed";
}
}
}else
{
reason = "Recipe List File missing";
}
//if the registry file is missing then calculate
MatterOverdrive.log.warn(reason + "! Recalculation required!");
return true;
}
@Override
public void addToBlacklist(ItemStack itemStack) {blacklist.add(getKey(itemStack));}
@Override
public void addToBlacklist(String key) {blacklist.add(key);}
@Override
public void addToBlacklist(Item item) {blacklist.add(getKey(item));}
@Override
public void addToBlacklist(Block block) {blacklist.add(getKey(block));}
@Override
public boolean blacklisted(Block block){return blacklist.contains(getKey(block));}
@Override
public boolean blacklisted(Item item){return blacklist.contains(getKey(item));}
@Override
public boolean blacklisted(ItemStack itemStack)
{
return blacklisted(getKey(itemStack.getItem())) || blacklisted(getKey(itemStack));
}
@Override
public boolean blacklisted(String key)
{
return blacklist.contains(key);
}
public boolean blacklistedFromMod(ItemStack stack)
{
Item item = stack.getItem();
if (item != null)
{
return modBlacklist.contains(GameRegistry.findUniqueIdentifierFor(item).modId);
}
return false;
}
public String getKey(Block block) {return getKey(new ItemStack(block));}
public String getKey(Item item) {return getKey(new ItemStack(item));}
public String getKey(ItemStack itemStack) {
try {
if (itemStack.getHasSubtypes())
{
if (itemStack.getItemDamage() > 0 && itemStack.getItemDamage() < Short.MAX_VALUE)
{
return GameData.getItemRegistry().getNameForObject(itemStack.getItem()) + itemStack.getItemDamage();
}
return GameData.getItemRegistry().getNameForObject(itemStack.getItem());
}else
{
return GameData.getItemRegistry().getNameForObject(itemStack.getItem());
}
} catch (Exception e)
{
if (itemStack.getItem() != null)
{
int damage = itemStack.getItemDamage();
damage = MathHelper.clamp_int(damage, 0, itemStack.getMaxDamage());
return itemStack.getItem().getUnlocalizedNameInefficiently(new ItemStack(itemStack.getItem(), 1, damage));
}
return null;
}
}
public MatterEntry register(Block block,int matter) {
if (!blacklisted(block)) {
String key = getKey(block);
int configMatter = checkInConfig(key);
if (configMatter > 0)
return register(new MatterEntry(key, configMatter, (byte) 1));
else
return register(new MatterEntry(key, matter, (byte) 1));
}
return null;
}
public MatterEntry register(Item item,int matter)
{
if (!blacklisted(item)) {
String key = getKey(item);
int configMatter = checkInConfig(key);
if (configMatter > 0)
return register(new MatterEntry(key, configMatter, (byte) 1));
else
return register(new MatterEntry(key, matter, (byte) 1));
}
return null;
}
public MatterEntry register(ItemStack itemStack,int matter)
{
if (!blacklisted(itemStack)) {
String key = getKey(itemStack);
int configMatter = checkInConfig(key);
if (configMatter > 0)
return register(new MatterEntry(key, configMatter, (byte) 1));
else
return register(new MatterEntry(key, matter, (byte) 2));
}
return null;
}
public MatterEntry register(String key,int matter)
{
if (!blacklisted(key)) {
int configMatter = checkInConfig(key);
if (configMatter > 0)
return register(new MatterEntry(key, configMatter, (byte) 1));
else
return register(new MatterEntry(key, matter, (byte) 0));
}
return null;
}
public int checkInConfig(String key){
if (MatterOverdrive.configHandler.config.hasKey(ConfigurationHandler.CATEGORY_OVERRIDE_MATTER, key))
{
return MatterOverdrive.configHandler.getInt(key, ConfigurationHandler.CATEGORY_OVERRIDE_MATTER,-1);
}
return -1;
}
public MatterEntry registerFromRecipe(ItemStack item)
{
int matter = getMatterFromRecipe(item, false, 0,true);
if(matter > 0)
return register(new MatterEntry(getKey(item),matter,(byte)2));
else {
//System.out.println("Could not register "+ getKey(item)+" from recipe");
return null;
}
}
public MatterEntry getEntry(Block block) {return getEntry(new ItemStack(block));}
public MatterEntry getEntry(Item item) {return getEntry(new ItemStack(item));}
public MatterEntry getEntry(ItemStack item)
{
try {
if (!blacklist.contains(getKey(item)))
{
MatterEntry e = entries.get(getKey(item));
if (e == null)
{
if (e == null) debug("Could not find matter entry for: %s", item);
e = getOreDicionaryEntry(item);
if (e == null) debug("Could not find ore dictionary entry for: %s", item);
}
return e;
}else
{
return null;
}
}catch (Exception e)
{
if (e == null) debug("There was a problem getting a Matter Entry for %s.", item);
return null;
}
}
public MatterEntry getEntry(String name)
{
MatterEntry e = entries.get(name);
if(e == null)
{
for (ItemStack itemStack : OreDictionary.getOres(name))
{
e = entries.get(Item.itemRegistry.getNameForObject(itemStack.getItem()));
if(e != null)
return e;
}
}
return e;
}
private MatterEntry getOreDicionaryEntry(ItemStack stack)
{
MatterEntry e;
int[] ids = OreDictionary.getOreIDs(stack);
if (ids.length <= 0)
{
if (stack.getItemDamage() == Short.MAX_VALUE)
{
debug("Messed up damage for: %s.", stack);
}else
{
debug("No OreDictionary support for: %s", stack);
}
return null;
}
for (int id : ids)
{
String entryName = OreDictionary.getOreName(id);
debug("Searching for OreDictionary key with name: %s for item: %s", entryName, stack);
e = entries.get(entryName);
if(e != null)
return e;
}
return null;
}
public int getMatterFromRecipe(Block block,boolean recursive,int depth,boolean calculated)
{
return getMatterFromRecipe(new ItemStack(block), recursive, depth,calculated);
}
public int getMatterFromRecipe(ItemStack item,boolean recursive,int depth,boolean calculated)
{
int matter = 0;
List<IRecipe> recipes = CraftingManager.getInstance().getRecipeList();
for(IRecipe recipe : recipes) {
ItemStack recipeOutput = recipe.getRecipeOutput();
if (recipeOutput != null && recipeOutput.isItemEqual(item)) {
int m = 0;
if (recipe instanceof ShapedRecipes) {
m = getMatterFromList(recipeOutput, ((ShapedRecipes) recipe).recipeItems, recursive, ++depth, calculated);
} else if (recipe instanceof ShapelessRecipes) {
m = getMatterFromList(recipeOutput, ((ShapelessRecipes) recipe).recipeItems.toArray(), recursive, ++depth, calculated);
} else if (recipe instanceof ShapedOreRecipe) {
m = getMatterFromList(recipeOutput, ((ShapedOreRecipe) recipe).getInput(), recursive, ++depth, calculated);
} else if (recipe instanceof ShapelessOreRecipe) {
m = getMatterFromList(recipeOutput, ((ShapelessOreRecipe) recipe).getInput().toArray(), recursive, ++depth, calculated);
}
matter += m;
}
}
return matter;
}
@Override
public void addModToBlacklist(String modID)
{
modBlacklist.add(modID);
}
public void loadNewItemsFromConfig(ConfigurationHandler c)
{
List<Property> category = c.getCategory(ConfigurationHandler.CATEGORY_NEW_ITEMS).getOrderedValues();
for (Property key : category)
{
int value = key.getInt(0);
if (value > 0)
{
register(key.getName(),value);
basicEntries++;
}
}
}
public void loadBlacklistFromConfig(ConfigurationHandler c)
{
String[] list = c.getStringList(ConfigurationHandler.CATEGORY_MATTER, ConfigurationHandler.KEY_MBLACKLIST);
for (String value : list)
{
addToBlacklist(value);
}
}
public void loadModBlacklistFromConfig(ConfigurationHandler c)
{
String[] list = c.getStringList(ConfigurationHandler.CATEGORY_MATTER, ConfigurationHandler.KEY_BLACKLIST_MODS);
for (String value : list)
{
addModToBlacklist(value);
}
}
public int getMatterFromList(ItemStack item, Object[] list,boolean recursive,int depth,boolean calculated)
{
int totalMatter = 0;
int tempMatter;
MatterEntry tempEntry;
if (depth < MAX_DEPTH)
{
for (Object s : list)
{
if (s == null)
continue;
//reset temp vars
tempMatter = 0;
if (s instanceof ItemStack || s instanceof Item || s instanceof Block) {
//converting them all to a stack, for code simplification
ItemStack stack = null;
if (s instanceof ItemStack)
{
stack = (ItemStack)s;
}
else if (s instanceof Block)
{
stack = new ItemStack((Block)s);
}
else if (s instanceof Item)
{
stack = new ItemStack((Item)s);
}
if (stack == null || blacklisted(stack) || blacklistedFromMod(stack)) {
debug("%s is blacklisted.", item);
return -1;
}
//check to see if the item in the recipe is the same as the output
//and if so then do not calculate to save unnecessary lopping
if (!ItemStack.areItemStacksEqual(stack,item)) {
tempEntry = getEntry(stack);
//if there is an entry use it's matter value
if (tempEntry != null)
{
tempMatter = tempEntry.getMatter();
}
//if there is no entry for item and recursive is true, then continue searching to it's recipe list
else if (recursive) {
tempMatter = getMatterFromRecipe(stack, true, ++depth,calculated);
debug("searching %s in depth: %s", stack, depth - 1);
//if the matter is higher than 0 that means the recipe search was successful.
//registration now helps to remove it from future checks
if (tempMatter > 0) {
register(stack, tempMatter).setCalculated(calculated);
}
else if (tempMatter < 0) {
//that means the item had a recipe with a blacklisted item
debug("%s has a blacklisted item in it's recipe", stack);
return -1;
}
else
{
debug("%s cannot be replicated. Contains 0 matter", stack);
return 0;
}
}
//if it's not recursive then check if any item in recipe is not replicatable
else
{
debug("%s cannot be replicated. Contains 0 matter", stack);
return 0;
}
}
}
//this is checking if it's a list of items stack
//this is different from the recipe list, it's oreDictionary list with items with the same ore name
//so recursion can't be used because of the devision of the stack size
//and we want to use the lowest possible matter amount from the list
else if (s instanceof List)
{
List l = (List)s;
//flag for checking if the item in the list was the first to register
//for checking the lowest matter amount of the list
//not using a index check, because the first item can be empty, and nothing positive is smaller than zero
boolean first = true;
for (Object element : l)
{
if (element instanceof ItemStack || element instanceof Item || element instanceof Block)
{
ItemStack stack = null;
if (element instanceof ItemStack)
{
stack = (ItemStack)element;
}
else if (element instanceof Item)
{
stack = new ItemStack((Item)element);
}else if (element instanceof Block)
{
stack = new ItemStack((Block)element);
}
tempEntry = getEntry(stack);
if (tempEntry != null)
{
//if the item has matter, has lower matter than the previous
//if the item was first there is no previous so store that amount
if ( tempEntry.getMatter() > 0)
{
if ((tempEntry.getMatter() < tempMatter || first)) {
tempMatter = tempEntry.getMatter();
first = false;
}
}else
{
debug("entry for %s, found in recipe for: %s was blacklisted or costs lower then previous", stack, item);
}
}
//here we use the recursion to calculate it's matter from any recipes it has
else if (recursive)
{
debug("Could not find Matter entry for %s, found in recipe for: %s", stack, item);
int m = getMatterFromRecipe(stack,true,++depth,calculated);
//if the item has matter, has lower matter than the previous
//if the item was first there is no previous so store that amount
if ( m > 0 && (m < tempMatter || first))
{
tempMatter = m;
first = false;
}else
{
debug("entry for %s, found in recipe for: %s was blacklisted or costs lower then previous", stack, item);
}
}
}else
{
debug("Found another type of object in list ot type: %s", element.getClass().toString());
}
}
//if for same reason the item is invalid, that can't really happen
if (tempMatter < 0)
{
debug("%s is invalid.", item);
return -1;
}
//if all the items in the list have not matter return as empty
else if (tempMatter == 0) {
debug("%s has no items with matter in recipe.", item);
return 0;
}
}
//if the recipe contains anything other than itemsStacks, Items, Blocks or lists
//may be used if there are strings to OreDictionary items i don't really know
else
{
debug("Element in list is unknown type: %s", s);
tempEntry = getEntry(s.toString());
if (tempEntry != null)
{
tempMatter = tempEntry.getMatter();
}
}
//increase the total matter amount
totalMatter += tempMatter;
}
}
//return the amount divided by the count of the items stack
//sometimes after the division the result is 0
return (int) Math.round((double) totalMatter / (double) item.stackSize);
}
private int handleReturns(Item item) {
if (item == Items.lava_bucket || item == Items.water_bucket || item == Items.milk_bucket) {
MatterEntry e = getEntry(Items.bucket);
if (e != null)
return -e.getMatter();
else {
e = getEntry(item);
if (e != null)
return -e.getMatter();
}
}
return 0;
}
public void clearCaluclatedEntries()
{
Iterator<Map.Entry<String,MatterEntry>> iter = entries.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String,MatterEntry> entry = iter.next();
if(entry.getValue().getCalculated()){
iter.remove();
}
}
}
public Map<String,MatterEntry> getEntries()
{
return entries;
}
public void setEntries(Map<String,MatterEntry> entries)
{
this.entries = entries;
}
public void debug(String debug,Object... params)
{
if (REGISTRATION_DEBUG)
{
for (int i = 0;i < params.length;i++)
{
if (params[i] instanceof ItemStack)
{
try
{
params[i] = getKey(((ItemStack)params[i]));
}
catch (Exception e)
{
MatterOverdrive.log.warn("There was a problem getting ItemStack's name");
}
}
}
MatterOverdrive.log.debug(debug,params);
}
}
}