Add new recipe sorter that is called after all mods are initalized. This is disabled by default in 1.6 to not break current worlds as it may change machine's recipy outputs. Will enable by default in 1.7. Players may enable it in the forge config.
This commit is contained in:
parent
a47cca7e42
commit
a4fa417114
2 changed files with 294 additions and 2 deletions
|
@ -15,6 +15,7 @@ import net.minecraftforge.common.network.ForgeConnectionHandler;
|
|||
import net.minecraftforge.common.network.ForgeNetworkHandler;
|
||||
import net.minecraftforge.common.network.ForgePacketHandler;
|
||||
import net.minecraftforge.common.network.ForgeTinyPacketHandler;
|
||||
import net.minecraftforge.oredict.RecipeSorter;
|
||||
import net.minecraftforge.server.command.ForgeCommand;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
@ -29,13 +30,14 @@ import cpw.mods.fml.common.Loader;
|
|||
import cpw.mods.fml.common.ModMetadata;
|
||||
import cpw.mods.fml.common.WorldAccessContainer;
|
||||
import cpw.mods.fml.common.event.FMLConstructionEvent;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
import cpw.mods.fml.common.network.FMLNetworkHandler;
|
||||
import cpw.mods.fml.common.network.NetworkMod;
|
||||
|
||||
import static net.minecraftforge.common.ForgeVersion.*;
|
||||
import static net.minecraftforge.common.Configuration.*;
|
||||
|
||||
@NetworkMod(
|
||||
channels = "FORGE",
|
||||
|
@ -54,6 +56,7 @@ public class ForgeDummyContainer extends DummyModContainer implements WorldAcces
|
|||
public static double zombieSummonBaseChance = 0.1;
|
||||
public static int[] blendRanges = { 20, 15, 10, 5 };
|
||||
public static float zombieBabyChance = 0.05f;
|
||||
public static boolean shouldSortRecipies = false;
|
||||
|
||||
public ForgeDummyContainer()
|
||||
{
|
||||
|
@ -153,7 +156,11 @@ public class ForgeDummyContainer extends DummyModContainer implements WorldAcces
|
|||
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBabyChance", 0.05);
|
||||
prop.comment = "Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.";
|
||||
zombieBabyChance = (float) prop.getDouble(0.05);
|
||||
|
||||
|
||||
prop = config.get(CATEGORY_GENERAL, "sortRecipies", shouldSortRecipies);
|
||||
prop.comment = "Set to true to enable the post initlization sorting of crafting recipes using Froge's sorter. May cause desyncing on conflicting recipies. ToDo: Set to true by default in 1.7";
|
||||
shouldSortRecipies = prop.getBoolean(shouldSortRecipies);
|
||||
|
||||
if (config.hasChanged())
|
||||
{
|
||||
config.save();
|
||||
|
@ -195,6 +202,15 @@ public class ForgeDummyContainer extends DummyModContainer implements WorldAcces
|
|||
ForgeChunkManager.loadConfiguration();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onAvalible(FMLLoadCompleteEvent evt)
|
||||
{
|
||||
if (shouldSortRecipies)
|
||||
{
|
||||
RecipeSorter.sortCraftManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void serverStarting(FMLServerStartingEvent evt)
|
||||
{
|
||||
|
|
276
common/net/minecraftforge/oredict/RecipeSorter.java
Normal file
276
common/net/minecraftforge/oredict/RecipeSorter.java
Normal file
|
@ -0,0 +1,276 @@
|
|||
package net.minecraftforge.oredict;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import cpw.mods.fml.common.DummyModContainer;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
import cpw.mods.fml.common.toposort.TopologicalSort;
|
||||
import cpw.mods.fml.common.toposort.TopologicalSort.DirectedGraph;
|
||||
import cpw.mods.fml.common.versioning.ArtifactVersion;
|
||||
import net.minecraft.item.crafting.CraftingManager;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.RecipeFireworks;
|
||||
import net.minecraft.item.crafting.RecipesArmorDyes;
|
||||
import net.minecraft.item.crafting.RecipesMapCloning;
|
||||
import net.minecraft.item.crafting.RecipesMapExtending;
|
||||
import net.minecraft.item.crafting.ShapedRecipes;
|
||||
import net.minecraft.item.crafting.ShapelessRecipes;
|
||||
import static net.minecraftforge.oredict.RecipeSorter.Category.*;
|
||||
|
||||
public class RecipeSorter implements Comparator<IRecipe>
|
||||
{
|
||||
public enum Category
|
||||
{
|
||||
UNKNOWN,
|
||||
SHAPELESS,
|
||||
SHAPED
|
||||
};
|
||||
|
||||
private static class SortEntry
|
||||
{
|
||||
private String name;
|
||||
private Class cls;
|
||||
private Category cat;
|
||||
List<String> before = Lists.newArrayList();
|
||||
List<String> after = Lists.newArrayList();
|
||||
|
||||
private SortEntry(String name, Class cls, Category cat, String deps)
|
||||
{
|
||||
this.name = name;
|
||||
this.cls = cls;
|
||||
this.cat = cat;
|
||||
parseDepends(deps);
|
||||
}
|
||||
|
||||
private void parseDepends(String deps)
|
||||
{
|
||||
if (deps.isEmpty()) return;
|
||||
for (String dep : deps.split(" "))
|
||||
{
|
||||
if (dep.startsWith("before:"))
|
||||
{
|
||||
before.add(dep.substring(7));
|
||||
}
|
||||
else if (dep.startsWith("after:"))
|
||||
{
|
||||
after.add(dep.substring(6));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid dependancy: " + dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("RecipeEntry(\"").append(name).append("\", ");
|
||||
buf.append(cat.name()).append(", ");
|
||||
buf.append(cls == null ? "" : cls.getName()).append(")");
|
||||
|
||||
if (before.size() > 0)
|
||||
{
|
||||
buf.append(" Before: ").append(Joiner.on(", ").join(before));
|
||||
}
|
||||
|
||||
if (after.size() > 0)
|
||||
{
|
||||
buf.append(" After: ").append(Joiner.on(", ").join(after));
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return name.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
private static Map<Class, Category> categories = Maps.newHashMap();
|
||||
private static Map<String, Class> types = Maps.newHashMap();
|
||||
private static Map<String, SortEntry> entries = Maps.newHashMap();
|
||||
private static Map<Class, Integer> priorities = Maps.newHashMap();
|
||||
|
||||
public static RecipeSorter INSTANCE = new RecipeSorter();
|
||||
private static boolean isDirty = true;
|
||||
|
||||
private static SortEntry before = new SortEntry("Before", null, UNKNOWN, "");
|
||||
private static SortEntry after = new SortEntry("After", null, UNKNOWN, "");
|
||||
|
||||
private RecipeSorter()
|
||||
{
|
||||
register("minecraft:shaped", ShapedRecipes.class, SHAPED, "before:minecraft:shapeless");
|
||||
register("minecraft:mapextending", RecipesMapExtending.class, SHAPED, "after:minecraft:shaped before:minecraft:shapeless");
|
||||
register("minecraft:shapeless", ShapelessRecipes.class, SHAPELESS, "after:minecraft:shaped");
|
||||
register("minecraft:fireworks", RecipeFireworks.class, SHAPELESS, "after:minecraft:shapeless");
|
||||
register("minecraft:armordyes", RecipesArmorDyes.class, SHAPELESS, "after:minecraft:shapeless");
|
||||
register("minecraft:mapcloning", RecipesMapCloning.class, SHAPELESS, "after:minecraft:shapeless");
|
||||
|
||||
register("forge:shapedore", ShapedOreRecipe.class, SHAPED, "after:minecraft:shaped before:minecraft:shapeless");
|
||||
register("forge:shapelessore", ShapelessOreRecipe.class, SHAPELESS, "after:minecraft:shapeless");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(IRecipe r1, IRecipe r2)
|
||||
{
|
||||
Category c1 = getCategory(r1);
|
||||
Category c2 = getCategory(r2);
|
||||
if (c1 == SHAPELESS && c2 == SHAPED) return 1;
|
||||
if (c1 == SHAPED && c2 == SHAPELESS) return -1;
|
||||
if (r2.getRecipeSize() < r1.getRecipeSize()) return -1;
|
||||
if (r2.getRecipeSize() > r1.getRecipeSize()) return 1;
|
||||
return getPriority(r2) - getPriority(r1); // high priority value first!
|
||||
}
|
||||
|
||||
private static Set<Class> warned = Sets.newHashSet();
|
||||
public static void sortCraftManager()
|
||||
{
|
||||
bake();
|
||||
FMLLog.fine("Sorting recipies");
|
||||
warned.clear();
|
||||
Collections.sort(CraftingManager.getInstance().getRecipeList(), INSTANCE);
|
||||
}
|
||||
|
||||
public static void register(String name, Class recipe, Category category, String dependancies)
|
||||
{
|
||||
assert(category != UNKNOWN) : "Category must not be unknown!";
|
||||
isDirty = true;
|
||||
|
||||
SortEntry entry = new SortEntry(name, recipe, category, dependancies);
|
||||
entries.put(name, entry);
|
||||
setCategory(recipe, category);
|
||||
}
|
||||
|
||||
public static void setCategory(Class recipe, Category category)
|
||||
{
|
||||
assert(category != UNKNOWN) : "Category must not be unknown!";
|
||||
categories.put(recipe, category);
|
||||
}
|
||||
|
||||
public static Category getCategory(IRecipe recipe)
|
||||
{
|
||||
return getCategory(recipe.getClass());
|
||||
}
|
||||
|
||||
public static Category getCategory(Class recipe)
|
||||
{
|
||||
Class cls = recipe;
|
||||
Category ret = categories.get(cls);
|
||||
|
||||
if (ret == null)
|
||||
{
|
||||
cls = cls.getSuperclass();
|
||||
while (cls != Object.class)
|
||||
{
|
||||
ret = categories.get(cls);
|
||||
if (ret != null)
|
||||
{
|
||||
categories.put(recipe, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret == null ? UNKNOWN : ret;
|
||||
}
|
||||
|
||||
private static int getPriority(IRecipe recipe)
|
||||
{
|
||||
Class cls = recipe.getClass();
|
||||
Integer ret = priorities.get(cls);
|
||||
|
||||
if (ret == null)
|
||||
{
|
||||
if (!INSTANCE.warned.contains(cls))
|
||||
{
|
||||
FMLLog.fine(" Unknown recipe class! %s Modder please refer to %s", cls.getName(), RecipeSorter.class.getName());
|
||||
warned.add(cls);
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
while (cls != Object.class)
|
||||
{
|
||||
ret = priorities.get(cls);
|
||||
if (ret != null)
|
||||
{
|
||||
priorities.put(recipe.getClass(), ret);
|
||||
FMLLog.fine(" Parent Found: %d - %s", ret.intValue(), cls.getName());
|
||||
return ret.intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret == null ? 0 : ret.intValue();
|
||||
}
|
||||
|
||||
private static void bake()
|
||||
{
|
||||
if (!isDirty) return;
|
||||
FMLLog.fine("Forge RecipeSorter Baking:");
|
||||
DirectedGraph<SortEntry> sorter = new DirectedGraph<SortEntry>();
|
||||
sorter.addNode(before);
|
||||
sorter.addNode(after);
|
||||
sorter.addEdge(before, after);
|
||||
|
||||
for (Map.Entry<String, SortEntry> entry : entries.entrySet())
|
||||
{
|
||||
sorter.addNode(entry.getValue());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, SortEntry> e : entries.entrySet())
|
||||
{
|
||||
SortEntry entry = e.getValue();
|
||||
boolean postAdded = false;
|
||||
|
||||
sorter.addEdge(before, entry);
|
||||
for (String dep : entry.after)
|
||||
{
|
||||
if (entries.containsKey(dep))
|
||||
{
|
||||
sorter.addEdge(entries.get(dep), entry);
|
||||
}
|
||||
}
|
||||
|
||||
for (String dep : entry.before)
|
||||
{
|
||||
postAdded = true;
|
||||
sorter.addEdge(entry, after);
|
||||
if (entries.containsKey(dep))
|
||||
{
|
||||
sorter.addEdge(entry, entries.get(dep));
|
||||
}
|
||||
}
|
||||
|
||||
if (!postAdded)
|
||||
{
|
||||
sorter.addEdge(entry, after);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<SortEntry> sorted = TopologicalSort.topologicalSort(sorter);
|
||||
int x = sorted.size();
|
||||
for (SortEntry entry : sorted)
|
||||
{
|
||||
FMLLog.fine(" %d: %s", x, entry);
|
||||
priorities.put(entry.cls, x--);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue