package com.pahimar.ee.exchange;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.pahimar.ee.api.event.EnergyValueEvent;
import com.pahimar.ee.api.exchange.EnergyValue;
import com.pahimar.ee.api.exchange.IEnergyValueProvider;
import com.pahimar.ee.recipe.RecipeRegistry;
import com.pahimar.ee.reference.Comparators;
import com.pahimar.ee.util.FilterUtils;
import com.pahimar.ee.util.LogHelper;
import com.pahimar.ee.util.SerializationHelper;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.oredict.OreDictionary;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.stream.Collectors;
import static com.pahimar.ee.api.exchange.EnergyValueRegistryProxy.Phase;
public class EnergyValueRegistry {
public static final EnergyValueRegistry INSTANCE = new EnergyValueRegistry();
private ImmutableSortedMap<WrappedStack, EnergyValue> stackValueMap;
private ImmutableSortedMap<EnergyValue, Set<WrappedStack>> valueStackMap;
private final Map<WrappedStack, EnergyValue> preCalculationStackValueMap;
private final Map<WrappedStack, EnergyValue> postCalculationStackValueMap;
private transient SortedSet<WrappedStack> unComputedStacks;
private transient boolean shouldSave;
private transient boolean valuesNeedRegeneration;
public static File energyValuesDirectory;
public static File energyValuesFile;
public static File preCalculationValuesFile;
public static File postCalculationValuesFile;
public static final Marker ENERGY_VALUE_MARKER = MarkerManager.getMarker("EE_ENERGY_VALUE", LogHelper.MOD_MARKER);
private EnergyValueRegistry() {
ImmutableSortedMap.Builder<WrappedStack, EnergyValue> stackMapBuilder = ImmutableSortedMap.naturalOrder();
stackValueMap = stackMapBuilder.build();
preCalculationStackValueMap = new TreeMap<>();
postCalculationStackValueMap = new TreeMap<>();
unComputedStacks = new TreeSet<>();
shouldSave = true;
}
/**
* Returns an {@link EnergyValue} for a {@link Object} in the provided {@link Map>} of {@link WrappedStack}s mapped
* to EnergyValues
*
* <p>The order of checking is as follows;</p>
* <ol>
* <li>{@link ItemStack}s whose {@link Item}s implement {@link IEnergyValueProvider}</li>
* <li>Direct EnergyValue mapping of the provided Object in the provided Map</li>
* <li>The following criteria are only checked (in order) in the event that this is a non-strict query;
* <ol>
* <li>
* ItemStacks that are part of an {@link OreDictionary} entry are checked to see if
* <strong>all</strong> Ores they are registered to have the same non-null EnergyValue assigned to
* it
* <ul>
* <li>
* e.g., ItemStack X is associated with OreDictionary entries A, B and C. An EnergyValue
* would be returned for X only if A, B and C all had the same non-null EnergyValue
* </li>
* </ul>
* </li>
* <li>
* ItemStacks are checked to see if there exist {@link OreDictionary#WILDCARD_VALUE} equivalents
* </li>
* <li>
* {@link OreStack}s are checked to see if all members of the OreDictionary entry represented by the
* OreStack have the same non-null EnergyValue (similar to the case for ItemStacks above)
* </li>
* </ol>
* </li>
* </ol>
*
* @param valueMap a {@link Map} of {@link EnergyValue}'s mapped to {@link WrappedStack}'s
* @param object the {@link Object} that is being checked for a corresponding {@link EnergyValue}
* @param strict whether this is a strict (e.g., only looking for direct value assignment vs associative value
* assignments) query or not
* @return an {@link EnergyValue} if there is one to be found for the provided {@link Object} in the provided Map, null otherwise
*/
public static EnergyValue getEnergyValue(Map<WrappedStack, EnergyValue> valueMap, Object object, boolean strict) {
if (WrappedStack.canBeWrapped(object)) {
WrappedStack wrappedStack = WrappedStack.build(object, 1);
Object wrappedObject = wrappedStack.getObject();
if (wrappedObject instanceof ItemStack && ((ItemStack) wrappedObject).getItem() instanceof IEnergyValueProvider && !strict) {
EnergyValue energyValue = ((IEnergyValueProvider) ((ItemStack) wrappedObject).getItem()).getEnergyValueFor(((ItemStack) wrappedObject));
if (energyValue != null && Float.compare(energyValue.getValue(), 0f) > 0) {
return energyValue;
}
}
if (valueMap != null && !valueMap.isEmpty()) {
/**
* First check to see if the object is a WILDCARD_VALUE ItemStack, as if it is it will match any similar
* ItemStack. If it is check to see how many objects it matches in the given map. If there is at least
* one similar object found, find the lowest energy value out of all the similar objects
*/
if (wrappedObject instanceof ItemStack && ((ItemStack) wrappedObject).getItemDamage() == OreDictionary.WILDCARD_VALUE) {
Set<WrappedStack> equivalentStacks = valueMap.keySet().stream()
.filter(wrappedStack::equals)
.filter(wrappedStack1 -> wrappedStack1.getObject() instanceof ItemStack)
.filter(wrappedStack1 -> ((ItemStack) wrappedStack1.getObject()).getItemDamage() != OreDictionary.WILDCARD_VALUE)
.collect(Collectors.toCollection(TreeSet::new));
if (equivalentStacks.size() >= 1) {
EnergyValue lowestValue = null;
for (WrappedStack wrappedStack1 : equivalentStacks) {
EnergyValue currentValue = getEnergyValue(valueMap, wrappedStack1, strict);
if (lowestValue == null) {
lowestValue = currentValue;
}
else if (currentValue.compareTo(lowestValue) < 0) {
lowestValue = currentValue;
}
}
return lowestValue;
}
}
// Check for an exact mapping
if (valueMap.containsKey(wrappedStack)) {
return valueMap.get(wrappedStack);
}
else if (!strict) {
if (wrappedObject instanceof ItemStack && !((ItemStack) wrappedObject).isEmpty()) {
ItemStack unValuedItemStack = ((ItemStack) wrappedObject).copy();
EnergyValue minEnergyValue = null;
int[] oreIds = OreDictionary.getOreIDs(unValuedItemStack);
if (oreIds.length > 0) {
EnergyValue energyValue = null;
boolean allHaveSameValue = true;
for (int oreId : oreIds) {
String oreName = OreDictionary.getOreName(oreId);
if (!"Unknown".equalsIgnoreCase(oreName)) {
WrappedStack oreStack = WrappedStack.build(new OreStack(oreName));
if (oreStack != null && valueMap.containsKey(oreStack)) {
if (energyValue == null) {
energyValue = valueMap.get(oreStack);
}
else if (!energyValue.equals(valueMap.get(oreStack))) {
allHaveSameValue = false;
}
}
else {
allHaveSameValue = false;
}
}
else {
allHaveSameValue = false;
}
}
if (allHaveSameValue) {
return energyValue;
}
}
else {
for (WrappedStack valuedWrappedStack : valueMap.keySet()) {
if (valuedWrappedStack.getObject() instanceof ItemStack) {
if (Item.getIdFromItem(((ItemStack) valuedWrappedStack.getObject()).getItem()) == Item.getIdFromItem(unValuedItemStack.getItem())) {
ItemStack valuedItemStack = (ItemStack) valuedWrappedStack.getObject();
if (valuedItemStack.getItemDamage() == OreDictionary.WILDCARD_VALUE || unValuedItemStack.getItemDamage() == OreDictionary.WILDCARD_VALUE) {
EnergyValue energyValue = valueMap.get(valuedWrappedStack);
if (energyValue.compareTo(minEnergyValue) < 0) {
minEnergyValue = energyValue;
}
}
}
}
}
}
}
else if (wrappedObject instanceof OreStack) {
OreStack oreStack = (OreStack) wrappedObject;
List<ItemStack> itemStacks = OreDictionary.getOres(oreStack.getOreName());
if (!itemStacks.isEmpty()) {
EnergyValue energyValue = null;
boolean allHaveSameValue = true;
for (ItemStack itemStack : itemStacks) {
WrappedStack wrappedItemStack = WrappedStack.build(itemStack, 1);
if (wrappedItemStack != null && valueMap.containsKey(wrappedItemStack)) {
if (energyValue == null) {
energyValue = valueMap.get(wrappedItemStack);
}
else if (!energyValue.equals(valueMap.get(wrappedItemStack))) {
allHaveSameValue = false;
}
}
else {
allHaveSameValue = false;
}
}
if (allHaveSameValue) {
return energyValue;
}
}
}
}
}
}
return null;
}
/**
* Calculates an {@link EnergyValue} for the provided {@link WrappedStack} output from the provided
* {@link Collection} of WrappedStack inputs and {@link Map} of energy value mappings to objects. We calculate the
* energy value for the output by, for each input, summing the input's energy value * the input's stack size. That
* sum is then divided by the stack size of the output. If <strong>any</strong> of the inputs do not have an energy
* value then no energy value can be calculated for the output - therefore we return null
*
* @param valueMap a {@link Map} of {@link EnergyValue}'s mapped to {@link WrappedStack}'s
* @param wrappedOutput the {@link WrappedStack} output for that the inputs "create"
* @param wrappedInputs a {@link Collection} of {@link WrappedStack}s that "create" the output
* @return an {@link EnergyValue} if there is one that can be calculated, null otherwise
*/
private static EnergyValue computeFromInputs(Map<WrappedStack, EnergyValue> valueMap, WrappedStack wrappedOutput, Collection<WrappedStack> wrappedInputs) {
float sumOfValues = 0f;
for (WrappedStack wrappedInput : wrappedInputs) {
EnergyValue inputValue;
int stackSize = Integer.MIN_VALUE;
// TODO Could be simplified a bit more
if (wrappedInput.getObject() instanceof ItemStack) {
ItemStack inputItemStack = (ItemStack) wrappedInput.getObject();
// Check if we are dealing with a potential fluid
if (FluidUtil.getFluidContained(inputItemStack) != null) {
FluidStack fluidStack = FluidUtil.getFluidContained(inputItemStack);
if (inputItemStack.getItem().getContainerItem(inputItemStack) != null) {
stackSize = fluidStack.amount;
inputValue = getEnergyValue(valueMap, fluidStack, false);
}
else {
inputValue = getEnergyValue(valueMap, wrappedInput, false);
}
}
else if (inputItemStack.getItem().hasContainerItem(inputItemStack)) {
ItemStack inputContainerItemStack = inputItemStack.getItem().getContainerItem(inputItemStack);
// FIXME There is a bug here, see how cakes value is calculated with a milk bucket
if (getEnergyValue(valueMap, inputItemStack, false) != null && getEnergyValue(valueMap, inputContainerItemStack, false) != null) {
float itemStackValue = getEnergyValue(valueMap, inputItemStack, false).getValue();
float containerStackValue = getEnergyValue(valueMap, inputContainerItemStack, false).getValue();
inputValue = new EnergyValue(itemStackValue - containerStackValue);
}
else {
inputValue = null;
}
}
else {
inputValue = getEnergyValue(valueMap, wrappedInput, false);
}
}
else {
inputValue = getEnergyValue(valueMap, wrappedInput, false);
}
if (inputValue != null) {
if (stackSize == Integer.MIN_VALUE) {
stackSize = wrappedInput.getStackSize();
}
sumOfValues += inputValue.getValue() * stackSize;
}
else {
return null;
}
}
return EnergyValue.factor(new EnergyValue(sumOfValues), wrappedOutput.getStackSize());
}
/**
* Returns an {@link ImmutableMap} containing the current energy value mappings
*
* @return an {@link ImmutableMap} containing the current energy value mappings
*/
public ImmutableMap<WrappedStack, EnergyValue> getEnergyValues() {
return stackValueMap;
}
/**
* Returns a {@link Map} containing the pre-calculation energy value mappings
*
* @return a {link Map} containing the pre-calculation energy value mappings
*/
public Map<WrappedStack, EnergyValue> getPreCalculationStackValueMap() {
return preCalculationStackValueMap;
}
/**
* Returns a {@link Map} containing the post-calculation energy value mappings
*
* @return a {@link Map} containing the post-calculation energy value mappings
*/
public Map<WrappedStack, EnergyValue> getPostCalculationStackValueMap() {
return postCalculationStackValueMap;
}
/**
* Checks if there exists an {@link EnergyValue} associated with the provided {@link Object}.
*
* @param object the {@link Object} that is being checked for a corresponding {@link EnergyValue}
* @return true if the provided object has an energy value, false otherwise
*/
public boolean hasEnergyValue(Object object) {
return hasEnergyValue(object, false);
}
/**
* Checks if there exists an {@link EnergyValue} associated with the provided {@link Object}
*
* @param object the {@link Object} that is being checked for a corresponding {@link EnergyValue}
* @param strict whether this is a strict (e.g., only looking for direct value assignment vs associative value
* assignments) query or not
* @return true if the provided object has an energy value, false otherwise
*/
public boolean hasEnergyValue(Object object, boolean strict) {
return getEnergyValue(object, strict) != null;
}
/**
* Returns an {@link EnergyValue} associated with the provided {@link Object} (if there is one)
*
* @param object the {@link Object} that is being checked for a corresponding {@link EnergyValue}
* @return an {@link EnergyValue} if there is one to be found, null otherwise
*/
public EnergyValue getEnergyValue(Object object) {
return getEnergyValue(object, false);
}
/**
* Returns an {@link EnergyValue} associated with the provided {@link Object} (if there is one)
*
* @param object the {@link Object} that is being checked for a corresponding {@link EnergyValue}
* @param strict whether this is a strict (e.g., only looking for direct value assignment vs associative value
* assignments) query or not
* @return an {@link EnergyValue} if there is one to be found, null otherwise
*/
public EnergyValue getEnergyValue(Object object, boolean strict) {
return getEnergyValue(stackValueMap, object, strict);
}
/**
* Returns an {@link EnergyValue} associated with the provided {@link Object} (if there is one)
*
* @param object the {@link Object} that is being checked for a corresponding {@link EnergyValue}
* @param strict whether this is a strict (e.g., only looking for direct value assignment vs associative value
* assignments) query or not
* @return an {@link EnergyValue} if there is one to be found, null otherwise
*/
public EnergyValue getEnergyValueForStack(Object object, boolean strict) {
WrappedStack wrappedObject = WrappedStack.build(object);
EnergyValue energyValue = getEnergyValue(object, strict);
if (wrappedObject != null && energyValue != null) {
return new EnergyValue(energyValue.getValue() * wrappedObject.getStackSize());
}
return null;
}
/**
* TODO Finish JavaDoc
*
* @param start
* @param finish
* @return
*/
public Set<ItemStack> getStacksInRange(Number start, Number finish) {
return getStacksInRange(new EnergyValue(start), new EnergyValue(finish));
}
/**
* TODO Finish JavaDoc
*
* @param lowerBound
* @param upperBound
* @return
*/
public Set<ItemStack> getStacksInRange(EnergyValue lowerBound, EnergyValue upperBound) {
Set<ItemStack> filteredItemStacks = new TreeSet<>(Comparators.ENERGY_VALUE_ITEM_STACK_COMPARATOR);
Set<ItemStack> greaterThanLowerBound = getStacksInRange(getEnergyValues(), lowerBound, false);
Set<ItemStack> lesserThanUpperBound = getStacksInRange(getEnergyValues(), upperBound, true);
if (!greaterThanLowerBound.isEmpty() && !lesserThanUpperBound.isEmpty()) {
for (ItemStack itemStack : greaterThanLowerBound) {
if (lesserThanUpperBound.contains(itemStack)) {
filteredItemStacks.add(itemStack);
}
}
}
else if (!greaterThanLowerBound.isEmpty()) {
return greaterThanLowerBound;
}
else if (!lesserThanUpperBound.isEmpty()) {
return lesserThanUpperBound;
}
return filteredItemStacks;
}
/**
* TODO Finish JavaDoc
*
* @param valueMap
* @param energyValueBound
* @param isUpperBound
* @return
*/
private static Set<ItemStack> getStacksInRange(Map<WrappedStack, EnergyValue> valueMap, EnergyValue energyValueBound, boolean isUpperBound) {
Set itemStacks = filterForItemStacks(valueMap.keySet());
if (valueMap != null) {
if (energyValueBound != null) {
if (isUpperBound) {
return FilterUtils.filterByEnergyValue(valueMap, itemStacks, energyValueBound, FilterUtils.ValueFilterType.VALUE_LOWER_THAN_BOUND, Comparators.ENERGY_VALUE_ITEM_STACK_COMPARATOR);
}
else {
return FilterUtils.filterByEnergyValue(valueMap, itemStacks, energyValueBound, FilterUtils.ValueFilterType.VALUE_GREATER_THAN_BOUND, Comparators.ENERGY_VALUE_ITEM_STACK_COMPARATOR);
}
}
}
return new TreeSet<>(Collections.EMPTY_SET);
}
/**
* Sets an {@link EnergyValue} for the provided {@link Object} (if it can be wrapped in a {@link WrappedStack}.
* Depending on whether or not this is a pre-calculation value assignment it's also possible for the calculated
* energy value map to be recomputed to take into account the new mapping.
*
* @param object the object the energy value is being assigned for
* @param energyValue the energy value being setEnergyValue on the object
* @param phase the {@link Phase} of energy value assignment to set this value for
*/
public void setEnergyValue(Object object, EnergyValue energyValue, Phase phase) {
setEnergyValue(object, energyValue, phase, false);
}
/**
* Sets an {@link EnergyValue} for the provided {@link Object} (if it can be wrapped in a {@link WrappedStack}.
* Depending on whether or not this is a pre-calculation value assignment it's also possible for the calculated
* energy value map to be recomputed to take into account the new mapping.
*
* @param object the object the energy value is being assigned for
* @param energyValue the energy value being setEnergyValue on the object
* @param phase the {@link Phase} of energy value assignment to set this value for
* @param doRegenValues whether or not the energy value map needs recomputing. Only an option if the energy value
* is being assigned in the <code>PRE_CALCULATION</code> phase
*/
public void setEnergyValue(Object object, EnergyValue energyValue, Phase phase, boolean doRegenValues) {
if (WrappedStack.canBeWrapped(object) && energyValue != null && Float.compare(energyValue.getValue(), 0f) > 0) {
WrappedStack wrappedStack = WrappedStack.build(object, 1);
EnergyValue factoredEnergyValue = EnergyValue.factor(energyValue, wrappedStack.getStackSize());
if (phase == Phase.PRE_CALCULATION) {
if (!MinecraftForge.EVENT_BUS.post(new EnergyValueEvent.SetEnergyValueEvent(wrappedStack, factoredEnergyValue, Phase.PRE_CALCULATION))) {
preCalculationStackValueMap.put(wrappedStack, factoredEnergyValue);
if (doRegenValues) {
compute();
}
else {
valuesNeedRegeneration = true;
}
}
}
else if (!MinecraftForge.EVENT_BUS.post(new EnergyValueEvent.SetEnergyValueEvent(wrappedStack, factoredEnergyValue, Phase.POST_CALCULATION))) {
TreeMap<WrappedStack, EnergyValue> valueMap = new TreeMap<>(stackValueMap);
valueMap.put(wrappedStack, energyValue);
ImmutableSortedMap.Builder<WrappedStack, EnergyValue> stackMappingsBuilder = ImmutableSortedMap.naturalOrder();
stackValueMap = stackMappingsBuilder.putAll(valueMap).build();
postCalculationStackValueMap.put(wrappedStack, factoredEnergyValue);
}
LogHelper.trace(ENERGY_VALUE_MARKER, "[{}] Mod '{}' set a {} value of {} on object '{}' with doRegen = {}", Loader.instance().getLoaderState(), Loader.instance().activeModContainer().getModId(), phase, energyValue, wrappedStack, doRegenValues);
}
}
/**
* TODO Finish JavaDoc
*
* @param shouldSave
*/
public void setShouldSave(boolean shouldSave) {
this.shouldSave = shouldSave;
}
/**
* TODO Finish JavaDoc
*
* This is where the magic happens
*/
public void compute() {
valuesNeedRegeneration = false;
// Initialize the "working copy" energy value map
final Map<WrappedStack, EnergyValue> stackValueMap = new TreeMap<>();
unComputedStacks = new TreeSet<>();
// Load in pre calculation value assignments from file
Map<WrappedStack, EnergyValue> fileValueMap = null;
try {
fileValueMap = SerializationHelper.readMapFromFile(preCalculationValuesFile);
}
catch (FileNotFoundException e) {
LogHelper.warn(ENERGY_VALUE_MARKER, "No pre calculation energy values were loaded from file - could not find {}", preCalculationValuesFile.getAbsolutePath());
}
if (fileValueMap != null) {
for (WrappedStack wrappedStack : fileValueMap.keySet()) {
if (wrappedStack != null && wrappedStack.getObject() != null && fileValueMap.get(wrappedStack) != null) {
preCalculationStackValueMap.put(wrappedStack, fileValueMap.get(wrappedStack));
}
}
}
// Add in all pre-calculation energy value mappings
preCalculationStackValueMap.keySet().stream()
.filter(wrappedStack -> wrappedStack != null && wrappedStack.getObject() != null && preCalculationStackValueMap.get(wrappedStack) != null)
.forEach(wrappedStack -> stackValueMap.put(wrappedStack, preCalculationStackValueMap.get(wrappedStack)));
// Calculate values from the known methods to create items, and the pre-calculation value mappings
Map<WrappedStack, EnergyValue> computedStackValueMap = calculateStackValueMap(stackValueMap);
for (WrappedStack wrappedStack : computedStackValueMap.keySet()) {
stackValueMap.put(wrappedStack, computedStackValueMap.get(wrappedStack));
}
// Load in post calculation value assignments from file
fileValueMap = null;
try {
fileValueMap = SerializationHelper.readMapFromFile(postCalculationValuesFile);
}
catch (FileNotFoundException e) {
LogHelper.warn(ENERGY_VALUE_MARKER, "No post calculation energy values were loaded from file - could not find {}", postCalculationValuesFile.getAbsolutePath());
}
if (fileValueMap != null) {
for (WrappedStack wrappedStack : fileValueMap.keySet()) {
if (wrappedStack != null && wrappedStack.getObject() != null && fileValueMap.get(wrappedStack) != null) {
postCalculationStackValueMap.put(wrappedStack, fileValueMap.get(wrappedStack));
}
}
}
// Add in all post-calculation energy value mappings
postCalculationStackValueMap.keySet().stream()
.filter(wrappedStack -> wrappedStack != null && wrappedStack.getObject() != null && postCalculationStackValueMap.get(wrappedStack) != null)
.forEach(wrappedStack -> stackValueMap.put(wrappedStack, postCalculationStackValueMap.get(wrappedStack)));
// Bake the final calculated energy value maps
ImmutableSortedMap.Builder<WrappedStack, EnergyValue> stackMappingsBuilder = ImmutableSortedMap.naturalOrder();
stackMappingsBuilder.putAll(stackValueMap);
this.stackValueMap = stackMappingsBuilder.build();
calculateValueStackMap();
// Save the results to disk
save();
// Report the objects for which we were unable to compute an energy value for
unComputedStacks.stream()
.filter(wrappedStack -> getEnergyValue(stackValueMap, wrappedStack, false) == null)
.forEach(wrappedStack -> LogHelper.info(ENERGY_VALUE_MARKER, "Unable to compute an energy value for {}", wrappedStack));
}
/**
*
* @param stackValueMap
* @return
*/
private Map<WrappedStack, EnergyValue> calculateStackValueMap(Map<WrappedStack, EnergyValue> stackValueMap) {
LogHelper.info(ENERGY_VALUE_MARKER, "Beginning energy value calculation");
long startingTime = System.nanoTime();
Map<WrappedStack, EnergyValue> computedMap = new TreeMap<>(stackValueMap);
Map<WrappedStack, EnergyValue> tempComputedMap = new TreeMap<>();
int passNumber = 0;
while ((passNumber == 0 || tempComputedMap.size() != computedMap.size()) && passNumber < 16) {
long passStartTime = System.nanoTime();
passNumber++;
computedMap.putAll(tempComputedMap);
tempComputedMap = new TreeMap<>(computedMap);
for (WrappedStack recipeOutput : RecipeRegistry.INSTANCE.getRecipeMappings().keySet()) {
WrappedStack unitWrappedStack = WrappedStack.build(recipeOutput, 1);
// We won't attempt to recalculate values that already have a pre-calculation value assignment
if (!stackValueMap.containsKey(unitWrappedStack)) {
for (Set<WrappedStack> recipeInputs : RecipeRegistry.INSTANCE.getRecipeMappings().get(recipeOutput)) {
EnergyValue currentOutputValue = getEnergyValue(tempComputedMap, unitWrappedStack, false);
EnergyValue computedOutputValue = computeFromInputs(tempComputedMap, recipeOutput, recipeInputs);
if (computedOutputValue != null && computedOutputValue.compareTo(currentOutputValue) < 0) {
unComputedStacks.remove(unitWrappedStack);
LogHelper.debug(ENERGY_VALUE_MARKER, "Pass {}: Calculated value {} for object {} with recipe inputs {} and output {}", passNumber, computedOutputValue, unitWrappedStack, recipeInputs, recipeOutput);
tempComputedMap.put(unitWrappedStack, computedOutputValue);
}
else if (computedOutputValue != null) {
unComputedStacks.add(unitWrappedStack);
}
}
}
}
long passDuration = System.nanoTime() - passStartTime;
LogHelper.info(ENERGY_VALUE_MARKER, "Pass {}: Calculated {} different values for objects in {} ms", passNumber, tempComputedMap.size(), passDuration / 100000);
}
long endingTime = System.nanoTime() - startingTime;
LogHelper.info(ENERGY_VALUE_MARKER, "Finished energy value calculation - calculated {} new values for objects in {} ms", computedMap.size() - stackValueMap.size(), endingTime / 100000);
unComputedStacks.stream()
.filter(unComputedStack -> getEnergyValue(computedMap, unComputedStack, false) == null)
.forEach(unComputedStack -> LogHelper.debug(ENERGY_VALUE_MARKER, "Unable to compute an energy value for {}", unComputedStack));
return computedMap;
}
private void calculateValueStackMap() {
SortedMap<EnergyValue, Set<WrappedStack>> tempValueMap = new TreeMap<>();
for (WrappedStack wrappedStack : getEnergyValues().keySet()) {
if (wrappedStack != null) {
EnergyValue energyValue = getEnergyValues().get(wrappedStack);
if (energyValue != null) {
if (tempValueMap.containsKey(energyValue)) {
if (!(tempValueMap.get(energyValue).contains(wrappedStack))) {
tempValueMap.get(energyValue).add(wrappedStack);
}
}
else {
tempValueMap.put(energyValue, new TreeSet<>(Arrays.asList(wrappedStack)));
}
}
}
}
valueStackMap = ImmutableSortedMap.copyOf(tempValueMap);
}
private static Set<ItemStack> filterForItemStacks(Set<WrappedStack> wrappedStacks) {
Set<ItemStack> itemStacks = new TreeSet<>(Comparators.ID_COMPARATOR);
for (WrappedStack wrappedStack : wrappedStacks) {
if (wrappedStack.getObject() instanceof ItemStack) {
itemStacks.add((ItemStack) wrappedStack.getObject());
}
else if (wrappedStack.getObject() instanceof OreStack) {
itemStacks.addAll(OreDictionary.getOres(((OreStack) wrappedStack.getObject()).getOreName()));
}
}
return itemStacks;
}
/**
* Saves the pre-calculation, post-calculation, and calculated energy value maps to disk
*/
public void save() {
/**
* If the current values were synched to us from a server, do not save them to disk as they would override
* the local ones
*/
if (shouldSave) {
if (valuesNeedRegeneration) {
if (energyValuesFile.exists()) {
energyValuesFile.delete();
}
}
else {
SerializationHelper.writeMapToFile(stackValueMap, energyValuesFile);
}
}
SerializationHelper.writeMapToFile(preCalculationStackValueMap, preCalculationValuesFile);
SerializationHelper.writeMapToFile(postCalculationStackValueMap, postCalculationValuesFile);
}
/**
* Loads the pre-calculation, post-calculation, and calculated energy value maps from disk. In the event that either
* the pre/post calculation maps can not be loaded from disk they will be initialized as empty maps. If the
* calculated energy value map can not be loaded from disk then the values will be computed from the pre/post
* calculation maps
*/
public void load() {
// Load in pre calculation value assignments from file
Map<WrappedStack, EnergyValue> fileValueMap = null;
try {
fileValueMap = SerializationHelper.readMapFromFile(preCalculationValuesFile);
}
catch (FileNotFoundException e) {
LogHelper.warn(ENERGY_VALUE_MARKER, "No pre calculation energy values were loaded from file - could not find {}", preCalculationValuesFile.getAbsolutePath());
}
if (fileValueMap != null) {
for (WrappedStack wrappedStack : fileValueMap.keySet()) {
if (wrappedStack != null && wrappedStack.getObject() != null && fileValueMap.get(wrappedStack) != null) {
preCalculationStackValueMap.put(wrappedStack, fileValueMap.get(wrappedStack));
}
}
}
// Load in post calculation value assignments from file
fileValueMap = null;
try {
fileValueMap = SerializationHelper.readMapFromFile(postCalculationValuesFile);
}
catch (FileNotFoundException e) {
LogHelper.warn(ENERGY_VALUE_MARKER, "No post calculation energy values were loaded from file - could not find {}", postCalculationValuesFile.getAbsolutePath());
}
if (fileValueMap != null) {
for (WrappedStack wrappedStack : fileValueMap.keySet()) {
if (wrappedStack != null && wrappedStack.getObject() != null && fileValueMap.get(wrappedStack) != null) {
postCalculationStackValueMap.put(wrappedStack, fileValueMap.get(wrappedStack));
}
}
}
// Load the calculated energy values assignments from file
fileValueMap = null;
try {
fileValueMap = SerializationHelper.readMapFromFile(energyValuesFile);
}
catch (FileNotFoundException e) {
LogHelper.warn("No calculated energy values were loaded from file - could not find {}", energyValuesFile.getAbsolutePath());
LogHelper.info("Recomputing energy values", energyValuesFile.getAbsolutePath());
compute();
}
if (fileValueMap != null) {
ImmutableSortedMap.Builder<WrappedStack, EnergyValue> stackMapBuilder = ImmutableSortedMap.naturalOrder();
for (WrappedStack wrappedStack : fileValueMap.keySet()) {
if (wrappedStack != null && wrappedStack.getObject() != null && fileValueMap.get(wrappedStack) != null) {
stackMapBuilder.put(wrappedStack, fileValueMap.get(wrappedStack));
}
}
stackValueMap = stackMapBuilder.build();
calculateValueStackMap();
}
}
/**
*
*
* @param valueMap
*/
public void load(Map<WrappedStack, EnergyValue> valueMap){
if (valueMap != null) {
setShouldSave(false);
ImmutableSortedMap.Builder<WrappedStack, EnergyValue> stackMappingsBuilder = ImmutableSortedMap.naturalOrder();
valueMap.keySet().stream()
.filter(wrappedStack -> wrappedStack != null && wrappedStack.getObject() != null && valueMap.get(wrappedStack) != null)
.forEach(wrappedStack -> stackMappingsBuilder.put(wrappedStack, valueMap.get(wrappedStack)));
stackValueMap = stackMappingsBuilder.build();
calculateValueStackMap();
}
}
}