Jump to content

[1.14.4] Custom IRecipeType


DiscardedMarrow

Recommended Posts

Forge version 1.14.4-28.0.45

Context:

I'm trying to implement an override for the new vanilla block "Blast Furnace" to make it produce 2 ingots for every supplied ore block.
For now I'm trying it out on a separate block but it will in the future override the vanilla block.

 

So far the json file for the recipe has been created:

Spoiler

{
  "type": "bettervanilla:blasting",
  "ingredient": {
    "item": "minecraft:iron_ore"
  },
  "result": "minecraft:iron_ingot",
  "count": 2,
  "experience": 0.7,
  "cookingtime": 100
}

 

Diff between this and the regular blasting recipe is the type of "bettervanilla:blasting" as well as "count".

 

As far as I've read through the code of vanilla this requires adding a new IRecipeType as one is needed when overriding AbstractCookingRecipe (as the vanilla BlastingRecipe does):

Spoiler

public AbstractCookingRecipe(IRecipeType<?> typeIn, ResourceLocation idIn, String groupIn, Ingredient ingredientIn, ItemStack resultIn, float experienceIn, int cookTimeIn) {
      ///...
   }

 

 

Issue:

I can't seem to figure out how to register a new IRecipeType as I can't subscribe to RegistryEvent<IRecipeType<?>>

Spoiler

// issue on IRecipeType<?>
// "Error:(48, 76) java: type argument net.minecraft.item.crafting.IRecipeType<?> is not within bounds of type-variable T"

@SubscribeEvent
    public static void onRecipeTypeRegistry(final RegistryEvent<IRecipeType<?>> RecipeTypeRegistry) {
        RecipeTypeRegistry.getRegistry().registerAll(

        );
    }

 

My guess for this issue is that "ForgeRegistries" does not have an "IForgeRegistry<IRecipeType<?>>" variable defined, but I really shouldn't be guessing on this.

I messed around with adding a registry for IRecipeType but gave up quickly as I just didn't know what I was doing.

 

It might just be that I'm doing it all backwards.

 

Any help is greatly appreciated, even if it's just a quick slap in the right direction.

Thanks!

 

(Will provide more information as quickly as I can if/when asked for.)

Link to comment
Share on other sites

4 hours ago, DiscardedMarrow said:

I can't seem to figure out how to register a new IRecipeType as I can't subscribe to RegistryEvent<IRecipeType<?>>

It's not an IRecipeType you need to register it's an IRecipeSerializer.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

I registered a RecipeSerializer before posting, which in hindsight i should've mentioned.
 

Spoiler

//setRegistryName("bettervanilla","blasting"); is called within the constructor for BlastingRecipeSerializer

public static final IRecipeSerializer<BlastingRecipe> BETTERBLASTINGRECIPE = new BlastingRecipeSerializer<>(BlastingRecipe::new,100);

@SubscribeEvent
    public static void onRecipeSerializerRegistry(final RegistryEvent.Register<IRecipeSerializer<?>> RecipesSerializerRegistry) {

        RecipesSerializerRegistry.getRegistry().registerAll(
                BETTERBLASTINGRECIPE
        );
    }

 

I also wasn't fully clear on that I'm trying to EXTEND AbstractCookingRecipe which requires an IRecipeType to be sent from the new constructor (in BlastingRecipe) via the super constructor for AbstractCookingRecipe.

Spoiler

public class BlastingRecipe extends AbstractCookingRecipe {
    ///...

