Skip to content

Instantly share code, notes, and snippets.

@williewillus
Last active July 4, 2023 01:01
Show Gist options
  • Save williewillus/a1a899ce5b0f0ba099078d46ae3dae6e to your computer and use it in GitHub Desktop.
Save williewillus/a1a899ce5b0f0ba099078d46ae3dae6e to your computer and use it in GitHub Desktop.
recipe to json dumper
// You can include this in your mod/a pack/whatever you want, as long as that work follows the Mojang EULA.
// The original source is viewable at https://gist.github.com/williewillus/a1a899ce5b0f0ba099078d46ae3dae6e
// This is a janky JSON generator, for porting from below 1.12 to 1.12.
// Simply replace calls to GameRegistry.addShapeless/ShapedRecipe with these methods, which will dump it to a json in RECIPE_DIR
// Also works with OD, replace GameRegistry.addRecipe(new ShapedOreRecipe/ShapelessOreRecipe with the same calls
// After you are done, call generateConstants()
// Note that in many cases, you can combine multiple old recipes into one, since you can now specify multiple possibilities for an ingredient without using the OD. See vanilla for examples.
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static File RECIPE_DIR = null;
private static final Set<String> USED_OD_NAMES = new TreeSet<>();
private static void setupDir() {
if (RECIPE_DIR == null) {
RECIPE_DIR = ConfigHandler.config.getConfigFile().toPath().resolve("../recipes/").toFile();
}
if (!RECIPE_DIR.exists()) {
RECIPE_DIR.mkdir();
}
}
// EXPERIMENTAL: JSONs generated will definitely not work in 1.12.2 and below, and may not even work when 1.13 comes out
// When Forge 1.13 is fully released, I will fix this to be correct
// Usage: Replace calls to GameRegistry.addSmelting with this
private static void addSmelting(ItemStack in, ItemStack result, float xp) { addSmelting(in, result, xp, 200); }
private static void addSmelting(ItemStack in, ItemStack result, float xp, int cookTime) {
setupDir();
// GameRegistry.addSmelting(in, out, xp);
Map<String, Object> json = new HashMap<>();
json.put("type", "minecraft:smelting");
json.put("ingredient", serializeObject(in));
json.put("result", serializeObject(result)); // vanilla jsons just have a string?
json.put("experience", xp);
json.put("cookingtime", cookTime);
// names the json the same name as the output's registry name
// repeatedly adds _alt if a file already exists
// janky I know but it works
String suffix = result.getItem().getHasSubtypes() ? "_" + result.getItemDamage() : "";
File f = new File(RECIPE_DIR, result.getItem().getRegistryName().getResourcePath() + suffix + ".json");
while (f.exists()) {
suffix += "_alt";
f = new File(RECIPE_DIR, result.getItem().getRegistryName().getResourcePath() + suffix + ".json");
}
try (FileWriter w = new FileWriter(f)) {
GSON.toJson(json, w);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void addShapedRecipe(ItemStack result, Object... components) {
setupDir();
// GameRegistry.addShapedRecipe(result, components);
Map<String, Object> json = new HashMap<>();
List<String> pattern = new ArrayList<>();
int i = 0;
while (i < components.length && components[i] instanceof String) {
pattern.add((String) components[i]);
i++;
}
json.put("pattern", pattern);
boolean isOreDict = false;
Map<String, Map<String, Object>> key = new HashMap<>();
Character curKey = null;
for (; i < components.length; i++) {
Object o = components[i];
if (o instanceof Character) {
if (curKey != null)
throw new IllegalArgumentException("Provided two char keys in a row");
curKey = (Character) o;
} else {
if (curKey == null)
throw new IllegalArgumentException("Providing object without a char key");
if (o instanceof String)
isOreDict = true;
key.put(Character.toString(curKey), serializeItem(o));
curKey = null;
}
}
json.put("key", key);
json.put("type", isOreDict ? "forge:ore_shaped" : "minecraft:crafting_shaped");
json.put("result", serializeItem(result));
// names the json the same name as the output's registry name
// repeatedly adds _alt if a file already exists
// janky I know but it works
String suffix = result.getItem().getHasSubtypes() ? "_" + result.getItemDamage() : "";
File f = new File(RECIPE_DIR, result.getItem().getRegistryName().getResourcePath() + suffix + ".json");
while (f.exists()) {
suffix += "_alt";
f = new File(RECIPE_DIR, result.getItem().getRegistryName().getResourcePath() + suffix + ".json");
}
try (FileWriter w = new FileWriter(f)) {
GSON.toJson(json, w);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void addShapelessRecipe(ItemStack result, Object... components)
{
setupDir();
// addShapelessRecipe(result, components);
Map<String, Object> json = new HashMap<>();
boolean isOreDict = false;
List<Map<String, Object>> ingredients = new ArrayList<>();
for (Object o : components) {
if (o instanceof String)
isOreDict = true;
ingredients.add(serializeItem(o));
}
json.put("ingredients", ingredients);
json.put("type", isOreDict ? "forge:ore_shapeless" : "minecraft:crafting_shapeless");
json.put("result", serializeItem(result));
// names the json the same name as the output's registry name
// repeatedly adds _alt if a file already exists
// janky I know but it works
String suffix = result.getItem().getHasSubtypes() ? "_" + result.getItemDamage() : "";
File f = new File(RECIPE_DIR, result.getItem().getRegistryName().getResourcePath() + suffix + ".json");
while (f.exists()) {
suffix += "_alt";
f = new File(RECIPE_DIR, result.getItem().getRegistryName().getResourcePath() + suffix + ".json");
}
try (FileWriter w = new FileWriter(f)) {
GSON.toJson(json, w);
} catch (IOException e) {
e.printStackTrace();
}
}
private static Map<String, Object> serializeItem(Object thing) {
if (thing instanceof Item) {
return serializeItem(new ItemStack((Item) thing));
}
if (thing instanceof Block) {
return serializeItem(new ItemStack((Block) thing));
}
if (thing instanceof ItemStack) {
ItemStack stack = (ItemStack) thing;
Map<String, Object> ret = new HashMap<>();
ret.put("item", stack.getItem().getRegistryName().toString());
if (stack.getItem().getHasSubtypes() || stack.getItemDamage() != 0) {
ret.put("data", stack.getItemDamage());
}
if (stack.getCount() > 1) {
ret.put("count", stack.getCount());
}
if (stack.hasTagCompound()) {
ret.put("type", "minecraft:item_nbt");
ret.put("nbt", stack.getTagCompound().toString());
}
return ret;
}
if (thing instanceof String) {
Map<String, Object> ret = new HashMap<>();
USED_OD_NAMES.add((String) thing);
ret.put("item", "#" + ((String) thing).toUpperCase(Locale.ROOT));
return ret;
}
throw new IllegalArgumentException("Not a block, item, stack, or od name");
}
// Call this after you are done generating
private static void generateConstants() {
List<Map<String, Object>> json = new ArrayList<>();
for (String s : USED_OD_NAMES) {
Map<String, Object> entry = new HashMap<>();
entry.put("name", s.toUpperCase(Locale.ROOT));
entry.put("ingredient", ImmutableMap.of("type", "forge:ore_dict", "ore", s));
json.add(entry);
}
try (FileWriter w = new FileWriter(new File(RECIPE_DIR, "_constants.json"))) {
GSON.toJson(json, w);
} catch (IOException e) {
e.printStackTrace();
}
}
@Darkhax
Copy link

Darkhax commented Jun 16, 2017

I believe you are missing a return in the oredict output bit. Specifically lines. 128-132. Without this return the last exceptions will be thrown.

@williewillus
Copy link
Author

fixed!

@KitsuneAlex
Copy link

looks interesting but i think i will look at RegistryEvent first

@williewillus
Copy link
Author

Not sure if there is a registry event for recipes yet

@Lothrazar
Copy link

Thanks for sharing this

@The-Fireplace
Copy link

image
Beautiful! 😃

@MatrexsVigil
Copy link

This doesn't generate a _constants.json file for me. Is there anything specific I need to do to get this to work?

@williewillus
Copy link
Author

You'll need to call generateConstants manually after generating everything else.

@P3pp3rF1y
Copy link

P3pp3rF1y commented Jul 28, 2017

After doing some notepad++ magic with recipes for about 3rd of them I ended up finding that this exists and used it for the rest of recipes and in the process made some simple enhancements to it

  • used LinkedHashMap because I just like recipe tags ordered similar to vanilla
  • uppercasing of patter and keys in shaped recipes
  • nbt support (only tested with very simple stuff, but should work with more complicated as well)
    In case that you want to use pieces from it here or anyone needs this stuff at all here it is (it really is 99% this original code and just small enhancements on top) https://gist.github.com/P3pp3rF1y/ea85fa337c9082e95336b1b61d1c3cb5

EDIT from williewillus: mine should now support NBT natively, if you still want the other changes take a look at the link above

@capnkirok
Copy link

You rock, @williewillus

@capnkirok
Copy link

Since I have dynamic recipes in Inventory Pets, which are based on events and/or config settings, I modified this to overwrite existing recipe JSONs at every restart instead of creating endless "alts".

@williewillus
Copy link
Author

since somebody asked: added a little header regarding usage in your own mods

@The-Fireplace
Copy link

Shouldn't lines 35 and 36 be serializeItem and not serializeObject?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment