Skip to content

Recipe Refactor#1419

Closed
Maxqia wants to merge 6 commits intoSpongePowered:bleedingfrom
Maxqia:recipe
Closed

Recipe Refactor#1419
Maxqia wants to merge 6 commits intoSpongePowered:bleedingfrom
Maxqia:recipe

Conversation

@Maxqia
Copy link
Contributor

@Maxqia Maxqia commented Nov 19, 2016

@kashike kashike changed the title [WIP] Recipes [WIP] Furnace Recipes Nov 19, 2016
* @param output the {@link ItemStack} the recipe gives
* @param exp the amount of exp the recipe gives
*/
void addSmeltingRecipe(ItemStack input, ItemStack output, float exp);
Copy link
Contributor

@liach liach Nov 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use ItemStackSnapshot.

@@ -0,0 +1,37 @@
package org.spongepowered.api.item.recipe;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing licence header.

@kashike kashike self-assigned this Nov 19, 2016
@kashike kashike changed the title [WIP] Furnace Recipes Furnace Recipes Nov 19, 2016
@Cybermaxke
Copy link
Contributor

Cybermaxke commented Nov 19, 2016

I would like to see a FurnaceRecipe and a Builder similar to the recipe system for crafting grids. I made the following draft to give a general idea, you can also checkout the package: https://github.com/SpongePowered/SpongeAPI/tree/bleeding/src/main/java/org/spongepowered/api/item/recipe

public interface SmeltingRecipeRegistry {

    void register(SmeltingRecipe recipe);

    void remove(SmeltingRecipe recipe);

    Set<SmeltingRecipe> getRecipes();

    Optional<SmeltingRecipe> match(ItemStack itemStack);

   // Move these to their own registry???

    void registerFuel(Fuel fuel);

    void removeFuel(Fuel fuel);

    Set<Fuel> getFuels();

    Optional<Fuel> matchFuel(ItemStack itemStack);
}
public interface SmeltingRecipe {

    ItemStackSnapshot getInput();

    ItemStackSnapshot getOutput();

    double getExperience();

    int getDuration(); // Duration may also be nice to have

    public interface Builder {

         // Builder stuff
    }
}

And maybe also custom fuel?

public interface Fuel {

     ItemStackSnapshot getInput();

    // The result item when the fuel is consumed, like a bucket from a lava bucket
     Optional<ItemStackSnapshot> getReturnItem();

     int getDuration();

     public interface Builder {

         // Builder stuff
    }
}

