Jump to content

Potions wont recolor even when i change the liquidColor...?


Recommended Posts

Posted

As the title says, I used reflection to modify the liquidColor value of a healing potion (Potion.heal).

Whenever i use a command to change its color i let it print out the RGB color int in the console which does actually change (System.out.println(Potion.heal.getLiquidColor());)

but it does not seem to actually change the color once the potion texture has been loaded once...

 

Is there something i forgot to do? Let me know if you need to have some insight on my source code.

Posted
public void changeHealpotColor(int color){
    	try{
        	Class potionClass = Potion.class;
        	Field f = potionClass.getField("heal");
        	
        	f.setAccessible(true);

            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
          
        	ReflectionHelper.setPrivateValue(potionClass, Potion.heal, color, "liquidColor");
        }
        catch(Exception e){
        	e.printStackTrace();
        }
    }

this is the source code i use to change the liquidColor

Posted
2 minutes ago, diesieben07 said:

Why are you even accessing the "heal" field? You are not using it...

I thought this would remove the "final" modifier . I know it works without but i seem to not have removed it after testing. Even when i remove it it doesnt work.

I feel like i need to reload something within forge for the recoloring to work without restarting the game, but i do not know how

Posted
5 minutes ago, diesieben07 said:

Why are you even accessing the "heal" field? You are not using it...

public void changeHealpotColor(int color){
    	try{
        	Class potionClass = Potion.class;
        	ReflectionHelper.setPrivateValue(potionClass, Potion.heal, color, "liquidColor");
        	//Minecraft.getMinecraft().refreshResources();
        }
        catch(Exception e){
        	e.printStackTrace();
        }
    }

this is what i am using, the method i commented out is what i tried to do to re-render the potion overlay to hopefully update the color. But that did not work

Posted
Just now, diesieben07 said:

Why are you bumping? It's been barely over an hour since your last post. Plus you have not even posted and kind of question.

 

Please clarify what you actually want to achieve. You cannot really change the color at runtime, it is designed to be static.

My question is: Would there be any way to maybe reload the Potion class and the textures so that it would possibly work without having to restart minecraft?

Posted
Just now, diesieben07 said:

If you change the color field on client and server you do not need to update anything.

As i said before, when i change the color field it does work as long as i do not load any potions.

Once i have loaded them the color will stay to the last color i set it to before loading them in, even if i change the liquidColor again.

 

If i would log the color int in the console it will log the most recent color i want it to be. But it will still not render with that color because it loaded with another, previous, color.

 

How would i be able to make it update instantly after changing the color using the method i showed?

Posted
Just now, diesieben07 said:

I am not familiar with the term "loading a potion", please clarify.

If i start up a game and do not get the potion item to show up at all, either in an inventory or in my hand, i can change the color. Once i "load" the potion by showing it in an inventory or in my hand the color will have changed to the latest color i changed it to. But after that i can still change the liquidColor Integer but it will no longer update potion colors anymore.

 

I thought it wouldve been possible to make it still work by making all the textures reload, but that does not seem to work. Now I am lowkey clueless on how to make it possible to change the color whenever i want, even if i have shown/seen the item before.

Posted
1 minute ago, diesieben07 said:

I don't see from the code how that would make sense. The color is queried and computed again every tick. What Minecraft version are you on?

I'm on 1.8.9, would that be why?

Posted
4 minutes ago, diesieben07 said:

I am not familiar with how this works on 1.8.9 exactly. However that version is old and outdated, you should update.

I would do that if 1.9+ would be good for pvp. The mods i make have an audience that either plays on 1.7.10 or 1.8.9.

But you said that the color is queried and computed every tick, is there a way that i can see where that happens in the vanilla source code?

Posted
Just now, diesieben07 said:

That's what I deduced from the 1.12.x code. I do not have a 1.8.x workspace.

 

You can use the debugger to see what's going on in the code.

I will do that, I might have found a solution and i will post it here if it works so that other people would be able to benefit from it too

 

thanks for your help!

Posted
10 hours ago, diesieben07 said:

Update to at least 1.11.x, where you will have PotionColorCalculationEvent.

would it be possible to recode that event in forge 1.7.10 and 1.8.9?

Posted

Events don't work that way. Events are coded by Forge as patches against the vanilla code, so you can't just add the same code yourself in an older version. However, if you look at the code around where the event is called in 1.11+ you might get ideas about other ways to intercept it.

 

