fabric
fabric copied to clipboard
Fabric custom ingredient API
Note: This PR is still WIP and untested, feel free to ignore it for now.
This API allows modders to define custom Ingredient
s, with more advanced matching logic than what vanilla allows.
Custom ingredients need to implement the CustomIngredient
interface, and also provide and register a CustomIngredientDeserializer
. No overriding of Ingredient
is needed.
For now, the only example is the OrCondition
.
TODO:
- [ ] The various TODOs in the PR.
- [ ] Decide on exact API and impl structure.
- [ ] Write javadoc.
A possible NBT ingredient would also be useful
A possible NBT ingredient would also be useful
I have the following ingredients in mind, just haven't written them yet: (matching Forge's)
- strict and partial NBT ingredient
- and ingredient
- difference ingredient
Thanks a lot for your detailed explanation.
I decided to go for the simpler augmenting paths algorithm for the maximum cardinality bipartite matching problem, which should look like this : https://github.com/Technici4n/fabric/blob/custom-ingredient-api/fabric-ingredient-api-v1/src/main/java/net/fabricmc/fabric/impl/ingredient/ShapelessMatch.java. I haven't tested it yet though, but I will definitely add tests similar to what you suggested.
I also renamed matchesStack
and getPreviewStacks
to test
and getMatchingStacks
respectively, and provided some javadoc for CustomIngredient
. I hope the javadoc is clear enough - there's some tricky details that might not always be obvious. I also borrowed your name for requiresTesting
which I like better than ignoresNbt
- hopefully you don't mind. :smile:
I just tested the following cases:
- Server with Fabric ingredient API, vanilla client, no recipe using custom ingredients: can connect fine.
- Server with Fabric ingredient API, vanilla client, some recipes using custom ingredients: can't connect, with the following error:
Is this fine?
- Client/server with both Fabric ingredient API and Faux ingredient extension API: packet writing sending works, but I used the 1.19.2 version of the ingredient extension API, so I get a crash due to other reasons. I think the two APIs are compatible. (Though obviously the ingredient extension API won't be needed anymore once this PR is merged, but I want a smooth transition).
This PR is now ready for reviews!
Client/server with both Fabric ingredient API and Faux ingredient extension API: packet writing sending works, but I used the 1.19.2 version of the ingredient extension API, so I get a crash due to other reasons. I think the two APIs are compatible. (Though obviously the ingredient extension API won't be needed anymore once this PR is merged, but I want a smooth transition
We have no intention of continuing development on the Faux Ingredient Extension API once this PR is merged, so even if it wasn't compatible, it shouldn't be an issue since neither APIs should share a version.
My thinking is that you might release a 1.19.3 version of your Extension API before this PR gets merged in 1.19.3. (I have no idea when it will be merged, it's going to require quite a bit of review).
Would it be out of scope to add a way to give these custom ingredients a recipe remainder that overrides the one in their item's settings? So one could, for example, make a "chocolate milk bucket" recipe that takes a milk bucket and cocoa beans, without the milk bucket leaving behind an empty bucket by overriding the milk bucket's remainder in just that recipe.
Would it be out of scope to add a way to give these custom ingredients a recipe remainder that overrides the one in their item's settings? So one could, for example, make a "chocolate milk bucket" recipe that takes a milk bucket and cocoa beans, without the milk bucket leaving behind an empty bucket by overriding the milk bucket's remainder in just that recipe.
I think it would be out of scope. Some of the problems:
- Ingredients are used in many places, so properly supporting this would require a lot of patches.
- The patches are very invasive as they require all remainder logic to go through the ingredient.
- For recipes, we don't even know which ingredients were matched where in the getRecipeRemainder method, so we'd need to recompte the match before we apply custom remainders. I think it's best left to other mods (for crafting recipes, it's "easy" to implement on top of this API with a custom recipe type).
I just implemented a graceful fallback mechanism to make this API work nicely for clients that don't support (some or all) custom ingredients. Summary follows:
- On login, server requests the client's available ingredients.
- The client answers by listing the serializer IDs it supports.
- The server stores that where it's relevant.
- When an ingredient is serialized, we lookup which IDs are supported by the client. If we can lookup and the client doesn't support the ingredient, we send it as a vanilla stack list ingredient instead. Otherwise, we send the custom ingredient as usual.
List of cases to test:
- [x] Server has ingredient API, client doesn't have the API.
- [x] Server has ingredient API, client has the API but doesn't support some ingredients.
- [x] Server has ingredient API, client has the API and all the used ingredients.
After some consideration, this new module will be renamed Fabric Recipe API v1, and the ingredient stuff will be moved to the net.fabricmc.fabric.api.recipe.v1.ingredient
package, in preparation for possible other kinds of recipe format extensions.
Works fine on vanilla clients now (ignoring the recipe book) !
Partial support on clients also works: (debug output on server, removed some ingredients on client)
[15:28:36] [Server thread/INFO]: Technici4n joined the game
Wrote fabric:nbt as a vanilla ingredient
Wrote fabric:and as a custom ingredient
Wrote fabric:difference as a vanilla ingredient
Wrote fabric:nbt as a vanilla ingredient
Wrote fabric:or as a custom ingredient
Just rebased to fix the gradle.properties
conflict, and switched getMatchingStacks
to a List<ItemStack>
.
I expect out of scope for this PR, but having a condition for a custom ingredient might be useful.