Jump to content
View in the app

A better way to browse. Learn more.

Forge Forums

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

[Solved] [1.7.10] How would you replace the existing FoodStats Instance

Featured Replies

Posted

Hi everyone,

 

So EntityPlayer has

 protected FoodStats foodStats = new FoodStats();

I want to replace this instance with my own so that I can control the food behaviour completely. I also have created my own FoodStats Class and made it extend the vanilla FoodStats class. This is the class where I will implement custom food behaviour.

 

Now I know that to get the protected field I have to use

ObfuscationReflectionHelper.getPrivateValue(EntityPlayer.class, (EntityPlayer)event.entity, "foodStats", "field_71100_bB");

 

I also know that to set a protected field I can use

ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class, (EntityPlayer)event.entity, customFieldValue, "foodStats", "field_71100_bB");			

 

But I am unsure how to actually do the replacement so that when the player begins gameplay the vanilla FoodStats Instance is thrown away and mine is put in its place.

 

I have my EntityConstructing event, my IEEP class, a PlayerLoggedInEvent and a EntityJoinWorldEvent working and created but I am unsure what to do next.

 

Any help is appreciated.

 

  • Author

Because the old Food Stats stuff still continues to execute whereas my custom stuff doesnt

  • Author

Im particularily interested in changing the onUpdate Method to be my own.  This is my code

 

The Event

@SubscribeEvent
public void onEntityConstructing(EntityConstructing event) {

	if (event.entity instanceof EntityPlayer) {

		ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class, (EntityPlayer)event.entity, new CustomFoodStats(), "foodStats", "field_71100_bB");

	}
}

 

My Custom FoodStats Class (based on Vanilla

public class CustomFoodStats extends FoodStats
{
    /** The player's food level. */
    private int foodLevel = 20;
    /** The player's food saturation. */
    private float foodSaturationLevel = 5.0F;
    /** The player's food exhaustion. */
    private float foodExhaustionLevel;
    /** The player's food timer value. */
    private int foodTimer;
    private int prevFoodLevel = 20;
   
    /**
     * Args: int foodLevel, float foodSaturationModifier
     */
    
    public void addStats(int min, float max)
    {	this.foodLevel = Math.min(min + this.foodLevel, 20);
        this.foodSaturationLevel = Math.min(this.foodSaturationLevel + (float)min * max * 2.0F, (float)this.foodLevel);
    }
    
    public void func_151686_a(ItemFood food, ItemStack itemStack)
    { 	
        this.addStats(food.func_150905_g(itemStack), food.func_150906_h(itemStack));
    }

    /**
     * Handles the food game logic.
     */
   
    public void onUpdate(EntityPlayer player)
    {  
    System.out.println("OVERRIDDEN");
    }
    

    /**
     * Reads food stats from an NBT object.
     */
   
    public void readNBT(NBTTagCompound nbt)
    {	if (nbt.hasKey("foodLevel", 99))
        {
            this.foodLevel = nbt.getInteger("foodLevel");
            this.foodTimer = nbt.getInteger("foodTickTimer");
            this.foodSaturationLevel = nbt.getFloat("foodSaturationLevel");
            this.foodExhaustionLevel = nbt.getFloat("foodExhaustionLevel");
        }
    }

    /**
     * Writes food stats to an NBT object.
     */
   
    public void writeNBT(NBTTagCompound nbt)
    {	
        nbt.setInteger("foodLevel", this.foodLevel);
        nbt.setInteger("foodTickTimer", this.foodTimer);
        nbt.setFloat("foodSaturationLevel", this.foodSaturationLevel);
        nbt.setFloat("foodExhaustionLevel", this.foodExhaustionLevel);
    }

    /**
     * Get the player's food level.
     */
    
    public int getFoodLevel()
    {	return this.foodLevel;
    }

    
    @SideOnly(Side.CLIENT)
    public int getPrevFoodLevel()
    {	
        return this.prevFoodLevel;
    }

    /**
     * If foodLevel is not max.
     */
    
    public boolean needFood()
    {	
        return this.foodLevel < 20;
    }

    /**
     * adds input to foodExhaustionLevel to a max of 40
     */
   
    public void addExhaustion(float amount)
    {	 this.foodExhaustionLevel = Math.min(this.foodExhaustionLevel + amount, 40.0F);
    }

    /**
     * Get the player's food saturation level.
     */
    
    public float getSaturationLevel()
    {	 return this.foodSaturationLevel;
    }

   
    @SideOnly(Side.CLIENT)
    public void setFoodLevel(int amount)
    {	
        this.foodLevel = amount;
    }

   
    @SideOnly(Side.CLIENT)
    public void setFoodSaturationLevel(float amount)
    {	
        this.foodSaturationLevel = amount;
    }
}

  • Author

public class CustomFoodStats extends FoodStats
{
   
   @Override
    public void onUpdate(EntityPlayer player)
    {  super.onUpdate(player);
    System.out.println("OVERRIDDEN");
    }
     
}

 

for now thats all I want to change but eventually I want to change all of it

  • Author

But if I do

 

@SubscribeEvent
public void playerLogIn(PlayerLoggedInEvent event) {
ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class,	(EntityPlayer) event.player, new CustomFoodStats(),	"foodStats", "field_71100_bB");
}

 