public interface SmeltingRegistry {
/**
* Adds a smelting recipe to this registry.
* @param input the input of the recipe
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs an empty javadoc line before this line. Same goes for all methods in this class.
Also the param descriptions should start with a capital letter after the param name.

* @return the experience of the {@link ItemStack}
*/
float getExperience(ItemStack stack);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an empty line before this line.

* @param the {@link ItemStack}
* @return the {@link ItemStack} it makes
*/
ItemStack getMapping(ItemStack stack);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return Optionals for materials that are not registered/smeltable.

* A RecipeRegistry holds all registered smelting recipes for a given game.
*/
public interface SmeltingRegistry {
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline before this line.

@Maxqia
Copy link
Contributor Author

Maxqia commented Nov 20, 2016

@Cybermaxke I was going to do that, until I realized that it would just be alot of = statements ...

@liach
Copy link
Contributor

liach commented Nov 20, 2016

@Maxqia What are a lot of = statements? The suggested one is way more logical.

@liach
Copy link
Contributor

liach commented Nov 20, 2016

@Cybermaxke Minecraft Forge manages fuel registry with a list of IFuelHandlers which returns the maximum burn time for a specific item stack, calling all registered handlers each time. Your Fuel interface probably cannot represent that.

@Maxqia
Copy link
Contributor Author

Maxqia commented Nov 20, 2016

eg.
SpongeSmeltingRecipe :

setInput(ItemStack input) {
this.input = input;
}
build() {
return new SpongeSmeltingStack(input,output,exp);
}

SpongeSmeltingStack :

ItemStack input,output;
float exp;
SpongeSmeltingStack(ItemStack input, ItemStack output, float exp) {
this.input = input; etc...
}

@Maxqia
Copy link
Contributor Author

Maxqia commented Nov 20, 2016

Also SmeltingRegistry.register(SmeltingRecipe.builder().setInput(input).setOutput(output).setExp(exp).build()) -> SmeltingRegistry.addSmeltingRecipe(input,output,exp)

* @param stack the {@link ItemStack}
* @return the {@link ItemStack} it makes
*/
ItemStack getMapping(ItemStack stack);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return Optional<ItemStack>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, forgot about that ...

@Cybermaxke
Copy link
Contributor

@Maxqia The sponge environment is built around Builders, this should't be an exception. You can always add helper methods to the SmeltingRecipe to simplify the creation.

@liach The implementation should be possible with the exception of the getFuels method, it would be a bit tricky though.

Copy link
Member

@gabizou gabizou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some discussion is necessary on the use of ItemStacks instead of ItemStackSnapshots. Since there's a big difference between the two (recipes in the underlying implementation aren't meant to change, but seeing as there's no immutable implementation aside from Sponge's, that's a different issue). To the API, we should be marking the objects as immutable as much as possible, when necessary.

* @return The recipe registry
* @return The smelting registry
*/
RecipeRegistry getRecipeRegistry();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be left in with @Deprecated to just redirect to the CraftingRegistry.

* @return The resultant list of item types
* @return the results of this {@link Recipe}
*/
List<ItemType> getResultTypes();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise with the previous deprecated method, this should be left in with @Deprecated to use the getResults() list.

* @param grid The ItemGrid to check for validity
* @return True if the given input matches this recipe's requirements
*/
boolean isValid(GridInventory grid);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be left in as deprecated, potentially default implemented to return true? I'm not sure about it, but try to have as minimal of a breakage as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean there's nothing to break... it was always broken

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Maxqia There's a difference between binary compatibility and something being inherently broken. This is a binary breakage, anything relying on these methods (while they should be deprecated) would ultimately fail to work between API builds. I don't see the reason why this wouldn't be possible to retain for the sake of deprecation and uselessness (you can even print out a pretty printed log if you want)

* @return All registered recipes
*/
Set<Recipe> getRecipes();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary whitespace removal.

* type of recipe for this registry
*/
void register(Recipe recipe);
void register(Recipe recipe) throws IllegalArgumentException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is screaming about how it should use generics instead of just a raw Recipe type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was just about to say that to myself :P

*
* @return the experience given when this recipe is made
*/
float getExperience();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never use float, you gain nothing from it. Use double.

*
* @return item needed to make this recipe
*/
ItemStack getIngredient();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why you're using an ItemStack instead of an ItemStackSnapshot.

*
* @return the result of this {@link Recipe}
*/
ItemStack getResult();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise here.

* @param stack the {@link ItemStack}
* @return the experience of the {@link ItemStack}
*/
Optional<Float> getExperience(ItemStack stack);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use Optional<Double> or if you want, java.util.OptionalDouble.

* @return A ItemStack or {@link Optional#empty()} if the given
* ItemStack does not match any recipes.
*/
Optional<ItemStack> getResult(ItemStack stack);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, here, Optional<ItemStackSnapshot> instead.

* @param ingredient The ingredient
* @return fluent interface
*/
Builder setIngredient(ItemStack ingredient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to prefix the builder methods with set, just use for example ingredient.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not prefix builder methods with set.

*/
ItemStack getResult();

default List<ItemStack> getResults() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Override?


import java.util.List;

public interface SmeltingRecipe extends Recipe {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javadoc

* Sets the ingredient for the requirements of this {@link SmeltingRecipe}.
*
* @param ingredient The ingredient
* @return fluent interface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change this to @return This instance for chaining (see other builders)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I just copied that part of the comment from the other recipe builders, I'll change them too while I'm at it

@Maxqia Maxqia force-pushed the recipe branch 2 times, most recently from 2c10b77 to 304af21 Compare December 12, 2016 02:38
* @param stack the {@link ItemStack}
* @return the experience of the {@link ItemStack}
*/
Optional<Double> getExperience(ItemStack stack);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use java.util.OptionalDouble

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't have a bunch of functions that Optional has ...

* ItemStack does not match any recipes.
*/
Optional<ItemStack> getResult(ItemStack stack);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line

* @return A new {@link SmeltingRecipe}
*/
SmeltingRecipe build();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line

* @param exp the experience
* @return This builder, for chaining
*/
Builder experience(float exp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accept double

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missed that, will do

*
* @return the result of this {@link Recipe}
*/
ItemStack getResult();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ItemStackSnapshot

* @return A list of ItemStacks or {@link Optional#empty()} if the given
* {@link GridInventory} does not match any recipes.
*/
Optional<List<ItemStack>> getResults(GridInventory grid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it return an empty list for nothing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, have to agree here, why Optional instead of just an empty list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK, just moved it from Recipe....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* {@link GridInventory} does not match any recipes.
*/
Optional<List<ItemStack>> getResults(GridInventory grid);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line

* Removes the given Recipe from registration in this registry.
*
* @param recipe The Recipe to unregister
* @return if it was successful or not
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capitalize the if

* ItemGrid does not match this recipe's requirements.
*/
Optional<List<ItemStack>> getResults(GridInventory grid);
RecipeRegistry<?> getRegistry();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this recipe is restricted to a single registry you should throw an IllegalArgumentException if you try to register it in a different one.

Is there a builder method for this one?

* @param grid The ItemGrid to check for validity
* @return True if the given input matches this recipe's requirements
*/
boolean isValid(GridInventory grid);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ImO this method would be better returning the matching recipe. The javadoc are strange currently.

* @return The ingredients
*/
Collection<ItemStack> getIngredients();
Collection<ItemStackSnapshot> getIngredients();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting ingredients as snapshots makes sense but I wouldn't require them when adding to the builder. Makes working with the API painful (when it could turn them into snapshots itself).

* @return the results of this {@link Recipe}
*/
Collection<ItemStackSnapshot> getResults();
ImmutableCollection<ItemStackSnapshot> getResults();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if this returns any kind of Collection instead of this specialized one.

@Maxqia Maxqia changed the title Recipe Refactor [Not WIP] Recipe Refactor Jan 7, 2017
@stephan-gh stephan-gh changed the title [Not WIP] Recipe Refactor Recipe Refactor Jan 7, 2017

/**
* A RecipeRegistry holds all registered recipes for a given game.
* Used for {@link SmeltingRegistry} and {@link CraftingRegistry}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing full stop at end of sentence.

* @return the results of this {@link Recipe}
*/
boolean isValid(GridInventory grid);
ImmutableCollection<ItemStackSnapshot> getResults();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collection

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's immutable because we don't want people changing it...

* @return All registered recipes
*/
Set<Recipe> getRecipes();
ImmutableCollection<T> getRecipes();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collection

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use ImmutableCollection, just Collection is fine (you can even say it's unmodifiable in the javadoc).

* @return A list of ItemStacks or {@link Optional#empty()} if the given
* {@link GridInventory} does not match any recipes.
*/
Optional<ImmutableCollection<ItemStackSnapshot>> getResults(GridInventory grid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collection<ItemStackSnapshot>

*
* @return The ingredients
*/
ImmutableMap<Character, ItemStackSnapshot> getIngredients();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Map

*
* @return The aisle
*/
ImmutableCollection<String> getShape();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List

* @return The builder
*/
default Builder ingredients(ItemStack... ingredients) {
return ingredients(ingredients);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh, doesn't even compile

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? It does...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will cause StackOverflowException

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0-0 oh...

* @return A {@link ItemStackSnapshot} or {@link Optional#empty()} if the given
* {@link ItemType} does not match any recipes.
*/
Optional<ItemStackSnapshot> getResult(ItemType type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take ItemStackSnapshot as argument instead

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or ItemStack

Copy link
Contributor Author

@Maxqia Maxqia Jan 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put ItemType there because it ignores stack size. I'll probably be changing getExperience to that too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, meta is super important. Stack size can simply be ignored.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data on the ItemStack is 100% very important, not to just the ItemType.

* @return If it removed any recipes or not
*/
void remove(Recipe recipe);
boolean remove(Predicate<? super T> predicate);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remove the previous version of this method, add a new one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seconded, the removal of the recipe can still be retained.

* @param ingredients The ingredients
* @return The builder
*/
Builder ingredients(Collection<ItemStack> ingredients);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No ItemStackSnapshot support? (Sorry for nagging)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Maxqia

You can do both lol.

* @return the {@link RecipeRegistry} that corresponds with this Recipe
*/
Optional<List<ItemStack>> getResults(GridInventory grid);
RecipeRegistry<?> getRegistry();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for this method?

I'm not really against it being here, but IMO its simply not-required overhead.

  • If this is not yet/no longer registered, this would return null
  • (if ever supported) custom impls might return the wrong registry
  • I don't see a usecase

What are your thoughts adding this method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the point in having this method. Just gut it.

*
* <p>This returns an unmodifiable copy of the original aisle.</p>
*
* @return The aisle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aisle or shape?

* @param ingredients The ingredients
* @return The builder
*/
default Builder ingredients(ItemStack... ingredients) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, avoid only adding vararg methods like this, if possible, you should have single argument methods and then two argument methods with the second argument being a vararg. This would simplify some logic.

* @return The builder
*/
default Builder ingredients(ItemType... ingredients) {
return ingredients(Lists.newArrayList(ingredients).stream().map(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix this formatting, it looks bad. Refer to other areas where streams are used.

*/
ItemStackSnapshot getResult();

default ImmutableCollection<ItemStackSnapshot> getResults() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You javadoc everything else but not this method? I'm confused as to why there's a single result and then a collection of other results. What other results can there be?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly certain there are a number of mods which give out more than one result in the furnace, can't think of any off the top of my head though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, It's a defaulted method from Recipe :P. not sure if @OverRide works here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Maxqia Yes, @Override works here. You should always annotate the methods appropriately so it's plain and clear that the method doesn't originate from this interface/class and is an overridden method. Everything in Sponge follows this rule.

@kashike
Copy link
Contributor

kashike commented Apr 13, 2017

This is more than likely going to need changes with 1.12 arrives.

@gabizou
Copy link
Member

gabizou commented Apr 21, 2017

@kashike While that is true, would it be worth having the changes being made so that recipes can be managed in API 6 and maybe 7?

@kashike
Copy link
Contributor

kashike commented Jun 17, 2017

I'm going to close this in favour of #1582.

@kashike kashike closed this Jun 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants