mojira.dev
MC-268471

Jump Boost, Haste and Mining Fatigue status effects are not tied to their corresponding attributes

Since the addition of some attributes in 24w06a, notably generic.block_break_speed, generic.safe_fall_distance and the refactor of generic.jump_strength, players are now able to achieve the effects of Jump Boost, Mining Fatigue and Haste via attributes.

However, these effects and attributes are not directly tied to each other, unlike other effects and attributes such as Strength/Weakness with attack_damage, Speed/Slowness with movement_speed, Health Boost/Absorption with max_health/max_absorption or even Haste/Mining Fatigue with attack_speed.

This causes an inconsistency with the previously mentioned effects and other issues:

  • block_break_speed and jump_strength attributes don't scale with Jump Boost, Haste or Mining Fatigue amplifiers.

  • Both of these attributes do not appear in potion tooltips, such as the Jump Boost potion.

  • Combined effects of high amplifiers + max value attributes allow you to exceed the upper cap (behavior-wise), which is not the case with other effects.

Curiously, the recently added generic.safe_fall_distance is tied to Jump Boost as expected, so it shows the attribute in the tooltip and can't surpass the attribute cap.

How to reproduce

  1. Execute the next command:

    /attribute @s generic.jump_strength get
  2. Notice the current attribute value.

  3. Grant the player the next effect:

    /effect give @s jump_boost infinite 1
  4. Execute the first command again.

  5. Notice how the value hasn't been changed.

Comments 7

I didn't include Levitation and Slow Falling as these two have weird issues and work a bit different compared to generic.gravity. You can't easily mimic their behavior with this attribute, especially Slow Falling since it seems to have a hardcoded value and isn't affected by amplifiers, at least from my testing.

Code analysis for block breaking

net.minecraft.world.entity.player.Player

public float getDestroySpeed(BlockState block) {
    float speed = inventory.getDestroySpeed(block);
    if (speed > 1) {
        int effLevel = EnchantmentHelper.getBlockEfficiency(this);
        ItemStack item = getMainHandItem();
        // Efficiency
        if (effLevel > 0 && !item.isEmpty()) {
            speed += effLevel * effLevel + 1;
        }
    }
    // Haste
    if (MobEffectUtil.hasDigSpeed(this)) {
        speed *= 1 + (MobEffectUtil.getDigSpeedAmplification(this) + 1) * 0.2f;
    }

    if (hasEffect(MobEffects.DIG_SLOWDOWN)) {
        float multiplier;

        // Mining Fatigue
        switch (getEffect(MobEffects.DIG_SLOWDOWN).getAmplifier()) {
        case 0:
            multiplier = 0.3f;
            break;
        case 1:
            multiplier = 0.09f;
            break;
        case 2:
            multiplier = 0.0027f;
            break;
        case 3:
        default:
            multiplier = 0.00081f;
            break;
        }
        speed *= multiplier;
    }
    // Attribute multiplier
    speed *= (float) getAttributeValue(Attributes.BLOCK_BREAK_SPEED);

    if (isEyeInFluid(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(this)) {
        speed /= 5f;
    }
    if (!onGround()) {
        speed /= 5f;
    }

    return speed;
}

In this code, the following operations are applied to the block breaking speed:

  1. If you have efficiency, add efficiency level2+1

  2. Multiply by haste level * 0.2 + 1

  3. If you have mining fatigue, multiply by 0.3 for level 1, 0.09 for level 2, 0.027 for level 3 and 0.0081 for level 4 and beyond

  4. Multiply by attribute value

  5. If in water, divide by 5

  6. If not on ground, divide by 5

Step 1 can't be put into the attribute because it depends on the block you are breaking.

Steps 2 and 3 can be put into attribute modification due to being multiplication.
Steps 5 and 6 might be put into attribute modification, however they depend on the environment.

Note: Step 3 might be slightly difficult, since attribute modification is usually linear and here, exponential

Code analysis for jump boost

net.minecraft.world.entity.LivingEntity

protected float getJumpPower(float multiplier) {
    return (float) getAttributeValue(Attributes.JUMP_STRENGTH) * multiplier * getBlockJumpFactor() + getJumpBoostPower();
}

public float getJumpBoostPower() {
    return hasEffect(MobEffects.JUMP) ? 0.1f * (getEffect(MobEffects.JUMP).getAmplifier() + 1) : 0;
}

The jump strength is here calculated as follows:

  1. Take the generic.jump_strength attribute value

  2. Multiply it by the multiplier of the jump (for horses and rabbits)

  3. Multiply it by the multiplier of the block you're standing on (0.5 for honey block, 1 for everything else)

  4. If you have jump boost, add 0.1 * jump boost level

The problem is that the jump boost effect happens last and after the multiplication of two values that are dependent on the action and the environment.

A good way to observe this is to jump with a horse that has a generic.jump_strength of 0 and jump boost 10. This will cause the horse to always jump the same amount, whether you try to do a small jump or a high jump.
If this behavior should stay, it is pretty much impossible to put the jump boost into the attribute. If, however, the jump boost effect would apply within the attribute and the multipliers all come after, the behavior from before could not be recreated that easily and the jump strength will depend more on these other multipliers.

Don't Haste/Mining Fatigue also control swing speed and its animation, and if so, are these tied to any existing attributes?

For swing duration:

net.minecraft.world.entity.LivingEntity

public static final int SWING_DURATION = 6;
// ...
private int getCurrentSwingDuration() {
    if (MobEffectUtil.hasDigSpeed(this)) {
        return SWING_DURATION - (1 + MobEffectUtil.getDigSpeedAmplification(this));
    }
    if (hasEffect(MobEffects.DIG_SLOWDOWN)
        return SWING_DURATION + (1 + getEffect(MobEffects.DIG_SLOWDOWN).getAmplifier()) * 2;
    }
    return SWING_DURATION;
}

This is currently not controlled by an attribute, it could theoretically be an attribute, but I don't think it would really be that important (could be generic.swing_duration with a base value of 6 and additive modifiers for haste and mining fatigue).

For Slow Falling and Levitation:

net.minecraft.world.entity.LivingEntity

public void travel(Vec3 vec) {
    if (isControlledByLocalInstance()) {
        double gravity = getGravity();
        boolean falling = getDeltaMovement().y <= 0;
        if (falling && hasEffect(MobEffects.SLOW_FALLING)) {
            gravity = Math.min(gravity, 0.01);
        }
        // ...
        if (hasEffect(MobEffects.LEVITATION)) {
            motionY += (0.05 * (getEffect(MobEffects.LEVITATION).getAmplifier() + 1) - motion.y) * 0.2;
        }
        // ...
    }
}

Slow falling is just a gravity override (gravity = 0.01) if the gravity was greater.

Levitation gradually increases the motion in the y direction, similarly to falling, however levitation causes 0.8 additional friction, causing the maximum velocity to be reached more quickly. Otherwise it can be modeled by gravity that is approximately -0.000926 times the levitation level, which reaches the same terminal velocity, but more slowly.

The thing with Slow Falling is that while it scales the same way gravity does when falling, it behaves differently when the player jumps, because gravity would not hinder them from temporarily float upwards, while slow falling only allows you to jump 1 block.

For Levitation I think being controlled by the attribute wouldn't work well, at least currently, because gravity doesn't seem to behave correctly at very low values (MC-268414), so I recall it wasn't possible to set it above -0.003 to mimic amplifier 0 behavior.

About swing speed animation, it's probably best to leave it independent from block_break_speed if you want to recreate custom tool speed seamlessly. Although other attributes have mechanic + visual effect tied to them such as movement_speed, changing both speed and fov at different values.

syarumi

(Unassigned)

Confirmed

Commands

24w06a

Retrieved