Skip to content

Instantly share code, notes, and snippets.

@LexManos LexManos/1-THOUGHTS
Last active Sep 30, 2018

Embed
What would you like to do?
1.12 Recipe enhancements
Basic loading.
To be done on ServerInit. This means things will load multiple times on the client.
Benifits:
Allows us to just nuke the custom recipes every server init
Allows us to have save leve overrides.
Primes us for syncing recipe types/contents S->C {Not gunna happen in 1.12, but I expect Mojang to work twards this in 1.13+}
Cons:
Increases single player server load time. No known stats at the moment but shouldn't be TO bad
Loading Process:
BootStrap: Vanilla loads their internal recipes, we'll freeze this registry and revert to it at the beginning of ServerInit
Gather Factories:
Gather a list of IRecipe, and Ingredient factories.
Loop through all mod jars/directories and load /assets/MODID/recipes/_factories.json
Names will automatically be prefixed with modid domains. Error if name contains : to dissallow overriding other's factories
Gather Constants:
Done as a second pass to allow modders to depend on other mods ingredient factories
Loop through all mods, loading /assets/MODID/recipes/_constants.json
Add to a central dictionary as modid:name, so mods can't overlap eachother. Again, error if people try to overwrite other's
Load jsons from Mod Jars:
Loop through all mods, loading /assets/MODID/recipes/[!_factories && !_constants].json
Register to central regirsty as modid:jsonFileName like vanilla does
Fire RegistryEvent.Register<IRecipe>:
Fire code event to allow people to create recipes in code?
Loading from modpack/config folder?
Check Forge config for a 'recipes_folder', or explicitly check GameDir/custom_recipes and load all recipes there.
Loads from all asset domains, automatically skipping the ones that do not match a loaded modid.
This should behave the same as loading from a single jar file.
Should we add a global FACTORIES/CONSTANTS json?
Loading from world folder?
Same as loading from modpacks folder but happens afterwords so that worlds override both modpacks and jars.
Conditions:
Simple conditions to determine if we should load the recipe, or set the constant.
All entries must return true.
Allow custom condition types defined in FACTORIES?
Types to add:
mod_loaded: modid
item_exists: itemname
not: [conditions] -- Inverts a condition, can be nested
or: condition1, condition2
[
{
"conditions": [
{
"type": "mod_loaded",
"mod": "minecraft"
}
],
"name" : "PLANKS",
"ingredient" : {
"type" : "ore_dict",
"ore" : "planks"
}
},
{
"conditions": [
{
"type": "mod_loaded",
"mod": "xycraft"
}
],
"name": "PLANKS",
"ingredient" : {
"item" : "xycraft:planks"
}
},
{
"name": "OAK_OR_SPRUCE",
"ingredient": [
{
"item": "minecraft:log",
"data" : 0
},
{
"item": "minecraft:log",
"data" : 2
}
],
},
{
"name": "MIXED",
"ingredient": [
{
"type": "ore_dict",
"ore": "stick"
},
{
"item": "minecraft:torch"
}
]
},
{
"name": "PigEgg",
"ingredient": {
"type": "itemstack_nbt",
"item": "minecraft:mobegg",
"nbt": {
"entityId": "pig"
}
}
}
]
{
"ingredients" : {
"ore_dict": "net.minecraftforge.oredict.OreIngredientFactory",
"itemstack_nbt": "net.minecraftforge.util.NBTIngredientFactory",
},
"recipes" : {
"shapeless_ore" : "net.minecraftforge.oredict.ShapelessOreRecipeFactory",
"shaped_ore" : "net.minecraftforge.oredict.ShapedOreRecipeFactory"
}
}
{
"conditions": [
{
"type": "mod_loaded",
"mod": "minecraft"
}
],
"type": "minecraft:crafting_shaped",
"group": "random_things",
"pattern": [
"###",
"XXX"
],
"key": {
"#": {
"item": "#PLANKS"
},
"X": [
{
"item": "#PLANKS"
},
{
"item": "#MIXED",
},
{
"item": "minecraft:planks",
"data": 2
}
]
},
"result": {
"item": "minecraft:bed",
"data": 15
}
}
@PaleoCrafter

This comment has been minimized.

Copy link

PaleoCrafter commented Jun 13, 2017

Regarding line 29: Not sure what purpose a global _factories.json would serve, since they only are a link to code (unless something more is planned, like JS support or Turing-complete JSON 😉), so doesn't make sense outside of individual mods, unless we add some sort of disabling functionality for modpacks to use.