    public BlastingRecipe(ResourceLocation idIn, String groupIn, Ingredient ingredientIn, ItemStack resultIn, float experienceIn, int cookTimeIn, int countIn) {
        super( /*IRecipeType<?> typeIn*/ , idIn, groupIn, ingredientIn, resultIn, experienceIn, cookTimeIn);
		///...
    }

 


So what I'm reading from your responses is basically that there's no point to extending AbstractCookingRecipe and instead, BlastingRecipe should just implement "IRecipe<IInventory>"?

Link to comment
Share on other sites

14 hours ago, DiscardedMarrow said:

I'm trying to implement an override for the new vanilla block "Blast Furnace" to make it produce 2 ingots for every supplied ore block.

If this is your end goal. Just create an IRecipeSerializer that outputs a BlastingRecipe and register it. Then using its registry name in a recipe file(json) make the recipe you want iron_ore --> iron_ingotx2. And I think you'll want it to be data.minecraft.recipes.iron_ingot_from_blasting.json

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

5 minutes ago, Animefan8888 said:

Just create an IRecipeSerializer that outputs a BlastingRecipe and register it.

That's what I've done yes, which isn't the issue. The issue is:

 

14 minutes ago, DiscardedMarrow said:

I'm trying to EXTEND AbstractCookingRecipe which requires an IRecipeType to be sent from the new constructor (in BlastingRecipe) via the super constructor for AbstractCookingRecipe.

And there doesn't seem to be a way to create new instances or extended instances of IRecipeType.
 

Basically if I can't send the super I can't create an instance of BlastingRecipe which in turn means I can't instanciate BlastingRecipeSerializer.
This is just telling me to avoid extending AbstractCookingRecipe so I'll try that and see where it takes me.

Link to comment
Share on other sites

8 minutes ago, DiscardedMarrow said:

This is just telling me to avoid extending AbstractCookingRecipe so I'll try that and see where it takes me.

You don't need to do this at all.

9 minutes ago, DiscardedMarrow said:

I can't instanciate BlastingRecipeSerializer.

Why would you want to do that in the first place. Make your own serializer and copy the relevant code.

10 minutes ago, DiscardedMarrow said:

I can't create an instance of BlastingRecipe

The BlastingRecipe constructor is public you can instantiate one. new BlastingRecipe(...)

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

9 minutes ago, Animefan8888 said:
21 minutes ago, DiscardedMarrow said:

This is just telling me to avoid extending AbstractCookingRecipe so I'll try that and see where it takes me.

You don't need to do this at all.

I don't know what you're referring to. I don't need to avoid extending it??

 

10 minutes ago, Animefan8888 said:
21 minutes ago, DiscardedMarrow said:

I can't instanciate BlastingRecipeSerializer.

Why would you want to do that in the first place. Make your own serializer and copy the relevant code

So I can register it??

Spoiler
36 minutes ago, DiscardedMarrow said:

I registered a RecipeSerializer before posting, which in hindsight i should've mentioned.
 

  Reveal hidden contents



//setRegistryName("bettervanilla","blasting"); is called within the constructor for BlastingRecipeSerializer

public static final IRecipeSerializer<BlastingRecipe> BETTERBLASTINGRECIPE = new BlastingRecipeSerializer<>(BlastingRecipe::new,100);

@SubscribeEvent
    public static void onRecipeSerializerRegistry(final RegistryEvent.Register<IRecipeSerializer<?>> RecipesSerializerRegistry) {

        RecipesSerializerRegistry.getRegistry().registerAll(
                BETTERBLASTINGRECIPE
        );
    }

 

I also wasn't fully clear on that I'm trying to EXTEND AbstractCookingRecipe which requires an IRecipeType to be sent from the new constructor (in BlastingRecipe) via the super constructor for AbstractCookingRecipe.

  Reveal hidden contents



public class BlastingRecipe extends AbstractCookingRecipe {
    ///...

    public BlastingRecipe(ResourceLocation idIn, String groupIn, Ingredient ingredientIn, ItemStack resultIn, float experienceIn, int cookTimeIn, int countIn) {
        super( /*IRecipeType<?> typeIn*/ , idIn, groupIn, ingredientIn, resultIn, experienceIn, cookTimeIn);
		///...
    }

 


So what I'm reading from your responses is basically that there's no point to extending AbstractCookingRecipe and instead, BlastingRecipe should just implement "IRecipe<IInventory>"?

 

 

Link to comment
Share on other sites

Instead of extending AbstractCookingRecipe I now implement IRecipe<IInventory> but I still need to send back an IRecipeType<?> due to the implemented method "IRecipeType<?> getType()"

Spoiler

///...

/** Should look like {@link net.minecraft.item.crafting.AbstractCookingRecipe} **/
public class BlastingRecipe implements IRecipe<IInventory> {
    public final ResourceLocation id;
    public final String group;
    public final Ingredient ingredient;
    public final ItemStack result;
    public final float experience;
    public final int cookTime;
    public final int count;

