From 0e262d5e71b53fc3f6576d004ad6852c3f2d771b Mon Sep 17 00:00:00 2001 From: ChiefArug <73862885+ChiefArug@users.noreply.github.com> Date: Sun, 5 Oct 2025 19:06:02 +1300 Subject: [PATCH 1/6] Reformat HungerManagerMixin to make it diff nicely with the vanilla code (vineflower) --- .../mixin/HungerManagerMixin.java | 133 +++++++++--------- 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java index c4fb53b..39687c1 100644 --- a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java +++ b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java @@ -2,13 +2,9 @@ import com.minenash.action_hunger.ActionHunger; import com.minenash.action_hunger.config.Config; -import net.minecraft.entity.damage.DamageSource; -import net.minecraft.entity.damage.DamageSources; import net.minecraft.entity.player.HungerManager; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Items; -import net.minecraft.registry.DynamicRegistryManager; -import net.minecraft.registry.Registries; import net.minecraft.world.Difficulty; import net.minecraft.world.GameRules; import org.spongepowered.asm.mixin.Mixin; @@ -45,12 +41,13 @@ public abstract class HungerManagerMixin { @Overwrite public void update(PlayerEntity player) { Difficulty difficulty = player.getWorld().getDifficulty(); - if (exhaustion > 4.0F) { - exhaustion -= 4.0F; - if (saturationLevel > 0.0F) - saturationLevel = Math.max(saturationLevel - 1.0F, 0.0F); - else if (difficulty != Difficulty.PEACEFUL) - foodLevel = Math.max(foodLevel - 1, 0); + if (this.exhaustion > 4.0F) { + this.exhaustion -= 4.0F; + if (this.saturationLevel > 0.0F) { + this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F); + } else if (difficulty != Difficulty.PEACEFUL) { + this.foodLevel = Math.max(this.foodLevel - 1, 0); + } } if (player.getAbilities().invulnerable) @@ -58,80 +55,80 @@ else if (difficulty != Difficulty.PEACEFUL) double dynamicRegenRateModifier = ActionHunger.getCurveModifier(player.getHealth(), Config.dynamicRegenRateCurve, Config.dynamicRegenRateMultiplier); - boolean regened = false; boolean isPlayerUsingShield = player.getActiveItem().getItem() == Items.SHIELD; if (isPlayerUsingShield) { - ++shieldExhaustionTimer; - if (shieldExhaustionTimer >= Config.shieldExhaustionRate) - exhaustion("Shield", Config.shieldExhaustionAmount); - } - else - shieldExhaustionTimer = 0; + ++this.shieldExhaustionTimer; + if (this.shieldExhaustionTimer >= Config.shieldExhaustionRate) { + if (Config.debug) + System.out.println("Exhaustion from " + "Shield" + ": " + Config.shieldExhaustionAmount); + this.addExhaustion(Config.shieldExhaustionAmount); + } + } else + this.shieldExhaustionTimer = 0; - boolean blockRegenFromShield = Config.disableRegenWhenUsingShield && isPlayerUsingShield; - if (player.getWorld().getGameRules().getBoolean(GameRules.NATURAL_REGENERATION) && !blockRegenFromShield) - regened = regen(player, dynamicRegenRateModifier); + boolean regened = true; - constantHungerTimer++; - if (constantHungerTimer >= Config.constantExhaustionRate * (Config.dynamicRegenOnConstantExhaustion ? dynamicRegenRateModifier : 1.0D)) { - exhaustion("Const", Config.constantExhaustionAmount); - constantHungerTimer = 0; - } + boolean blockRegenFromShield = Config.disableRegenWhenUsingShield && isPlayerUsingShield; + boolean bl = player.getWorld().getGameRules().getBoolean(GameRules.NATURAL_REGENERATION); + if (bl && !blockRegenFromShield) { + constantRegenTimer++; + if (constantRegenTimer >= Config.constantRegenRate * (Config.dynamicRegenOnConstantRegen ? dynamicRegenRateModifier : 1.0D)) { + if (Config.debug) + System.out.println("Heal from " + "Const" + ": " + Config.constantRegenAmount); + player.heal(Config.constantRegenAmount); + constantRegenTimer = 0; + } - if (foodLevel <= 0) { - ++foodTickTimer; - if (foodTickTimer >= Config.starvationDamageRate) { - if (player.getHealth() > 10.0F || difficulty == Difficulty.HARD || player.getHealth() > 1.0F && difficulty == Difficulty.NORMAL) - player.damage(player.getDamageSources().starve(), Config.starvationDamageAmount); - foodTickTimer = 0; + if (this.saturationLevel > 0.0F && player.canFoodHeal() && this.foodLevel >= Config.hyperFoodRegenMinimumHunger) { + ++this.foodTickTimer; + if (this.foodTickTimer >= Config.hyperFoodRegenRate * (Config.dynamicRegenOnHyperFoodRegen ? dynamicRegenRateModifier : 1.0D)) { + float f = Math.min(this.saturationLevel, 6.0F); + if (Config.debug) + System.out.println("Heal from " + "Hyper" + ": " + f / 6.0F * Config.hyperFoodRegenHealthMultiplier); + player.heal(f / 6.0F * Config.hyperFoodRegenHealthMultiplier); + if (Config.debug) + System.out.println("Exhaustion from " + "Hyper" + ": " + f * Config.hyperFoodRegenExhaustionMultiplier); + this.addExhaustion(f * Config.hyperFoodRegenExhaustionMultiplier); + this.foodTickTimer = 0; + } + } else if (this.foodLevel >= Config.foodRegenMinimumHunger && player.canFoodHeal()) { + ++this.foodTickTimer; + if (this.foodTickTimer >= Config.foodRegenRate * (Config.dynamicRegenOnFoodRegen ? dynamicRegenRateModifier : 1.0D)) { + if (Config.debug) + System.out.println("Heal from " + "Food" + ": " + Config.foodRegenHealthAmount); + player.heal(Config.foodRegenHealthAmount); + if (Config.debug) + System.out.println("Exhaustion from " + "Food" + ": " + Config.foodRegenExhaustionAmount); + this.addExhaustion(Config.foodRegenExhaustionAmount); + this.foodTickTimer = 0; + } + } else { + regened = false; } - } else if (!regened){ - foodTickTimer = 0; } - } - - private boolean regen(PlayerEntity player, double dynamicRegenRateModifier) { - constantRegenTimer++; - if (constantRegenTimer >= Config.constantRegenRate * (Config.dynamicRegenOnConstantRegen ? dynamicRegenRateModifier : 1.0D)) { - heal(player, "Const", Config.constantRegenAmount); - constantRegenTimer = 0; + this.constantHungerTimer++; + if (this.constantHungerTimer >= Config.constantExhaustionRate * (Config.dynamicRegenOnConstantExhaustion ? dynamicRegenRateModifier : 1.0D)) { + if (Config.debug) + System.out.println("Exhaustion from " + "Const" + ": " + Config.constantExhaustionAmount); + this.addExhaustion(Config.constantExhaustionAmount); + this.constantHungerTimer = 0; } - if (saturationLevel > 0.0F && player.canFoodHeal() && foodLevel >= Config.hyperFoodRegenMinimumHunger) { - ++foodTickTimer; - if (foodTickTimer >= Config.hyperFoodRegenRate * (Config.dynamicRegenOnHyperFoodRegen ? dynamicRegenRateModifier : 1.0D)) { - float f = Math.min(saturationLevel, 6.0F); - heal(player, "Hyper", f / 6.0F * Config.hyperFoodRegenHealthMultiplier); - exhaustion("Hyper", f * Config.hyperFoodRegenExhaustionMultiplier); - foodTickTimer = 0; - } - } else if (foodLevel >= Config.foodRegenMinimumHunger && player.canFoodHeal()) { - ++foodTickTimer; - if (foodTickTimer >= Config.foodRegenRate * (Config.dynamicRegenOnFoodRegen ? dynamicRegenRateModifier : 1.0D)) { - heal(player, "Food", Config.foodRegenHealthAmount); - exhaustion("Food", Config.foodRegenExhaustionAmount); - foodTickTimer = 0; + if (this.foodLevel <= 0) { + ++this.foodTickTimer; + if (this.foodTickTimer >= Config.starvationDamageRate) { + if (player.getHealth() > 10.0F || difficulty == Difficulty.HARD || player.getHealth() > 1.0F && difficulty == Difficulty.NORMAL) + player.damage(player.getDamageSources().starve(), Config.starvationDamageAmount); + this.foodTickTimer = 0; } + } else if (!regened){ + this.foodTickTimer = 0; } - else - return false; - return true; - } - - private void heal(PlayerEntity player, String source, float amount) { - if (Config.debug) - System.out.println("Heal from " + source + ": " + amount); - player.heal(amount); } - private void exhaustion(String source, float amount) { - if (Config.debug) - System.out.println("Exhaustion from " + source + ": " + amount); - addExhaustion(amount); - } } From 9566f5ece587afb5625484053841e5e43f69ee3e Mon Sep 17 00:00:00 2001 From: ChiefArug <73862885+ChiefArug@users.noreply.github.com> Date: Sun, 5 Oct 2025 19:11:25 +1300 Subject: [PATCH 2/6] Remove redirect from HungerManagerMixin --- .../action_hunger/mixin/HungerManagerMixin.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java index 39687c1..ceb5a37 100644 --- a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java +++ b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java @@ -1,5 +1,6 @@ package com.minenash.action_hunger.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.minenash.action_hunger.ActionHunger; import com.minenash.action_hunger.config.Config; import net.minecraft.entity.player.HungerManager; @@ -12,7 +13,6 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(HungerManager.class) public abstract class HungerManagerMixin { @@ -22,16 +22,20 @@ public abstract class HungerManagerMixin { @Shadow private float saturationLevel; @Shadow private int foodTickTimer; @Shadow public abstract void addExhaustion(float exhaustion); - @Shadow private void addInternal(int food, float exhaustion) {} @Unique private int constantRegenTimer = 0; @Unique private int constantHungerTimer = 0; @Unique private int shieldExhaustionTimer = 0; - @Redirect(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/HungerManager;addInternal(IF)V")) - private void actionHunger$modifyFoodComponent(HungerManager manager, int food, float saturation) { - addInternal(Math.round(food * Config.hungerFromFoodMultiplier), saturation * Config.saturationFromFoodMultiplier); + @ModifyExpressionValue(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/FoodComponent;nutrition()I")) + private int actionHunger$modifyFoodNutrition(int nutrition) { + return (int) (nutrition * Config.hungerFromFoodMultiplier); + } + + @ModifyExpressionValue(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/FoodComponent;saturation()F")) + private float actionHunger$modifyFoodSaturation(float saturation) { + return saturation * Config.saturationFromFoodMultiplier; } /** From 8a2fa202bdeac5079b5ed8675894e197edf37d61 Mon Sep 17 00:00:00 2001 From: ChiefArug <73862885+ChiefArug@users.noreply.github.com> Date: Sun, 5 Oct 2025 19:27:44 +1300 Subject: [PATCH 3/6] Move simple cache booleans to methods to ease conversion of mixin --- .../mixin/HungerManagerMixin.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java index ceb5a37..ffc2bf9 100644 --- a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java +++ b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java @@ -57,12 +57,8 @@ public void update(PlayerEntity player) { if (player.getAbilities().invulnerable) return; - double dynamicRegenRateModifier = ActionHunger.getCurveModifier(player.getHealth(), Config.dynamicRegenRateCurve, Config.dynamicRegenRateMultiplier); - - boolean isPlayerUsingShield = player.getActiveItem().getItem() == Items.SHIELD; - - if (isPlayerUsingShield) { + if (isIsPlayerUsingShield(player)) { ++this.shieldExhaustionTimer; if (this.shieldExhaustionTimer >= Config.shieldExhaustionRate) { if (Config.debug) @@ -74,11 +70,10 @@ public void update(PlayerEntity player) { boolean regened = true; - boolean blockRegenFromShield = Config.disableRegenWhenUsingShield && isPlayerUsingShield; boolean bl = player.getWorld().getGameRules().getBoolean(GameRules.NATURAL_REGENERATION); - if (bl && !blockRegenFromShield) { + if (bl && !shouldBlockRegenFromShield(player)) { constantRegenTimer++; - if (constantRegenTimer >= Config.constantRegenRate * (Config.dynamicRegenOnConstantRegen ? dynamicRegenRateModifier : 1.0D)) { + if (constantRegenTimer >= Config.constantRegenRate * (Config.dynamicRegenOnConstantRegen ? getCurveModifier(player) : 1.0D)) { if (Config.debug) System.out.println("Heal from " + "Const" + ": " + Config.constantRegenAmount); player.heal(Config.constantRegenAmount); @@ -87,7 +82,7 @@ public void update(PlayerEntity player) { if (this.saturationLevel > 0.0F && player.canFoodHeal() && this.foodLevel >= Config.hyperFoodRegenMinimumHunger) { ++this.foodTickTimer; - if (this.foodTickTimer >= Config.hyperFoodRegenRate * (Config.dynamicRegenOnHyperFoodRegen ? dynamicRegenRateModifier : 1.0D)) { + if (this.foodTickTimer >= Config.hyperFoodRegenRate * (Config.dynamicRegenOnHyperFoodRegen ? getCurveModifier(player) : 1.0D)) { float f = Math.min(this.saturationLevel, 6.0F); if (Config.debug) System.out.println("Heal from " + "Hyper" + ": " + f / 6.0F * Config.hyperFoodRegenHealthMultiplier); @@ -99,7 +94,7 @@ public void update(PlayerEntity player) { } } else if (this.foodLevel >= Config.foodRegenMinimumHunger && player.canFoodHeal()) { ++this.foodTickTimer; - if (this.foodTickTimer >= Config.foodRegenRate * (Config.dynamicRegenOnFoodRegen ? dynamicRegenRateModifier : 1.0D)) { + if (this.foodTickTimer >= Config.foodRegenRate * (Config.dynamicRegenOnFoodRegen ? getCurveModifier(player) : 1.0D)) { if (Config.debug) System.out.println("Heal from " + "Food" + ": " + Config.foodRegenHealthAmount); player.heal(Config.foodRegenHealthAmount); @@ -114,7 +109,7 @@ public void update(PlayerEntity player) { } this.constantHungerTimer++; - if (this.constantHungerTimer >= Config.constantExhaustionRate * (Config.dynamicRegenOnConstantExhaustion ? dynamicRegenRateModifier : 1.0D)) { + if (this.constantHungerTimer >= Config.constantExhaustionRate * (Config.dynamicRegenOnConstantExhaustion ? getCurveModifier(player) : 1.0D)) { if (Config.debug) System.out.println("Exhaustion from " + "Const" + ": " + Config.constantExhaustionAmount); this.addExhaustion(Config.constantExhaustionAmount); @@ -134,5 +129,21 @@ public void update(PlayerEntity player) { } + @Unique + private static double getCurveModifier(PlayerEntity player) { + return ActionHunger.getCurveModifier(player.getHealth(), Config.dynamicRegenRateCurve, Config.dynamicRegenRateMultiplier); + } + + + @Unique + private static boolean isIsPlayerUsingShield(PlayerEntity player) { + return player.getActiveItem().getItem() == Items.SHIELD; + } + + @Unique + private static boolean shouldBlockRegenFromShield(PlayerEntity player) { + return Config.disableRegenWhenUsingShield && isIsPlayerUsingShield(player); + } + } From 50131c31b0f35fa9840e798b6462068f4ebbfd40 Mon Sep 17 00:00:00 2001 From: ChiefArug <73862885+ChiefArug@users.noreply.github.com> Date: Sun, 5 Oct 2025 20:58:29 +1300 Subject: [PATCH 4/6] Include newer version of MixinExtras to be able to use Expressions --- build.gradle | 1 + src/main/resources/action_hunger.mixins.json | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/build.gradle b/build.gradle index 5701f6e..0a9ad5b 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ repositories { } dependencies { + include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.5.0"))) // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" diff --git a/src/main/resources/action_hunger.mixins.json b/src/main/resources/action_hunger.mixins.json index 6e0da89..24de419 100644 --- a/src/main/resources/action_hunger.mixins.json +++ b/src/main/resources/action_hunger.mixins.json @@ -17,5 +17,11 @@ ], "injectors": { "defaultRequire": 1 + }, + "mixinextras": { + "minVersion": "0.5.0" + }, + "overwrites": { + "requireAnnotations": true } } From 7087b5ea9a590f7634645530e14b237bea742e22 Mon Sep 17 00:00:00 2001 From: ChiefArug <73862885+ChiefArug@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:14:43 +1300 Subject: [PATCH 5/6] Split Overwrite into 16 mini injections --- .../mixin/HungerManagerMixin.java | 244 +++++++++++++----- 1 file changed, 174 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java index ffc2bf9..15a70c7 100644 --- a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java +++ b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java @@ -1,62 +1,79 @@ package com.minenash.action_hunger.mixin; +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import com.minenash.action_hunger.ActionHunger; import com.minenash.action_hunger.config.Config; import net.minecraft.entity.player.HungerManager; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Items; -import net.minecraft.world.Difficulty; -import net.minecraft.world.GameRules; +import org.spongepowered.asm.mixin.Debug; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; - +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Debug(export = true) @Mixin(HungerManager.class) public abstract class HungerManagerMixin { - @Shadow private int foodLevel; - @Shadow private float exhaustion; - @Shadow private float saturationLevel; - @Shadow private int foodTickTimer; @Shadow public abstract void addExhaustion(float exhaustion); + @Shadow private int prevFoodLevel; @Unique private int constantRegenTimer = 0; @Unique private int constantHungerTimer = 0; @Unique private int shieldExhaustionTimer = 0; - @ModifyExpressionValue(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/FoodComponent;nutrition()I")) + @ModifyExpressionValue( + method = "eat", + at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/FoodComponent;nutrition()I") + ) private int actionHunger$modifyFoodNutrition(int nutrition) { return (int) (nutrition * Config.hungerFromFoodMultiplier); } - @ModifyExpressionValue(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/FoodComponent;saturation()F")) + @ModifyExpressionValue( + method = "eat", + at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/FoodComponent;saturation()F") + ) private float actionHunger$modifyFoodSaturation(float saturation) { return saturation * Config.saturationFromFoodMultiplier; } - /** - * @author Minenash - * @reason Too complicated to with normal mixins - */ - @Overwrite - public void update(PlayerEntity player) { - Difficulty difficulty = player.getWorld().getDifficulty(); - if (this.exhaustion > 4.0F) { - this.exhaustion -= 4.0F; - if (this.saturationLevel > 0.0F) { - this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F); - } else if (difficulty != Difficulty.PEACEFUL) { - this.foodLevel = Math.max(this.foodLevel - 1, 0); - } - } + @WrapOperation( // no idea why this was in the original mixin + method = "update", + at = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/HungerManager;foodLevel:I", ordinal = 0) + ) + private int actionHunger$dontSetPrevFoodLevel(HungerManager instance, Operation original) { + return this.prevFoodLevel; + } + @Inject( + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getBoolean(Lnet/minecraft/world/GameRules$Key;)Z"), + cancellable = true, + order = 999 // go before shield exhaustion TODO: CONFIRM THIS + ) + private void actionHunger$earlyExitIfInvuln(PlayerEntity player, CallbackInfo ci) { if (player.getAbilities().invulnerable) - return; - + ci.cancel(); + } + @Inject( + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getBoolean(Lnet/minecraft/world/GameRules$Key;)Z") + ) + private void actionHunger$updateShieldExhaustion(PlayerEntity player, CallbackInfo ci) { if (isIsPlayerUsingShield(player)) { ++this.shieldExhaustionTimer; @@ -67,47 +84,128 @@ public void update(PlayerEntity player) { } } else this.shieldExhaustionTimer = 0; + } - boolean regened = true; - - boolean bl = player.getWorld().getGameRules().getBoolean(GameRules.NATURAL_REGENERATION); + @Definition(id = "bl", local = @Local(type = boolean.class)) + @Expression("bl") + @ModifyExpressionValue( + method = "update", + at = @At("MIXINEXTRAS:EXPRESSION"), + slice = @Slice(to = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/HungerManager;foodTickTimer:I")) + ) + private boolean actionHunger$constantRegen(boolean bl, PlayerEntity player) { if (bl && !shouldBlockRegenFromShield(player)) { - constantRegenTimer++; - if (constantRegenTimer >= Config.constantRegenRate * (Config.dynamicRegenOnConstantRegen ? getCurveModifier(player) : 1.0D)) { + this.constantRegenTimer++; + if (this.constantRegenTimer >= Config.constantRegenRate * (Config.dynamicRegenOnConstantRegen ? getCurveModifier(player) : 1.0D)) { if (Config.debug) System.out.println("Heal from " + "Const" + ": " + Config.constantRegenAmount); player.heal(Config.constantRegenAmount); - constantRegenTimer = 0; - } - - if (this.saturationLevel > 0.0F && player.canFoodHeal() && this.foodLevel >= Config.hyperFoodRegenMinimumHunger) { - ++this.foodTickTimer; - if (this.foodTickTimer >= Config.hyperFoodRegenRate * (Config.dynamicRegenOnHyperFoodRegen ? getCurveModifier(player) : 1.0D)) { - float f = Math.min(this.saturationLevel, 6.0F); - if (Config.debug) - System.out.println("Heal from " + "Hyper" + ": " + f / 6.0F * Config.hyperFoodRegenHealthMultiplier); - player.heal(f / 6.0F * Config.hyperFoodRegenHealthMultiplier); - if (Config.debug) - System.out.println("Exhaustion from " + "Hyper" + ": " + f * Config.hyperFoodRegenExhaustionMultiplier); - this.addExhaustion(f * Config.hyperFoodRegenExhaustionMultiplier); - this.foodTickTimer = 0; - } - } else if (this.foodLevel >= Config.foodRegenMinimumHunger && player.canFoodHeal()) { - ++this.foodTickTimer; - if (this.foodTickTimer >= Config.foodRegenRate * (Config.dynamicRegenOnFoodRegen ? getCurveModifier(player) : 1.0D)) { - if (Config.debug) - System.out.println("Heal from " + "Food" + ": " + Config.foodRegenHealthAmount); - player.heal(Config.foodRegenHealthAmount); - if (Config.debug) - System.out.println("Exhaustion from " + "Food" + ": " + Config.foodRegenExhaustionAmount); - this.addExhaustion(Config.foodRegenExhaustionAmount); - this.foodTickTimer = 0; - } - } else { - regened = false; + this.constantRegenTimer = 0; } + return true; } + return false; + } + + @ModifyConstant( // explicitly conflict with anyone else trying to do this. + method = "update", + constant = @Constant(intValue = 20) + ) + private int actionHunger$replaceMinHunger(int original) { + return Config.hyperFoodRegenMinimumHunger; + } + + + @ModifyConstant( // explicitly conflict with anyone else trying to do this. + method = "update", + constant = @Constant(intValue = 10) + ) + private int actionHunger$replaceFoodTickRate(int original, PlayerEntity player) { + return (int) (Config.hyperFoodRegenRate * (Config.dynamicRegenOnHyperFoodRegen ? getCurveModifier(player) : 1)); + } + + @ModifyArg( + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;heal(F)V", ordinal = 0), + require = 1, expect = 1, allow = 1 // extra assertions to make sure this only matches once + ) + private float actionHunger$multiplyHyperFoodRegen(float health) { + health *= Config.hyperFoodRegenHealthMultiplier; + if (Config.debug) System.out.println("Heal from Hyper: " + health); + return health; + } + + @ModifyArg( + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/HungerManager;addExhaustion(F)V", ordinal = 0), + require = 1, expect = 1, allow = 1 // extra assertions to make sure this only matches once + ) + private float actionHunger$multiplyHyperExhaustion(float exhaustion) { + exhaustion *= Config.hyperFoodRegenExhaustionMultiplier; + if (Config.debug) System.out.println("Exhaustion from " + "Hyper" + ": " + exhaustion); + return exhaustion; + } + + @Definition(id = "bl", local = @Local(type = boolean.class)) + @Expression("bl") + @ModifyExpressionValue( // idk why the original mixin had this + method = "update", + at = @At("MIXINEXTRAS:EXPRESSION"), + slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/HungerManager;foodTickTimer:I")) + ) + private boolean actionHunger$removeCondition(boolean bl) { + return true; // it is used in an AND so true is the noop value + } + + @ModifyConstant( // explicitly conflict with anyone else trying to do this. + method = "update", + constant = @Constant(intValue = 18) + ) + private int actionHunger$replaceMinSlowHealHunger(int original) { + return Config.foodRegenMinimumHunger; + } + + @ModifyConstant( + method = "update", + constant = @Constant(intValue = 80, ordinal = 0), + require = 1, expect = 1, allow = 1 // extra assertions to make sure this only matches once. + ) + private int actionHunger$replaceFoodRegenTicks(int original, PlayerEntity player) { + return (int) (Config.foodRegenRate * (Config.dynamicRegenOnFoodRegen ? getCurveModifier(player) : 1.0D)); + } + + @ModifyArg( // do not conflict with anyone trying to do this, instead ignore them. + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;heal(F)V", ordinal = 1), + require = 1, expect = 1, allow = 1 // extra assertions to make sure this only matches once + ) + private float actionHunger$replaceSlowFoodHealAmount(float amount) { + amount = Config.foodRegenHealthAmount; + if (Config.debug) + System.out.println("Heal from " + "Food" + ": " + amount); + return amount; + } + + @ModifyArg( + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/HungerManager;addExhaustion(F)V", ordinal = 1), + require = 1, expect = 1, allow = 1 // extra assertions to make sure this only matches once. + ) + private float actionHunger$replaceSlowFoodExhaustionAmount(float amount) { + amount = Config.foodRegenExhaustionAmount; + + if (Config.debug) + System.out.println("Exhaustion from " + "Food" + ": " + Config.foodRegenExhaustionAmount); + return amount; + } + + + @Inject( // in the original mixin this comes before the starving damage check, but because that does not use exhaustion we can safely put this at the end of the mixin as it is impossible to put it before the starving check + method = "update", + at = @At(value = "TAIL") + ) + private void actionHunger$updateConstantExhaustion(PlayerEntity player, CallbackInfo ci) { this.constantHungerTimer++; if (this.constantHungerTimer >= Config.constantExhaustionRate * (Config.dynamicRegenOnConstantExhaustion ? getCurveModifier(player) : 1.0D)) { if (Config.debug) @@ -115,18 +213,24 @@ public void update(PlayerEntity player) { this.addExhaustion(Config.constantExhaustionAmount); this.constantHungerTimer = 0; } + } - if (this.foodLevel <= 0) { - ++this.foodTickTimer; - if (this.foodTickTimer >= Config.starvationDamageRate) { - if (player.getHealth() > 10.0F || difficulty == Difficulty.HARD || player.getHealth() > 1.0F && difficulty == Difficulty.NORMAL) - player.damage(player.getDamageSources().starve(), Config.starvationDamageAmount); - this.foodTickTimer = 0; - } - } else if (!regened){ - this.foodTickTimer = 0; - } + @ModifyConstant( + method = "update", + constant = @Constant(intValue = 80, ordinal = 1), + require = 1, expect = 1, allow = 1 // extra assertions to make sure this only matches once. + ) + private int actionHunger$replaceStarvationDamageRate(int ticks) { + return Config.starvationDamageRate; + } + @ModifyArg( // do not conflict with anyone trying to do this, instead ignore them. + method = "update", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;damage(Lnet/minecraft/entity/damage/DamageSource;F)Z") + ) + private float actionHunger$replaceStarvationDamageAmount(float amount) { + // we could instead return amount * starvationDamageAmount, which would be the same in vanilla but if other mods modifyied this we would stack + return Config.starvationDamageAmount; } @Unique From 16116eaa2906e2d5511c71a4d0a5b77dbd24a010 Mon Sep 17 00:00:00 2001 From: ChiefArug <73862885+ChiefArug@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:27:31 +1300 Subject: [PATCH 6/6] Remove TODO --- .../com/minenash/action_hunger/mixin/HungerManagerMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java index 15a70c7..f312777 100644 --- a/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java +++ b/src/main/java/com/minenash/action_hunger/mixin/HungerManagerMixin.java @@ -63,7 +63,7 @@ public abstract class HungerManagerMixin { method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getBoolean(Lnet/minecraft/world/GameRules$Key;)Z"), cancellable = true, - order = 999 // go before shield exhaustion TODO: CONFIRM THIS + order = 999 // go before shield exhaustion ) private void actionHunger$earlyExitIfInvuln(PlayerEntity player, CallbackInfo ci) { if (player.getAbilities().invulnerable)