For example, I noticed that the event is actually fired during the metadata update and interestingly the metadata includes the potion color that is set to the data manager. So it may well be possible to set the datamanager directly. Here is the code in the event:

    protected void updatePotionMetadata()
    {
        if (this.activePotionsMap.isEmpty())
        {
            this.resetPotionEffectMetadata();
            this.setInvisible(false);
        }
        else
        {
            Collection<PotionEffect> collection = this.activePotionsMap.values();
            net.minecraftforge.event.entity.living.PotionColorCalculationEvent event = new net.minecraftforge.event.entity.living.PotionColorCalculationEvent(this, PotionUtils.getPotionColorFromEffectList(collection), areAllPotionsAmbient(collection), collection);
            net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event);
            this.dataManager.set(HIDE_PARTICLES, event.areParticlesHidden());
            this.dataManager.set(POTION_EFFECTS, event.getColor());
            this.setInvisible(this.isPotionActive(MobEffects.INVISIBILITY));
        }
    }

 

However, I can't remember if the datamanager was used in 1.8.9 or it might have been the data watcher instead. If so, you should look at the data watcher for potions to see if the color is in that. If so, then you might be able to override it directly.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted
5 minutes ago, jabelar said:

Events don't work that way. Events are coded by Forge as patches against the vanilla code, so you can't just add the same code yourself in an older version. However, if you look at the code around where the event is called in 1.11+ you might get ideas about other ways to intercept it.

 

For example, I noticed that the event is actually fired during the metadata update and interestingly the metadata includes the potion color that is set to the data manager. So it may well be possible to set the datamanager directly. Here is the code in the event:


    protected void updatePotionMetadata()
    {
        if (this.activePotionsMap.isEmpty())
        {
            this.resetPotionEffectMetadata();
            this.setInvisible(false);
        }
        else
        {
            Collection<PotionEffect> collection = this.activePotionsMap.values();
            net.minecraftforge.event.entity.living.PotionColorCalculationEvent event = new net.minecraftforge.event.entity.living.PotionColorCalculationEvent(this, PotionUtils.getPotionColorFromEffectList(collection), areAllPotionsAmbient(collection), collection);
            net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event);
            this.dataManager.set(HIDE_PARTICLES, event.areParticlesHidden());
            this.dataManager.set(POTION_EFFECTS, event.getColor());
            this.setInvisible(this.isPotionActive(MobEffects.INVISIBILITY));
        }
    }

 

However, I can't remember if the datamanager was used in 1.8.9 or it might have been the data watcher instead. If so, you should look at the data watcher for potions to see if the color is in that. If so, then you might be able to override it directly.

Yeah, in 1.8.9 it is handled by a DataWatcher, but unfortunately I have no idea how to override the method you mentioned above. 

Could you maybe explain that to me a bit more?

Posted
Just now, diesieben07 said:

I don't think you understand what "override" means.

 

The problem with this approach is that you need to set the color for every entity that has the potion effect and there is no fast way to get those, you'd have to loop through every entity in the world.

I do know what overriding means. I do not know how to get that to work in forge modding as i am still rather new to this.

 

The way i would want this to work is by setting the private boolean potionsNeedUpdate to true as soon as i change the color of a certain potion type. That way, correct me if im wrong, it should update all the potions to have its new liquidColor. My idea was to override the updatePotionMetadata() method from the LivingEntityBase class so that i can call that method whenever i need it. It makes sense for me, but it is probably a rather amateuristic way of seeing this.

Posted
1 minute ago, diesieben07 said:

Except there is not just one of these fields, it exists for every entity. You need to set it to true for every entity that is loaded and has the potion.

But modifying the vanilla EntityLivingBase class would be possible with ASM right? What if i just modify the vanilla method that updates potions so that i can get it to un every tick regardless of the boolean's state?

Posted
2 minutes ago, diesieben07 said:

Coremodding will not be supported on this forum. There is a perfectly adequate solution to your problem: Update.

Hacking around with a coremod is not a good idea.

As i said before, if mojang did not completely ignore the pvp community and keep the combat mechanics the same i would have never ever used minecraft versions from 2014.