Then I get "OVERRIDDEN" outprinted to consol (from my custom class). But my player still gains HP like he would normally due to the regular food stats onUpdate method...

  • Author

Yes EntityConstructing is being called for sure I can see an outprint when I add it. And the super is commented out sorry I must have accidentally deleted the //

  • Author

@SubscribeEvent
public void onEntityConstructing(EntityConstructing event) {

	if (event.entity instanceof EntityPlayer) {
		System.out.println("CAAAAAAAAAALLLLLLLLLLLLLLLEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDD");
		ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class,	(EntityPlayer) event.entity, new CustomFoodStats(),	"foodStats", "field_71100_bB");
	}
}

 

I see the print line so yes it is being called but I dont see the "OVERRIDDEN" being printed

 

public class CustomFoodStats extends FoodStats
{
    @Override
    public void onUpdate(EntityPlayer player)
    { 
    	System.out.println("OVERRIDDEN");
    }
     
}

 

And I should. I deleted the //super.onUpdate(player); but my player still gains health over time.... AND he shouldnt

  • Author

Funny Thing is if I move the refletion code to

@SubscribeEvent
public void playerLogIn(PlayerLoggedInEvent event) {
ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class,	(EntityPlayer) event.player, new CustomFoodStats(),	"foodStats", "field_71100_bB");
}

 

Then I do get Overridden printed to the consol but again the player still regens hp... And he shouldnt

  • Author

Exactly, and anyways I am currently using it but I need to disable or gain access to the healing of the player. The player is still being healed somehow and I have no idea how

  • Author

I want to conditionally disable player health regen hence why I wanted to replace the food stats instance with my own so that the player doesnt regen HP overtime and instead replace the regen with my own conditional regen code.

  • Author

Why would the players hp still regen if I replace the object inside EntityPlayer with my own? Where As you can see by my class  I clearly removed any reference to calling

player.heal(1.0F);

as I said PlayerJoinServer wont replace correct in all cases. I am not even sure if there is one case where it will work

  • Author

I found the following.

in vanilla FoodStats onUpdate

if (this.foodTimer >= 80)
            {
                p_75118_1_.heal(1.0F);
                this.addExhaustion(3.0F);
                this.foodTimer = 0;
            }

 

inEntityPlayer #onLivingUpdate

if (this.worldObj.difficultySetting == EnumDifficulty.PEACEFUL && this.getHealth() < this.getMaxHealth() && this.worldObj.getGameRules().getGameRuleBooleanValue("naturalRegeneration") && this.ticksExisted % 20 * 12 == 0)
        {
            this.heal(1.0F);
        }

 

and there is the issue that Failender Pointed out with the food stats not being replaced in all cases if I use Login event...

 

ugh... This altering of healing behaviour is a pain...

  • Author

I do think that the best event to use would be the

 

@SubscribeEvent
public void onEntityJoinWorld(EntityJoinWorldEvent event) {}

as this is called whenever the player joins the world (and yes when you die and respawn this does get called.)

So did you tested it on peaceful?

It is completely separate mechanism as shown in the code.

To change that, you should patch/replace player which would have more possibility of breaking compatibility.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

  • Author

Ok So I kind of have a fix but it kinda sucks haha. To overcome the problem with PEACEFUL Mode I simply decriment the players health everytime the onUpdate method incriments it. Kinda hacky n shitty but it works.

 

My custom FoodStats Class

public class CustomFoodStats extends FoodStats
{
/** The player's food amountl. */
    private int foodAmount = 15;
    /** The player's food saturation. */
    private float foodSaturation = 2.50F;
    /** The player's food exhaustion. */
    private float foodExhaustionAmount;
    /** The player's food bar timer value. */
    private int foodBarTimer;
    private int prevFoodAmount = 15;
       
