From 3cb465015c716f6f071deff13e8f5b182ed8cf03 Mon Sep 17 00:00:00 2001 From: connorhartley Date: Sun, 16 Oct 2022 14:18:26 +1300 Subject: [PATCH] fix health scaling --- .../ServerPlayerEntityHealthScaleBridge.java | 2 +- .../level/ServerPlayerMixin_HealthScale.java | 66 ++++++++++--------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerEntityHealthScaleBridge.java b/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerEntityHealthScaleBridge.java index c8e67080268..b720c90e8c1 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerEntityHealthScaleBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerEntityHealthScaleBridge.java @@ -33,7 +33,7 @@ public interface ServerPlayerEntityHealthScaleBridge { void bridge$resetHealthScale(); - double bridge$getHealthScale(); + Double bridge$getHealthScale(); float bridge$getInternalScaledHealth(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_HealthScale.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_HealthScale.java index c807b33acb8..27eccc471d8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_HealthScale.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_HealthScale.java @@ -43,7 +43,6 @@ import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; import org.spongepowered.common.bridge.server.level.ServerPlayerEntityHealthScaleBridge; import org.spongepowered.common.mixin.core.world.entity.player.PlayerMixin; -import org.spongepowered.common.util.Constants; import java.util.Collection; import java.util.Iterator; @@ -58,8 +57,8 @@ public abstract class ServerPlayerMixin_HealthScale extends PlayerMixin implemen @Shadow public ServerGamePacketListenerImpl connection; // @formatter:on - private double impl$healthScale = Constants.Entity.Player.DEFAULT_HEALTH_SCALE; - private float impl$cachedModifiedHealth = -1; + private AttributeInstance impl$cachedMaxHealthAttribute = null; + private Double impl$healthScale = null; @Inject(method = "doTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getArmorValue()I", ordinal = 1)) private void updateHealthPriorToArmor(final CallbackInfo ci) { @@ -78,7 +77,6 @@ private void updateHealthPriorToArmor(final CallbackInfo ci) { } this.impl$healthScale = scale; - this.impl$cachedModifiedHealth = -1; this.lastSentHealth = -1.0F; ((SpongeDataHolderBridge) this).bridge$offer(Keys.HEALTH_SCALE, scale); @@ -89,8 +87,7 @@ private void updateHealthPriorToArmor(final CallbackInfo ci) { @Override public void bridge$resetHealthScale() { - this.impl$healthScale = Constants.Entity.Player.DEFAULT_HEALTH_SCALE; - this.impl$cachedModifiedHealth = -1; + this.impl$healthScale = null; this.lastSentHealth = -1.0F; ((SpongeDataHolderBridge) this).bridge$remove(Keys.HEALTH_SCALE); @@ -110,9 +107,12 @@ private void updateHealthPriorToArmor(final CallbackInfo ci) { final FoodData foodData = this.shadow$getFoodData(); this.connection.send(new ClientboundSetHealthPacket(this.bridge$getInternalScaledHealth(), foodData.getFoodLevel(), foodData.getSaturationLevel())); this.connection.send(new ClientboundUpdateAttributesPacket(this.shadow$getId(), dirtyInstances)); + // Reset the dirty instances since they've now been manually updated on the client. dirtyInstances.clear(); + // Clear the cached value, so it doesn't carry over to future calls to getInternalScaledHealth. + this.impl$cachedMaxHealthAttribute = null; } @Override @@ -130,6 +130,7 @@ private void updateHealthPriorToArmor(final CallbackInfo ci) { break; } } + if (!foundMax) { // Means we didn't find the max health attribute and need to fetch the modifiers from // the cached map because it wasn't marked dirty for some reason @@ -139,53 +140,56 @@ private void updateHealthPriorToArmor(final CallbackInfo ci) { final AttributeInstance attribute = new AttributeInstance(Attributes.MAX_HEALTH, i -> {}); if (this.bridge$isHealthScaled()) { attribute.setBaseValue(this.impl$healthScale); + } else { + attribute.setBaseValue(this.shadow$getMaxHealth()); } if (!modifiers.isEmpty()) { modifiers.forEach(attribute::addTransientModifier); } + set.add(attribute); + + this.impl$cachedMaxHealthAttribute = attribute; } @Override - public double bridge$getHealthScale() { + public Double bridge$getHealthScale() { return this.impl$healthScale; } @Override public float bridge$getInternalScaledHealth() { - if (!this.bridge$isHealthScaled()) { - return this.shadow$getHealth(); - } - if (this.impl$cachedModifiedHealth == -1) { - // Because attribute modifiers from mods can add onto health and multiply health, we - // need to replicate what the mod may be trying to represent, regardless whether the health scale - // says to show only x hearts. - final AttributeInstance maxAttribute = this.shadow$getAttribute(Attributes.MAX_HEALTH); - double modifiedScale = this.impl$healthScale; - // Apply additive modifiers - for (final AttributeModifier modifier : maxAttribute.getModifiers(AttributeModifier.Operation.ADDITION)) { - modifiedScale += modifier.getAmount(); - } + float maximumHealth = this.shadow$getMaxHealth(); + float healthScale = maximumHealth; - for (final AttributeModifier modifier : maxAttribute.getModifiers(AttributeModifier.Operation.MULTIPLY_BASE)) { - modifiedScale += modifiedScale * modifier.getAmount(); - } - - for (final AttributeModifier modifier : maxAttribute.getModifiers(AttributeModifier.Operation.MULTIPLY_TOTAL)) { - modifiedScale *= 1.0D + modifier.getAmount(); + // Attribute modifiers from mods can add onto health and multiply health, we need to replicate + // what the mod may be trying to represent, regardless whether the health scale says to show + // only x hearts. + if (this.bridge$isHealthScaled()) { + final AttributeInstance attribute; + if (this.impl$cachedMaxHealthAttribute == null) { + final Collection modifiers = this.shadow$getAttribute(Attributes.MAX_HEALTH).getModifiers(); + attribute = new AttributeInstance(Attributes.MAX_HEALTH, i -> {}); + + attribute.setBaseValue(this.impl$healthScale); + + if (!modifiers.isEmpty()) { + modifiers.forEach(attribute::addTransientModifier); + } + } else { + attribute = this.impl$cachedMaxHealthAttribute; } - this.impl$cachedModifiedHealth = (float) modifiedScale; + healthScale = (float) attribute.getValue(); } - return (this.shadow$getHealth() / this.shadow$getMaxHealth()) * this.impl$cachedModifiedHealth; + + return (this.shadow$getHealth() / maximumHealth) * healthScale; } @Override public boolean bridge$isHealthScaled() { - return this.impl$healthScale != Constants.Entity.Player.DEFAULT_HEALTH_SCALE; + return this.impl$healthScale != null; } } - -