    public BlastingRecipe(ResourceLocation idIn, String groupIn, Ingredient ingredientIn, ItemStack resultIn, float experienceIn, int cookTimeIn, int countIn) {
        this.id = idIn;
        this.group = groupIn;
        this.ingredient = ingredientIn;
        this.result = resultIn;
        this.experience = experienceIn;
        this.cookTime = cookTimeIn;
        this.count = countIn;
    }

    public boolean matches(IInventory inv, World worldIn) {
        return this.ingredient.test(inv.getStackInSlot(0));
    }

    public ItemStack getCraftingResult(IInventory inv) {
        return this.result.copy();
    }

    /**
     * Used to determine if this recipe can fit in a grid of the given width/height
     */
    public boolean canFit(int width, int height) {
        return true;
    }

    public NonNullList<Ingredient> getIngredients() {
        NonNullList<Ingredient> nonnulllist = NonNullList.create();
        nonnulllist.add(this.ingredient);
        return nonnulllist;
    }

    /**
     * Gets the experience of this recipe
     */
    public float getExperience() {
        return this.experience;
    }

    /**
     * Get the result of this recipe, usually for display purposes (e.g. recipe book). If your recipe has more than one
     * possible result (e.g. it's dynamic and depends on its inputs), then return an empty stack.
     */
    public ItemStack getRecipeOutput() {
        return this.result;
    }

    /**
     * Recipes with equal group are combined into one button in the recipe book
     */
    public String getGroup() {
        return this.group;
    }

    /**
     * Gets the cook time in ticks
     */
    public int getCookTime() {
        return this.cookTime;
    }

    public ResourceLocation getId() {
        return this.id;
    }

    public int getCount() {
        return count;
    }

    @Override
    public IRecipeSerializer<?> getSerializer() {
        return MiscInit.BETTERBLASTINGRECIPE;
    }

	//This is passed as null for now since I don't know what to pass which obviously gives me a NullPointerException
    public IRecipeType<?> getType() {
        return null;
    }
}

 

I get a NullPointerException as I don't know what to pass through getType() (using any of the vanilla recipe types obv wouldnt work so i should pass a type for my recipe) but what it says is kind of interesting:

Caused by: java.lang.NullPointerException: null key in entry: null={bettervanilla:iron_ingot_from_blasting=chokemonster.bettervanilla.recipes.BlastingRecipe@37d5d3647}

It seems to be understanding that the recipe is there at least but I don't know what else to make out of this error.

 

Here's my code for the BlastingRecipeSerializer if it helps shed some light on the situation:

Spoiler

///...

public class BlastingRecipeSerializer<T extends BlastingRecipe> extends net.minecraftforge.registries.ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<T> {

    private final int cookingTime;
    private final BlastingRecipeSerializer.IFactory<T> iBlastRecipeFactory;

    public BlastingRecipeSerializer(BlastingRecipeSerializer.IFactory<T> factoryIn, int cookingTimeIn) {
        this.cookingTime = cookingTimeIn;
        this.iBlastRecipeFactory = factoryIn;
        setRegistryName("bettervanilla","betterblasting");
    }
  
  	//The reads and writes will be cleaned up when I'm done with this issue
  
    public T read(ResourceLocation recipeId, JsonObject json) {
        String s = JSONUtils.getString(json, "group", "");
        JsonElement jsonelement = (JsonElement)(JSONUtils.isJsonArray(json, "ingredient") ? JSONUtils.getJsonArray(json, "ingredient") : JSONUtils.getJsonObject(json, "ingredient"));
        Ingredient ingredient = Ingredient.deserialize(jsonelement);
        //Forge: Check if primitive string to keep vanilla or a object which can contain a count field.
        if (!json.has("result")) throw new com.google.gson.JsonSyntaxException("Missing result, expected to find a string or object");
        ItemStack itemstack;
        if (json.get("result").isJsonObject()) itemstack = ShapedRecipe.deserializeItem(JSONUtils.getJsonObject(json, "result"));
        else {
            String s1 = JSONUtils.getString(json, "result");
            ResourceLocation resourcelocation = new ResourceLocation(s1);
            itemstack = new ItemStack(ForgeRegistries.ITEMS.getValue(resourcelocation));
        }
        float experience = JSONUtils.getFloat(json, "experience", 0.0F);
        int cookingTime = JSONUtils.getInt(json, "cookingtime", this.cookingTime);
        int count = JSONUtils.getInt(json, "count", 1);
        return this.iBlastRecipeFactory.create(recipeId, s, ingredient, itemstack, experience, cookingTime, count);
    }