    @Override
    public void onUpdate(EntityPlayer player)
    { 
    	 if (player.worldObj.difficultySetting == EnumDifficulty.PEACEFUL && player.getHealth() < player.getMaxHealth() && player.worldObj.getGameRules().getGameRuleBooleanValue("naturalRegeneration") && player.ticksExisted % 20 * 12 == 0)
         {
             player.heal(-1.0F);//Need to do this because the onLivingUpdate in EntityPlayer heals the player in peaceful mode.
         }
    	
        EnumDifficulty enumdifficulty = player.worldObj.difficultySetting;
        this.prevFoodAmount = this.foodAmount;

        if (this.foodExhaustionAmount > 4.0F)
        {
            this.foodExhaustionAmount -= 4.0F;

            if (this.foodSaturation > 0.0F)
            {
                this.foodSaturation = Math.max(this.foodSaturation - 1.0F, 0.0F);
            }
            else if (enumdifficulty != EnumDifficulty.PEACEFUL)
            {
                this.foodAmount = Math.max(this.foodAmount - 1, 0);
            }
        }

        if (player.worldObj.getGameRules().getGameRuleBooleanValue("naturalRegeneration") && this.foodAmount >= 18 && player.shouldHeal())
        {
            ++this.foodBarTimer;

            if (this.foodBarTimer >= 80)
            {
            	if(condition = true){
                //Do Custom Healing
                }else{
                player.heal(1.0F);//You can cancel healing by commenting this out
                }
                this.addExhaustion(3.0F);
                this.foodBarTimer = 0;
            }
        }
        else if (this.foodAmount <= 0)
        {
            ++this.foodBarTimer;

            if (this.foodBarTimer >= 80)
            {
                if (player.getHealth() > 10.0F || enumdifficulty == EnumDifficulty.HARD || player.getHealth() > 1.0F && enumdifficulty == EnumDifficulty.NORMAL)
                {
                    player.attackEntityFrom(DamageSource.starve, 1.0F);
                }

                this.foodBarTimer = 0;
            }
        }
        else
        {
            this.foodBarTimer = 0;
        }
    }
     
}

 

And my event which makes it all possible

@SubscribeEvent
public void onEntityJoinWorld(EntityJoinWorldEvent event) {

	if (event.entity instanceof EntityPlayer && !event.entity.worldObj.isRemote) {

	ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class,(EntityPlayer) event.entity, new CustomFoodStats(),"foodStats", "field_71100_bB");

	}
}

 

If you know of a better way to stop healing from happening I would love to hear about it. I can now just use my condition to trigger this and keep default behavior if my condition isnt true.

On the FoodStats, I think you'd better wrap existing foodstats as I mentioned earlier.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

I sometimes wonder if people have any level of creativity. Just because I said "You can probably use EntityConstructing event" doesn't mean you have to do it and it's the only way.

 

Yes you can do replacement whenever you want, but you can't do it in EntityConstructing - java won't allow you (seems like it's too soon :x).

 

Anyway:

public class ExtendedFoodStats extends FoodStats
{
FoodStats wrapped;

public ExtendedFoodStats(FoodStats wrapped)
{
	this.wrapped = wrapped;
}

@Override
public void onUpdate(EntityPlayer player)
{
	// Wrapping allows you to refere to parent stats with not only super,
	// which would only refere to super-class, but to ANY class that you've replaced.
	// So if there are two mods modifying FoodStats you (or other mod) would actually wrap around its predecessor.
	// Which wraps which depends on order of calling.
	// Eg. this.wrapped.onUpdate() will call object that was there previously. It can be vanilla one, or custom one you've replaced.
	System.out.println("FOOOOD");
}
}

 

What is so hard to understand about object wrapping? And yes - you shouldn't use it in your case - too big of a mod, won't work well with anything else so why bother.

 

public void onEntityJoined(EntityJoinWorldEvent event)
{
	if (event.entity instanceof EntityPlayer)
	{
		FoodStats foodStats = ((EntityPlayer) event.entity).getFoodStats();
		if (!(foodStats instanceof ExtendedFoodStats))
		{
			foodStats = new ExtendedFoodStats(foodStats);
			ObfuscationReflectionHelper.setPrivateValue(EntityPlayer.class, (EntityPlayer) event.entity, foodStats, "foodStats", "field_71100_bB");
		}
	}
}

 

EDIT:

Also, lol me - I didn't read second page of thread, seems like it was resolved :x

1.7.10 is no longer supported by forge, you are on your own.

And yes - you shouldn't use it in your case - too big of a mod, won't work well with anything else so why bother.

Why? There are some compatibility issues.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

  • Author

Yes there are my mod is at almost a million lines of code now i think. Also Thanks a bunch Ernio

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...

Important Information

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

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.