But in that case I understand and respect it that you guys are not willing to support me into doing this. Regardless of that, I want to thank you for all your help.

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

    • You probably used jd-gui to open it, didn't you? Nothing wrong with that, I also made that mistake, except that Notch was a smart guy and he obfuscated the code. That's why you only see files called "a", "b", "c" and then a file that combines them all. As I said, use RetroMCP to deobfuscate the code so that you will 100% understand it and be able to navigate it.
    • Decompiling minecraft indev, infdev, alpha, beta or whichever legacy version is really easy. I'm not a plug, I just also got interested in modding legacy versions (Infdev to be specific). Use https://github.com/MCPHackers/RetroMCP-Java Once you install their client and the Zulu Architecture that they say they recommend (or use your own Java). I encountered some problems, so I run it with: "java -jar RetroMCP-Java-CLI.jar". You should run it in a seperate folder (not in downloads), otherwise the files and folders will go all over the place. How to use RetroMCP: Type setup (every time you want change version), copy-paste the version number from their list (they support indev), write "decompile" and done! The code will now be deobfuscated and filenames will be normal, instead of "a", "b" and "c"! Hope I helped you, but I don't expect you to reply, as this discussion is 9 years old! What a piece of history!  
    • I know that this may be a basic question, but I am very new to modding. I am trying to have it so that I can create modified Vanilla loot tables that use a custom enchantment as a condition (i.e. enchantment present = item). However, I am having trouble trying to implement this; the LootItemRandomChanceWithEnchantedBonusCondition constructor needs a Holder<Enchantment> and I am unable to use the getOrThrow() method on the custom enchantment declared in my mod's enchantments class. Here is what I have so far in the GLM:   protected void start(HolderLookup.Provider registries) { HolderLookup.RegistryLookup<Enchantment> registrylookup = registries.lookupOrThrow(Registries.ENCHANTMENT); LootItemRandomChanceWithEnchantedBonusCondition lootItemRandomChanceWithEnchantedBonusCondition = new LootItemRandomChanceWithEnchantedBonusCondition(0.0f, LevelBasedValue.perLevel(0.07f), registrylookup.getOrThrow(*enchantment here*)); this.add("nebu_from_deepslate", new AddItemModifier(new LootItemCondition[]{ LootItemBlockStatePropertyCondition.hasBlockStateProperties(Blocks.DEEPSLATE).build(), LootItemRandomChanceCondition.randomChance(0.25f).build(), lootItemRandomChanceWithEnchantedBonusCondition }, OrichalcumItems.NEBU.get())); }   Inserting Enchantments.[vanilla enchantment here] actually works but trying to declare an enchantment from my custom enchantments class as [mod enchantment class].[custom enchantment] does not work even though they are both a ResourceKey and are registered in Registries.ENCHANTMENT. Basically, how would I go about making it so that a custom enchantment declared as a ResourceKey<Enchantment> of value ResourceKey.create(Registries.ENCHANTMENT, ResourceLocation.fromNamespaceAndPath([modid], [name])), declared in a seperate enchantments class, can be used in the LootItemRandomChanceWithEnchantedBonusCondition constructor as a Holder? I can't use getOrThrow() because there is no level or block entity/entity in the start() method and it is running as datagen. It's driving me nuts.
    • Hi here is an update. I was able to fix the code so my mod does not crash Minecraft. Please understand that I am new to modding but I honestly am having a hard time understanding how anyone can get this to work without having extensive programming and debugging experience as well as searching across the Internet, multiple gen AI bots (claude, grok, openai), and examining source code hidden in the gradle directory and in various github repositories. I guess I am wrong because clearly there are thousands of mods so maybe I am just a newbie. Ok, rant over, here is a step by step summary so others can save the 3 days it took me to figure this out.   1. First, I am using forge 54.1.0 and Minecraft 1.21.4 2. I am creating a mod to add a shotgun to Minecraft 3. After creating the mod and compiling it, I installed the .jar file to the proper directory in Minecraft and used 1.21.4-forge-54.1.0 4. The mod immediately crashed with the error: Caused by: java.lang.NullPointerException: Item id not set 5. Using the stack trace, I determined that the Exception was being thrown from the net.minecraft.world.item.Item.Properties class 6. It seems that there are no javadocs for this class, so I used IntelliJ which was able to provide a decompiled version of the class, which I then examined to see the source of the error. Side question: Are there javadocs? 7. This method, specifically, was the culprit: protected String effectiveDescriptionId() {      return this.descriptionId.get(Objects.requireNonNull(this.id, "Item id not set"));  } 8. Now my quest was to determine how to set this.id. Looking at the same source file, I determined there was another method:  public Item.Properties setId(ResourceKey<Item> pId) {             this.id = pId;             return this;   } 9. So now, I need to figure out how to call setId(). This required working backwards a bit. Starting from the constructor, I stubbed out the variable p which is of type Item.Properties public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); Rather than putting this all on one line, I split it up for readability like this: private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); Here is was the missing function, setId(), which takes a type of ResourceKey<Item>. My next problem is that due to the apparent lack of documentation (I tried searching the docs on this site) I could not determine the full import path to ResourceKey. I did some random searching on the Internet and stumbled across a Github repository which gave two clues: import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; Then I created the rk variable like this: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); And now putting it all together in order: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); This compiled and the mod no longer crashes. I still have more to do on it, but hopefully this will save someone hours. I welcome any feedback and if I missed some obvious modding resource or tutorial that has this information. If not, I might suggest we add it somewhere for people trying to write a mod that doesn't crash. Thank you !!!  
  • Topics

×
×
  • Create New...

Important Information

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