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();
}
}
@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