Jump to content

How to Avoid Using Int Enchant IDs in JSON Recipes


codahq

Recommended Posts

I'm trying to update my mod to 1.12.2.  Some of my recipes return books with stored enchants.  I can't figure out how to do this without using NBT and the int IDs of the enchants.  

 

 

According to @diesieben07 they should never be used.  How do I avoid using them in this scenario?  For example, I am using this as a recipe.  

 

{
    "type": "minecraft:crafting_shaped",
    "pattern":
    [
        "pbp",
        " p "
    ],
    "key":
    {
        "p":
        {
            "item": "minecraft:ender_pearl"
        },
        "b":
        {
            "item": "minecraft:book"
        }
    },
    "result":
    {
        "item": "minecraft:enchanted_book",
        "nbt": 
        {
            "StoredEnchantments": 
            [
                {
                    "id": 32,
                    "lvl": 1
                }
            ]
        }
    }
}

 

How can this be done without the int ID.  This is especially important since I also have custom enchants.

Edited by codahq
Additional Information:
Link to comment
Share on other sites

It took me a little while to figure this out because there aren't really that many examples out there but thanks for getting me on correct path.  Here is what I ended up doing.

 

JSON recipe:

{
    "type": "pearlmod:shaped_enchanting_book_recipe",
    "pattern": 
    [
        "fbf",
        " p "
    ],

    "key": 
    {
        "p": 
        {
            "item": "minecraft:ender_pearl"
        },

        "b": 
        {
            "item": "minecraft:book"
        },

        "f": 
        {
            "item": "minecraft:feather"
        }
    },

    "result": 
    {
        "item": "minecraft:enchanted_book",
        "enchantments": 
        [
            {
                "enchant": "minecraft:infinity",
                "lvl": 1
            }
        ]
    }
}

 

Parse method on recipe factory:

@Override
	public IRecipe parse(JsonContext context, JsonObject json)
	{
		ShapedOreRecipe recipe = ShapedOreRecipe.factory(context, json);
		ItemStack output = recipe.getRecipeOutput();

		final JsonObject result = JsonUtils.getJsonObject(json, "result");
		final String item = JsonUtils.getString(result, "item");
		assert item == "minecraft:enchanted_book";
		final JsonArray enchantments = JsonUtils.getJsonArray(result, "enchantments");
		for (JsonElement e : enchantments)
		{
			final String enchant = JsonUtils.getString(e.getAsJsonObject(), "enchant");
			final int lvl = JsonUtils.getInt(e.getAsJsonObject(), "lvl");
			EnchantmentData storedEnchant = new EnchantmentData(Enchantment.getEnchantmentByLocation(enchant), lvl);
			ItemEnchantedBook.addEnchantment(output, storedEnchant);
		}

		ShapedPrimer primer = new ShapedPrimer();
		primer.width = recipe.getRecipeWidth();
		primer.height = recipe.getRecipeHeight();
		primer.mirrored = JsonUtils.getBoolean(json, "mirrored", true);
		primer.input = recipe.func_192400_c();

		return new ShapedEnchantingBookRecipe(new ResourceLocation(Constants.modid, "shaped_enchanting_book_recipe"), output, primer);
	}

 

I'm not sure if there was a more effective way of parsing the JSON or not.  I'm not familiar with teh goog's JSON library and I didn't find any quick examples so I just fudged this together quickly.

 

At any rate, this works.  If anybody has a suggestion to make it better please contribute to the example.

Link to comment
Share on other sites

So, embarrassingly enough this doesn't work but I didn't notice because I didn't test it very well. 

 

At any rate, it works with the OOB enchants but it doesn't work with the custom enchants.  In parse it stores an int for the custom enchant's without using resource location for compatibility so it's just an int ID in NBT.  But by the time you are in-game and the recipe class's getCraftingResult method is called those enchant IDs have been changed.  Since I can't control when EITHER of the registry events happen (recipe factory or enchant registering), what am I supposed to do now?'

 

I guess I could store NBT data in the stack so I can find it later and just build the output stack when getCraftingResult is called but won't that break NEI, JEI, etc?

Link to comment
Share on other sites

5 hours ago, codahq said:

what am I supposed to do now?

Write your own IRecipe implementation. Potentially just override getResult() from one of the ShapedRecipes. (I think that is the methods name). And have it return a new ItemStack with the EnchantmentData that is stored in your IRecipe implementation.

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

Here's an example:

