Jump to content

Recommended Posts

Posted (edited)

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:
Posted

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.

Posted

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?

Posted
  On 8/19/2019 at 4:03 AM, codahq said:

what am I supposed to do now?

Expand  

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.

Posted (edited)

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.

Posted

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?

Posted
  On 8/19/2019 at 11:51 PM, 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.

Expand  

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.

Posted (edited)
  On 8/20/2019 at 12:25 AM, Draco18s said:

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

Expand  

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
Posted

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.javaFetching info... ShapedEnchantingBookRecipeFactory.javaFetching info... _factories.jsonFetching info... book_ender_stability.jsonFetching info...

Posted
  On 8/19/2019 at 11:51 PM, codahq said:

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

Expand  

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.

Posted
  On 8/20/2019 at 11:32 AM, Animefan8888 said:

Why would you have to return two different stacks?

Expand  

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.

Posted
  On 8/20/2019 at 5:35 PM, 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.

Expand  

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.

Posted

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.

Posted
  On 8/21/2019 at 4:27 AM, 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.

Expand  

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

Expand  

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.

Posted
  On 8/21/2019 at 4:27 AM, codahq said:

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

Expand  

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.

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • https://mclo.gs/9y5ciD2 anyone ever had this issue?  Internal exception illegal argument exception: unable to fit 3194354 into 3
    • Hoffman Law Recovery helped me recovered my lost funds
    • Hi! I'm trying to add my custom models/textures renderer like this: public class PonyPlayerWrapperRenderer extends EntityRenderer<Player> { // wrapper class under my LivingEntityRenderer class implementation private final PonyPlayerRenderer innerRenderer; private final PonyPlayerRenderer innerSlimRenderer; public PonyPlayerWrapperRenderer(final EntityRendererProvider.Context context) { super(context); System.out.println("creating new PonyPlayerWrapperRenderer"); this.innerRenderer = new PonyPlayerRenderer(context, false); this.innerSlimRenderer = new PonyPlayerRenderer(context, true); } @Override public void render(final Player entity, final float yaw, final float partialTicks, final PoseStack poseStack, final MultiBufferSource bufferSource, final int packedLight) { System.out.println("PonyPlayerWrapperRenderer render: " + entity.toString()); if (entity instanceof AbstractClientPlayer clientPlayer) { if (clientPlayer.getModelName().contains("slim")) { innerSlimRenderer.render(clientPlayer, yaw, partialTicks, poseStack, bufferSource, packedLight); } else { innerRenderer.render(clientPlayer, yaw, partialTicks, poseStack, bufferSource, packedLight); } } } @Override public ResourceLocation getTextureLocation(final Player player) { System.out.println("PonyPlayerWrapperRenderer getTextureLocation"); if (player instanceof AbstractClientPlayer clientPlayer) { return clientPlayer.getSkinTextureLocation(); } System.out.println("player instanceof AbstractClientPlayer is false"); return getDefaultSkin(player.getUUID()); } } public class PonyPlayerRenderer extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> { private final PlayerModel<AbstractClientPlayer> earthModel; private final PlayerModel<AbstractClientPlayer> pegasusModel; private final PlayerModel<AbstractClientPlayer> unicornModel; public PonyPlayerRenderer(final EntityRendererProvider.Context context, final boolean slim) { super( context, slim ? new PonyModelSlim(context.bakeLayer(PonyModelSlim.LAYER_LOCATION)) : new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)), 0.5f ); System.out.println("creating new PonyPlayerRenderer"); this.earthModel = slim ? new PonyModelSlim(context.bakeLayer(PonyModelSlim.LAYER_LOCATION)) : new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)); this.pegasusModel = new PegasusModel(context.bakeLayer(PegasusModel.LAYER_LOCATION)); this.unicornModel = new UnicornModel(context.bakeLayer(UnicornModel.LAYER_LOCATION)); } @Override public void render(final AbstractClientPlayer player, final float entityYaw, final float partialTicks, final PoseStack poseStack, final MultiBufferSource buffer, final int packedLight) { final PonyRace race = player.getCapability(PONY_DATA) .map(data -> ofNullable(data.getRace()).orElse(PonyRace.EARTH)) .orElse(PonyRace.EARTH); this.model = switch (race) { case PEGASUS -> pegasusModel; case UNICORN -> unicornModel; case EARTH -> earthModel; }; super.render(player, entityYaw, partialTicks, poseStack, buffer, packedLight); } @Override public ResourceLocation getTextureLocation(final AbstractClientPlayer player) { final PonyRace race = player.getCapability(PONY_DATA) .map(data -> ofNullable(data.getRace()).orElse(PonyRace.EARTH)) .orElse(PonyRace.EARTH); return switch (race) { case EARTH -> fromNamespaceAndPath(MODID, "textures/entity/earth_pony.png"); case PEGASUS -> fromNamespaceAndPath(MODID, "textures/entity/pegasus.png"); case UNICORN -> fromNamespaceAndPath(MODID, "textures/entity/unicorn.png"); }; } } @Mod.EventBusSubscriber(modid = MODID, bus = MOD, value = CLIENT) public class ClientRenderers { // mod bus render registration config @SubscribeEvent public static void onRegisterLayerDefinitions(final EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(PonyModel.LAYER_LOCATION, PonyModel::createBodyLayer); event.registerLayerDefinition(PonyModelSlim.LAYER_LOCATION, PonyModelSlim::createBodyLayer); event.registerLayerDefinition(PegasusModel.LAYER_LOCATION, PegasusModel::createBodyLayer); event.registerLayerDefinition(UnicornModel.LAYER_LOCATION, UnicornModel::createBodyLayer); event.registerLayerDefinition(InnerPonyArmorModel.LAYER_LOCATION, InnerPonyArmorModel::createBodyLayer); event.registerLayerDefinition(OuterPonyArmorModel.LAYER_LOCATION, OuterPonyArmorModel::createBodyLayer); } @SubscribeEvent public static void onRegisterRenderers(final EntityRenderersEvent.RegisterRenderers event) { event.registerEntityRenderer(EntityType.PLAYER, PonyPlayerWrapperRenderer::new); System.out.println("onRegisterRenderers end"); } } Method onRegisterRenderers() is called and I can see it being logged. But when I enter the world, my PonyWrapperRenderer render() method doesn't ever seem to be called. I also tried to put my renderer to EntityRenderDispatcher's playerRenderers via reflection: @Mod.EventBusSubscriber(modid = MODID, bus = MOD, value = CLIENT) public class ClientRenderers { @SubscribeEvent public static void onRegisterLayerDefinitions(final EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(PonyModel.LAYER_LOCATION, PonyModel::createBodyLayer); event.registerLayerDefinition(PonyModelSlim.LAYER_LOCATION, PonyModelSlim::createBodyLayer); event.registerLayerDefinition(PegasusModel.LAYER_LOCATION, PegasusModel::createBodyLayer); event.registerLayerDefinition(UnicornModel.LAYER_LOCATION, UnicornModel::createBodyLayer); event.registerLayerDefinition(InnerPonyArmorModel.LAYER_LOCATION, InnerPonyArmorModel::createBodyLayer); event.registerLayerDefinition(OuterPonyArmorModel.LAYER_LOCATION, OuterPonyArmorModel::createBodyLayer); } @SubscribeEvent public static void onClientSetup(final FMLClientSetupEvent event) { event.enqueueWork(() -> { try { final EntityRenderDispatcher dispatcher = Minecraft.getInstance().getEntityRenderDispatcher(); final Field renderersField = getEntityRenderDispatcherField("playerRenderers"); final Field itemInHandRenderer = getEntityRenderDispatcherField("itemInHandRenderer"); @SuppressWarnings("unchecked") final Map<String, EntityRenderer<? extends Player>> playerRenderers = (Map<String, EntityRenderer<? extends Player>>)renderersField.get(dispatcher); final PonyPlayerWrapperRenderer renderer = new PonyPlayerWrapperRenderer( new EntityRendererProvider.Context( dispatcher, Minecraft.getInstance().getItemRenderer(), Minecraft.getInstance().getBlockRenderer(), (ItemInHandRenderer)itemInHandRenderer.get(dispatcher), Minecraft.getInstance().getResourceManager(), Minecraft.getInstance().getEntityModels(), Minecraft.getInstance().font ) ); playerRenderers.put("default", renderer); playerRenderers.put("slim", renderer); System.out.println("Player renderers replaced"); } catch (final Exception e) { throw new RuntimeException("Failed to replace player renderers", e); } }); } private static Field getEntityRenderDispatcherField(final String fieldName) throws NoSuchFieldException { final Field field = EntityRenderDispatcher.class.getDeclaredField(fieldName); field.setAccessible(true); return field; } } But I receive the error before Minecraft Client appears (RuntimeException: Failed to replace player renderers - from ClientRenderers onClientSetup() method - and its cause below): java.lang.IllegalArgumentException: No model for layer anotherlittlepony:earth_pony#main at net.minecraft.client.model.geom.EntityModelSet.bakeLayer(EntityModelSet.java:18) ~[forge-1.20.1-47.4.0_mapped_official_1.20.1-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A} at net.minecraft.client.renderer.entity.EntityRendererProvider$Context.bakeLayer(EntityRendererProvider.java:69) ~[forge-1.20.1-47.4.0_mapped_official_1.20.1-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A} at com.thuggeelya.anotherlittlepony.client.renderer.pony.PonyPlayerRenderer.<init>(PonyPlayerRenderer.java:32) ~[main/:?] {re:classloading} at com.thuggeelya.anotherlittlepony.client.renderer.pony.PonyPlayerWrapperRenderer.<init>(PonyPlayerWrapperRenderer.java:24) ~[main/:?] {re:classloading} at com.thuggeelya.anotherlittlepony.client.renderer.ClientRenderers.lambda$onClientSetup$0(ClientRenderers.java:79) ~[main/:?] {re:classloading} ... 33 more Problem appears when EntityRendererProvider context tries to bakeLayer with my model layer location: new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)); // PonyPlayerRenderer.java:32 public class PonyModel extends PlayerModel<AbstractClientPlayer> { // the model class itself public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation( ResourceLocation.fromNamespaceAndPath(MODID, "earth_pony"), "main" ); public PonyModel(final ModelPart root) { super(root, false); } public static LayerDefinition createBodyLayer() { // some CubeListBuilder stuff for model appearance } } Textures PNGs are placed at: resources/assets/[my mod id]/textures/entity. My forge version is 1.20.1. Would appreciate any help.
    • Well, a bit more information about what you're trying to do would be helpful. e.g. why you're trying to use "INVOKE_ASSIGN" instead of "INVOKE". "INVOKE_ASSIGN" calls your code after the "target" is called and its value is stored, if applicable. "INVOKE" calls your code before the target is called. "target" expects a fully qualified name, as per the SpongePowered docs, if that name is going to be remapped (which it will be if your injecting into Minecraft itself and not another mod). For more information on fully qualified names versus canonical names, see the Java specifications. Here's an example of a working "@At" from my own code that targets the "getClosestsVulnerablePlayerToEntity" call inside a mob's logic: @At(value = "INVOKE_ASSIGN", target = "net.minecraft.world.World.getClosestVulnerablePlayerToEntity(Lnet/minecraft/entity/Entity;D)Lnet/minecraft/entity/player/EntityPlayer;") Hope this helps!
    • Ran it one more time just to check, and there's no errors this time on the log??? Log : https://mclo.gs/LnuaAiu I tried allocating more memory to the modpack, around 8000MB and it's still the same; stopping at "LOAD_REGISTRIES". Are some of the mods clashing, maybe? I have no clue what to do LOL
  • Topics

×
×
  • Create New...

Important Information

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