Apart from that this looks pretty good and sufficient, imo. A question regarding OreDict recipes, though (note that I haven't looked at the Vanilla implementation of this), but do they require a separate recipe class? I'd have assumed that we could somehow make use of different ingredients within a recipe type and you also seem to use the MIXED constant like that.

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

As it sits OreDict and Vanilla recipes are fairly close to identical.
The only main difference is that vanilla FORCES the recipe to be <= 3x3. OreDict doesn't care.
And oredict also has a mirror flag to disable mirroring.

Its not so much a OreRecipe anymore, it's more a AdvancedRecipe

@ShetiPhian

This comment has been minimized.

Copy link

ShetiPhian commented Jun 13, 2017

Is an error thrown, or is the recipe ignored if the result is a missing item?
If its an error should an 'item_exists' condition be added?


Would it be possible to load recipes based on configs?
Some mods have their normal recipes, with config options to choose easier/harder versions.
Personally I have a set of recipes that are only loaded if the user wants shortcuts. If they where always available blocks would end up auto crafted rather then done in world.

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

Right now, as with vanilla there is an exception thrown if any referenced item isn't in the registry.
Result or ingredient.
Having a item_exists may be a good idea i'll add it to the list.

I'm not sure what you mean by harder/easier. I'm not sure why anyone would want to do that... And if they did wouldn't it be better to pull it out to the modpack config? I am not sure how to address this one, we may add a config_option condition. My only issue with that is this isnt designed to allow for multiple jsons to get the same recipe name in the registry.

Do you have more information?

@Darkhax

This comment has been minimized.

Copy link

Darkhax commented Jun 13, 2017

This system looks simple enough. I do have a few questions and concerns though.

  • Is the modid preserved when the file is desearialized? If so, that would be an acceptable replacement of this PR MinecraftForge/MinecraftForge#3739
  • Would it be possible to add a recipe function/condition system? Specifically something like the functions and conditions in loot tables? I think that adding something like this may help encourage devs to switch to json based recipes. I am specifically thinking of stuff like biome specific recipes, or positional recipes, and player specific stuff. By extending the system in this way it would also allow modpack/map makers to take advantage of mod conditions/functions.
  • Is there a way to remove recipes, and if not could one be easily provided? There are a few mods such as CraftTweaker which are widely used, and depend on things like this. I know a while back we had an issue with CraftTweaker crashing the seed drops because it was reflecting into the drops and breaking stuff. Im pretty sure all of us would agree that hacks like that should be avoided, so providing an official way to remove recipes would be really helpful.
@HenryLoenwind

This comment has been minimized.

Copy link

HenryLoenwind commented Jun 13, 2017

For the variant recipes, I think that may best be handled with a custom recipe class which checks your config flag at runtime. I'm assuming here that an IRecipe can say "nope, I have nothing". The other way would be a custom recipe factory that does the checking. Again assuming a recipe factory can return 0..n recipes for one json entry. Third option would be a custom condition that checks your config flag (this is how the Ender IO recipe system does it atm).

(Side note: I find the ability to generate multiple IRecipes per json entry desirable. Some recipes may be declared once with a dynamic match, but entered into the system once per matched item to show up as independent recipes in (e.g.) JEI. Example: "any pressure plate + any wool => silent variant of that pressure plate with nbt preserved".)

@HenryLoenwind

This comment has been minimized.

Copy link

HenryLoenwind commented Jun 13, 2017

re darkhax: Yes, removing recipes via the user/modpack/world jsons is something I also find very important. However, it seems recipes do not have some kind of ID field, other than the group. It may be enough to be able to remove whole groups?

user_recipe.json
{
  "type": "forge:remove_recipe",
  "group": "random_things"
}

Recipes that only work at full moon if the player is standing on sand with a stick in their left hand should be possible with custom IRecipes, I presume?

@Darkhax

This comment has been minimized.

Copy link

Darkhax commented Jun 13, 2017

Re Henry: IRecipe currently hasn't changed too much, so stuff like that should still be possible. That said, I think that being able to define conditions and stuff like that in the json directly may be better long term. There is already a condition system, although I believe it is limited to loading only.

  "conditions": [
    {
      "type": "mod_loaded",
      "mod": "minecraft"
    }
  ],

As for the recipe identifier, I am really hoping that the modid and file name are preserved in deserialization. This would allow the file name to be used as the identifier.

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

Yes modids are being preserved for all recipes, and all recipes have a unique registry name, Which is modid:filename

I'm not quite sure about functions... The issue with that is the way that IRecipe is written it MUST be a ItemStack as a result.
We can look into that later tho if you have ideas.

Yes, removing crafting recipes via json, or code is intended, not exactly sure how i want to implement that, or in what pass. If implemented as JSON thing that ONLY things that would be allowed to remove recipes would be the modpack and world folders. As it would be stupid to allow a mod to add a recipe and then remove itself all in the same json parsing setup. And no im not letting you ship your mod automatically removing recipes from other's mods in json. Code wise however... I'll need to think more about that.

0..n recipes: I haven't thought about that.. it wouldn't be difficult to do... except for getting the unique names. But I would need better use cases for this so I understand this better. The example you provided could easily be a custom IRecipe itself.

And as before, IRecipe.matches() has a inventory, and world parameter so you can do timed stuff there. It doesn't really have location information, or the entity doing the crafting {which i think is bad to add because automation} But those can be expanded outside of this scope.

@HenryLoenwind

This comment has been minimized.

Copy link

HenryLoenwind commented Jun 13, 2017

Yes, Lex asked about that:

  Allow custom condition types defined in FACTORIES?

Extending it to runtime conditions? I don't know. Maybe it would be easy to include a IRecipe that wraps another IRecipe and takes the same conditions that are used at loading? Its factory would need to be able to query and remove existing recipes in order to produce wrapped recipes. And it would be best to have this in Forge, so JEI can know about it and ask it for the wrapped recipe to have correct mod-specific renderer handle it.

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

Factories WILL NOT be able to query and remove other recipes. If you MUST write a wrapper recipe, then wrap the recipe in the json.
Again, The mod specific json will NOT be allowed to modify recipes outside of your modid. So removing another mod's recipe and replacing it with your own via json will NOT be supported. That's the job of the modpack/world jsons/code.

@darkevilmac

This comment has been minimized.

Copy link

darkevilmac commented Jun 13, 2017

A bit confused about lines 23 and 24.

Does this mean it will still be possible to register IRecipe implementations? And if so, why not just do this in preInit/Init like a lot of mods do already, is it to allow reloading of the recipe registry if needed?

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

Yes, see the note about why we're doing it in Server init. It is better future proofing to introduce modders to the idea that things should be done at ServerInit not at client load.
I don't think that reloading the registry after server init will be supported as it would mess up all client connections by desyncing all ids.
We'd have to guard everything with kicking everyone from the server. Which honestly might as well just restart the server then.

@Paul17041993

This comment has been minimized.

Copy link

Paul17041993 commented Jun 13, 2017

Not sure if this was already covered, but there most definitely should be a recipe craft event to allow special conditions, such as blocking a craft with a certain variable or firing other events such as achievements...

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

THere is already an event like we've always had in Forge. This has nothing to do with that.

@darkevilmac

This comment has been minimized.

Copy link

darkevilmac commented Jun 13, 2017

Alright, thanks for the clarification.

@mcenderdragon

This comment has been minimized.

Copy link

mcenderdragon commented Jun 13, 2017

The _constands do they only work for items or also for meta/data ? And is it possible to inject constands from code? I am aking because I have an item with many metas so typing #green_jar is better readable than 23.

@asiekierka

This comment has been minimized.

Copy link

asiekierka commented Jun 13, 2017

Looks reasonable and customizable. (Especially if in-code recipes are kept, though custom recipe factories should do the trick and give modpack creators more to play with.)

I'd honestly miss the ability to reload recipes - I used that feature with MineTweaker 3 a lot when developing a modpack, because it allowed me to very quickly push changes and get feedback from players. Do recipes need numeric IDs? Wouldn't ResourceLocation IDs avoid desyncing all of them on reload?

Could some of this system be generalized to apply to non-vanilla recipes? (Let's say, a custom crafting table, or even custom mod machines) It could potentially replace a huge chunk of what MineTweaker/ModTweaker were used for with proper compatibility across the board.

Singleplayer server load times would probably scale pretty quickly. Large modpacks can easily get to dozens of thousands of recipes. Since the registry is being frozen after vanilla's recipes are loaded, couldn't - similarly - mod/modpack recipes be loaded and frozen on postInit, at which point only world/server-specific recipes (and recipe deletions) would have to be reloaded?

EDIT: Could one use other mods' factories?

@PaleoCrafter

This comment has been minimized.

Copy link

PaleoCrafter commented Jun 13, 2017

@mcenderdragon: As far as I understand it, you can specify any ingredient as a constant, so metadata or wholly custom data (through a custom factory) should be allowed.

@Alpvax

This comment has been minimized.

Copy link

Alpvax commented Jun 13, 2017

Am I missing something or is there a way to add custom conditions for general recipes? For example making beds only craftable based on a property of the player's capability. Is the only way to add a custom IRecipe implementation?

@PaleoCrafter

This comment has been minimized.

Copy link

PaleoCrafter commented Jun 13, 2017

@Alpvax: There's no access to players in a generic crafting context (think automation, like Lex said). The only way to do it 'reliably' right now is a custom recipe implementation that checks the container, so a general purpose system can't really do this. The condition system as it is proposed here also is about loading a recipe, not whether it may be used on a case-to-case basis.

@Alpvax

This comment has been minimized.

Copy link

Alpvax commented Jun 13, 2017

@PaleoCrafter My mistake, I thought this was a way of adding conditions to recipes. Could the condition be registered as an ingredient or will ingredient be limited to items? My current approach has been to write wrappers every time I want to be able to use a custom condition, it would be really helpful if that could be done in the json files. I understand it would mean a re-write of the current 'AdvancedRecipe' implementation, but this seems to be a good time to do it. Then this system could be extended to custom recipe types (i.e. furnace, modded machines etc.).
Machines could then implement an IRecipeHandler interface, which could expose the required ingredients that the recipe would then check.

However, this seems more in-depth than currently required, and would need more thorough fleshing out, so I suggest leaving it for later (see what Mojang do for their customisable recipes implementation).

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 13, 2017

I've played around with the idea of freezing the registry after jar/code runs. And then causing the ServerInit load to just be mods folder/world folder. If we add those. However this would need to be on cpw as he's the one deeply familiar with the registry freezing system.

Again there is no plans on by default allowing a reload system. It uses numeric IDs for the recipe book and management of enabled recipes on both the server and the client. The GOOD thing is thats all encapsulated in one class. So we don't have to sync ids in world. But I see things like the client thinking you have a recipe unlocked but really the server says you have another one. It something we can address in the second pass once we see all the caveats of the current system, It would however require us to write custom registry syncing code that is done post-handshake.

As for custom recipe registries {Things that don't extend IRecipe} This system is fiarly generic, it really only hardcodes IRecipe in two places. The idea is that it might be able to be copy/pasted into a new registry for say.. macerators, or something. But im not sure how the recipe book would deal with that. So the goal is getting IRecipes working first, then see what we can do for custom stuff.

Yes you can use other mod's factories. The factories will be loaded in one global pass, then it'll do a single pass of constants+recipes.
You can NOT use other mod's constants. I see no reason to hard dep on that you can re-make the constant yourself using the mod its with a few conditionals.

No, these conditions are LOAD TIME. So specifying things like player cap checks in the json will not work. However, you're allowed to use your own custom IRecipeFactory which allows you runtime access, so you can go that way.

@Nuchaz

This comment has been minimized.

Copy link

Nuchaz commented Jun 13, 2017

Could we have a way to specify multiple result variants based on input ingredients in a single json? For example, I have a number of recipes that are identical with the exception of the wood type used so I get a different output for each wood type. The only thing really changing in that scenario is the meta data on each input/output. It would be nice if I could handle that in a single json. What if there was a sort of "output" tag that could be assigned to ingredients and that same tag could also be assigned to results. So we could have multiple results and each result would only use ingredients that either have a matching "output" tag or no tag at all. It would possibly look something like this: https://pastebin.com/J67vZ1u7

@Gaelan

This comment has been minimized.

Copy link

Gaelan commented Jun 14, 2017

The new constant system feels kind of redundant with oredict. Would it be possible to replace oredict with a "global" namespace for constants? Then mods could just add their wood blocks to global:LogWood in their constants.json.

@Alpvax

This comment has been minimized.

Copy link

Alpvax commented Jun 14, 2017

@Gaelan That just results in a json configurable oredict. I'm not sure that is beneficial.

Having runtime condition factories would be useful, they could be added as ingredients in json for more advanced modpack configuration.

@TechnicianLP

This comment has been minimized.

Copy link

TechnicianLP commented Jun 15, 2017

can it be made possible to specify multiple Recipes in one json? ie replace the top JsonObject witn an JsonArray, which then in return contains multiple JsonObjects (which have to be given a individual name manually)

@FVbico

This comment has been minimized.

Copy link

FVbico commented Jun 15, 2017

I actually made a different recipes format as well; perhaps you could look into it, it may be of use to you too.
https://github.com/FVbico/MinecraftData/tree/master/minecraft/assets/server/recipes

@KitsuneAlex

This comment has been minimized.

Copy link

KitsuneAlex commented Jun 17, 2017

alright, RegistryEvent you are my friend

@TehenoPengin

This comment has been minimized.

Copy link

TehenoPengin commented Jun 17, 2017

Thanks for leaving RegistryEvent option :P

@LexManos

This comment has been minimized.

Copy link
Owner Author

LexManos commented Jun 17, 2017

Don't expect it as of 1.13, Its only in because I know people will bitch if its removed directly.
This is a first round of the json format. Its not perfect but its functional for 99% of use cases.

@MCOfficer

This comment has been minimized.

Copy link

MCOfficer commented Nov 5, 2017

i might be a bit late, but is there a way to set the amount of results? (e.g pumpkin -> pumpkin seeds)

@SkyBlade1978

This comment has been minimized.

Copy link

SkyBlade1978 commented Nov 10, 2017

Is there any update on the correct procedure for removing a vanilla recipe in 1.12?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.