https://github.com/Draco18s/ReasonableRealism/blob/1.14.4/src/main/java/com/draco18s/hardlib/api/recipe/RecipeTagOutput.java

My recipe returns an output based on tags (as all things tagged as iron (copper, steel, tin...) ingots (dust, nuggets...) are all functionally iron ingots, and all I want is to have an iron ingot output, find all items tagged as iron ingot and get the first one), but the general process would be the same for grabbing enchantments by registry name.

Edited by Draco18s

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

Yes, thank you to both of you.  I understand that I need to implement logic on the result.  I'm not able to just implement logic in parse as I was doing previously.  The dilemma now is that the result itemstack that I give during parse (and pre-init) is different than the result itemstack that I would give during recipe use and when getCraftingResult is called in-game.

 

For example...  the id in the NBT for stored enchant in pre-init is assigned to, let's say 85, and written to the item stack.  But if I call Enchantment.getEnchantmentbyId(85) in-game that enchant ID has been moved since pre-init and it fails to find an enchant with 85.  If I get the id again with the resource location it returns 95 let's say.

 

So, if I use the old result with the old ID it has bad enchantment data in it.  I can recreate the output (which I'm doing in the current implementation in getCraftingResult) by getting the new id from the resource location but that breaks mods like JEI, NEI which must be caching or grabbing data created during pre-init.  

 

Doesn't that bother anybody?  That I have to return two different stacks?  Or is that the way it should be?  Shouldn't those stacks be the same?  Wouldn't you think the factory event should fire AFTER the enchant IDs are moved so that the result that is saved during the factory's parse stores a recipe result with the current enchant IDs?

Link to comment
Share on other sites

34 minutes ago, codahq said:

For example...  the id in the NBT for stored enchant in pre-init is assigned to, let's say 85, and written to the item stack.  But if I call Enchantment.getEnchantmentbyId(85) in-game that enchant ID has been moved since pre-init and it fails to find an enchant with 85.  If I get the id again with the resource location it returns 95 let's say.

And this is why you don't used numerical IDs anywhere ever.

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

3 hours ago, Draco18s said:

And this is why you don't used numerical IDs anywhere ever.

I'm not.  You're not getting it yet.  I'm using the resource location as is best practice.  Minecraft/forge is assigning the int ID.  Then it runs the recipe factories and builds the result with the first IDs.  Then at some point it changes the IDs.  Eventually in game when I use the same resource location I get a different ID back.  The recipe output built during pre-init has the wrong IDs.  Do you understand?

Edited by codahq
*spelling
Link to comment
Share on other sites

Let me tee it up for you.  See the attached code examples:

 

The output:

[22:24:34] [main/INFO] [STDOUT]: [me.bendy.pearlmod.recipe.ShapedEnchantingBookRecipeFactory:parse:48]: pre-init :: location: pearlmod:ender_stability id: 11 lvl: 1

 

And when I use the recipe in a table in-game later on:

[22:25:31] [Server thread/INFO] [STDOUT]: [me.bendy.pearlmod.recipe.ShapedEnchantingBookRecipeFactory$ShapedEnchantingBookRecipe:getCraftingResult:91]: in-game :: location: pearlmod:ender_stability id: 90 lvl: 1

 

PearlmodEnchants.java ShapedEnchantingBookRecipeFactory.java _factories.json book_ender_stability.json

Link to comment
Share on other sites

11 hours ago, codahq said:

Doesn't that bother anybody?  That I have to return two different stacks?

Why would you have to return two different stacks?

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 hours ago, Animefan8888 said:

Why would you have to return two different stacks?

Because forge has changed the int IDs of the enchants since the factories were called.  If I don't return a different stack, when the recipe is used the first stack is returned with IDs that no longer match any enchants.  Since the IDs are not the same anymore it the ItemStack shows as an enchanted book with stored enchants but the stored enchants are missing.

Link to comment
Share on other sites

2 minutes ago, codahq said:

Because forge has changed the int IDs of the enchants since the factories were called.  If I don't return a different stack, when the recipe is used the first stack is returned with IDs that no longer match any enchants.  Since the IDs are not the same anymore it the ItemStack shows as an enchanted book with stored enchants but the stored enchants are missing.

Why not just return one stack...Pass in the raw enchantment data. IE the registry names and levels. Then compute the enchantments after the registry has been loaded. In both the output methods in your IRecipe implementation because it appears you are only doing it in one.

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

That exactly what I did in my workaround.  I decided to put the resource location and level in the factory ItemStack so it would be there for easy access in getCraftingResult.  This is still a problem though because apparently mods like NEI/JEI look and cache recipes during pre-init or shortly afterwards.  If I don't put the wrong, soon-to-be-replaced enchants on the books during factory pre-init they won't show up in those mods.  

 

I don't need a workaround.  I don't need a solution.  I have that already.  I'm continuing to post now because I want somebody to realize and validate what I'm saying about the order of these events.  Why are the IDs changed AFTER recipe registration instead of before?  Or why aren't they adjusted?  I have to return ItemStacks with two different IDs for the same enchant for this to work.  The order should be changed so that I don't have to do that OR when the IDs are changed by forge forge SHOULD go to all of the places they are used and update them.

Link to comment
Share on other sites

6 hours ago, codahq said:

I'm continuing to post now because I want somebody to realize and validate what I'm saying about the order of these events.

It didn't replicate the issue when I downloaded your file you sent.
 

Quote

[04:02:58] [main/INFO] [STDOUT]: [me.bendy.pearlmod.recipe.ShapedEnchantingBookRecipeFactory:parse:47]: pre-init :: location: blah:stability id: 11 lvl: 1
...
[04:03:46] [Server thread/INFO] [STDOUT]: [me.bendy.pearlmod.recipe.ShapedEnchantingBookRecipeFactory$ShapedEnchantingBookRecipe:getCraftingResult:90]: in-game :: location: blah:stability id: 11 lvl: 1

Try creating a new world.

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 hours ago, codahq said:

This is still a problem though because apparently mods like NEI/JEI look and cache recipes during pre-init or shortly afterwards. 

That's their problem and they should fix it.

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

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.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Hello all, I recently tried to upgrade my Minecraft mod to version 1.21.3, and I seem to have a problem with the `RenderSystem#setShaderColor` method. Here is how the screen is usually supposed to look like: Here is how it looks at present: To further explain, the note labels atop of the note buttons are colored cyan (here) using `RenderSystem#setShaderColor`. The note buttons and labels get their colors from their native texture - white. This means that even though I set the coloring to apply to the label (top), it is applied to the button (bottom). What I conclude is happening then, in essence, is that this method (at least for my case) does not color the blit after - but rather the blit before..?  This also blocks me from later resetting the shader, as I need to set it back to white before the blitting is done, and not when it's done. So like... what? I've tested this further with other components that require this method, and this preceding-like behavior seems to be pretty consistent for them too.   I should mention that all the snippets I am about to show have all worked in past versions. In my class `ClientUtil`, the following methods are defined: public static void setShaderColor(final Color color, final float alpha) { RenderSystem.setShaderColor( color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, alpha ); } public static void setShaderColor(final Color color) { setShaderColor(color, 1); } public static void resetShaderColor() { setShaderColor(Color.WHITE); } //... public static RenderType guiRT(final ResourceLocation loc) { return RenderType.guiTextured(loc); } And here is the snippet from `NoteButtonRenderer#renderNote` using it: // "Note" here refers to those symbols in the middle of a note button protected void renderNote(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { final int noteWidth = noteButton.getWidth()/2, noteHeight = noteButton.getHeight()/2; ClientUtil.setShaderColor((noteButton.isPlaying() && !foreignPlaying) ? themeLoader.notePressed() : themeLoader.noteReleased() ); gui.blit(ClientUtil::guiRT, noteTextureProvider.get(), noteButton.getX() + noteWidth/2, noteButton.getY() + noteHeight/2, 0, 0, noteWidth, noteHeight, noteWidth, noteButton.getHeight()/2 ); ClientUtil.resetShaderColor(); } You may find the full source here. The odd thing is that Minecraft does seem to use this method the regular way I showed, for instance `SkyRenderer#renderSkyDisc` (not sure if I'l allowed to paste it). Could it be that I'm doing something wrong that leads to this issue..? I'm really stumped on this one.  I couldn't really find any change logs or documentation for this rendering API (even in this Fabric blog post), so I'm sorry if this seems obvious or anything.  Either way, any help would be appreciated.
    • This did work. rip Quark Thank you
    • It says Quark Requires zeta. do i just remove Quark? https://mclo.gs/cq799kI
    • It refers to the mod elenaidodge Backup the world and make a test without it
    • Remove the mod Zeta   If you have further issues, add the new crash-report with sites like https://mclo.gs/ and paste the link to it here
  • Topics

×
×
  • Create New...

Important Information

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