    public T read(ResourceLocation recipeId, PacketBuffer buffer) {
        String s = buffer.readString(32767);
        Ingredient ingredient = Ingredient.read(buffer);
        ItemStack itemstack = buffer.readItemStack();
        float f = buffer.readFloat();
        int i = buffer.readVarInt();
        int count = buffer.readVarInt();
        return this.iBlastRecipeFactory.create(recipeId, s, ingredient, itemstack, f, i, count);
    }

    public void write(PacketBuffer buffer, T recipe) {
        buffer.writeString(recipe.group);
        recipe.ingredient.write(buffer);
        buffer.writeItemStack(recipe.result);
        buffer.writeFloat(recipe.experience);
        buffer.writeVarInt(recipe.cookTime);
        buffer.writeVarInt(recipe.count);
    }

    public interface IFactory<T extends BlastingRecipe> {
        T create(ResourceLocation resourceLocation, String s, Ingredient ingredient, ItemStack itemStack, float experience, int cookingTime, int count);
    }
}

 

 

I've also read through the Docs for 1.13 regarding the _factories file hoping it might be necessary in some way I haven't understood yet so if it might be linked to the issue I would love to know how, or even get a clear description on how or where it should be used.

Thanks.

Link to comment
Share on other sites

Look at existing usages? ICraftingRecipe is as far back as you should really go, which returns IRecipeType.CRAFTING from getType, distinguishing it from SMELTING, BLASTING, SMOKING, CAMPFIRE_COOKING, and STONECUTTING.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

4 minutes ago, Draco18s said:

Look at existing usages?

Yeah I've looked around a lot and even tried just outright returning IRecipeType.CRAFTING from my recipe just to see if I got any errors that would feed me some information but no luck. I'll look around some more though, specifically at ICraftingRecipe, and see if anything clicks.
Thanks Draco.

Link to comment
Share on other sites

2 hours ago, DiscardedMarrow said:

Yeah I've looked around a lot and even tried just outright returning IRecipeType.CRAFTING from my recipe just to see if I got any errors that would feed me some information but no luck. I'll look around some more though, specifically at ICraftingRecipe, and see if anything clicks.
Thanks Draco.

If you want it to be for the Blast Furnace you probably need to return the BLASTING type instead of the CRAFTING type.

  • Like 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

So.
I made it work using IRecipeType.BLASTING and I wish I could explain it better but it's 03:23 AM and I need rest.

However, I have a public git repo if anyone finds this and is curious how I did it (or wants to yell at me for doing it wrong).

https://github.com/kimcodekill/BetterVanilla

 

Thanks for your help @Draco18s & @Animefan8888

Link to comment
Share on other sites

3 minutes ago, DiscardedMarrow said:

(or wants to yell at me for doing it wrong).

This is my time to shine lol.

 

It would make more sense for the result to be a Json Object and also contain the count tag.

 

Why does this class even exist? You can access all the fields in the BlastingRecipe vanilla provides with its getters.

 

That's all I really have to say. But i could also ask why you are using an IFactory to create your BlastingRecipes instead of just calling the constructor, but it seems Vanilla has them too.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

4 minutes ago, Animefan8888 said:

It would make more sense for the result to be a Json Object and also contain the count tag.

Very true, I'll do this for next version.

 

4 minutes ago, Animefan8888 said:

Why does this class even exist? You can access all the fields in the BlastingRecipe vanilla provides with its getters.

I used the count in this class earlier, so I agree. It's obsolete and I'll look at it for next version.

 

6 minutes ago, Animefan8888 said:

But i could also ask why you are using an IFactory to create your BlastingRecipes instead of just calling the constructor, but it seems Vanilla has them too.

Yeah I felt safer doing it like this for now because vanilla does it and I saw another post on the forums where they used an IFactory. I agree though it would be a lot simpler if I just used the constructor.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.