Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public enum GeyserAttributeType {
MINING_EFFICIENCY("minecraft:mining_efficiency", null, 0f, 1024f, 0f),
BLOCK_BREAK_SPEED("minecraft:block_break_speed", null, 0f, 1024f, 1f),
SUBMERGED_MINING_SPEED("minecraft:submerged_mining_speed", null, 0f, 20f, 0.2f),
WATER_MOVEMENT_EFFICIENCY("minecraft:water_movement_efficiency", null, 0f, 1f, 0f),

// Bedrock Attributes
ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f),
Expand All @@ -62,7 +63,9 @@ public enum GeyserAttributeType {
EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 0f),
HEALTH(null, "minecraft:health", 0f, 1024f, 20f),
HUNGER(null, "minecraft:player.hunger", 0f, 20f, 20f),
SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f);
SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f),
LAVA_MOVEMENT(null, "minecraft:lava_movement", 0f, 3.4028235E38f, 0.02f),
UNDERWATER_MOVEMENT(null, "minecraft:underwater_movement", 0f, 3.4028235E38f, 0.02f);
Comment on lines +67 to +68
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LAVA_MOVEMENT and UNDERWATER_MOVEMENT use a hard-coded float literal (3.4028235E38f) for the maximum value. Elsewhere in this enum (e.g., ATTACK_KNOCKBACK) Float.MAX_VALUE is used; using the constant here would improve readability and reduce the chance of typos or inconsistencies.

Suggested change
LAVA_MOVEMENT(null, "minecraft:lava_movement", 0f, 3.4028235E38f, 0.02f),
UNDERWATER_MOVEMENT(null, "minecraft:underwater_movement", 0f, 3.4028235E38f, 0.02f);
LAVA_MOVEMENT(null, "minecraft:lava_movement", 0f, Float.MAX_VALUE, 0.02f),
UNDERWATER_MOVEMENT(null, "minecraft:underwater_movement", 0f, Float.MAX_VALUE, 0.02f);

Copilot uses AI. Check for mistakes.

private final String javaIdentifier;
private final String bedrockIdentifier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,4 +572,10 @@ public void forceFlagUpdate() {
public boolean isGliding() {
return getFlag(EntityFlag.GLIDING);
}

public AttributeData updateDolphinsGrace(boolean value) {
AttributeData data = GeyserAttributeType.UNDERWATER_MOVEMENT.getAttribute(value ? 0.024f : 0.02f);
Comment on lines +576 to +577
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateDolphinsGrace hard-codes both the “off” value (0.02f) and the “on” value (0.024f). Consider deriving the “off” value from GeyserAttributeType.UNDERWATER_MOVEMENT.getDefaultValue() (to avoid drift if the default changes) and extracting the “on” multiplier/value into a named constant with a short rationale (magic numbers are hard to validate/maintain).

Suggested change
public AttributeData updateDolphinsGrace(boolean value) {
AttributeData data = GeyserAttributeType.UNDERWATER_MOVEMENT.getAttribute(value ? 0.024f : 0.02f);
/**
* Multiplier applied to the default underwater movement speed when Dolphin's Grace is active.
* 1.2x corresponds to Java's Dolphin's Grace movement speed increase.
*/
private static final float DOLPHINS_GRACE_UNDERWATER_SPEED_MULTIPLIER = 1.2f;
public AttributeData updateDolphinsGrace(boolean value) {
float baseUnderwaterMovement = (float) GeyserAttributeType.UNDERWATER_MOVEMENT.getDefaultValue();
float underwaterMovement = value
? baseUnderwaterMovement * DOLPHINS_GRACE_UNDERWATER_SPEED_MULTIPLIER
: baseUnderwaterMovement;
AttributeData data = GeyserAttributeType.UNDERWATER_MOVEMENT.getAttribute(underwaterMovement);

Copilot uses AI. Check for mistakes.
attributes.put(GeyserAttributeType.UNDERWATER_MOVEMENT, data);
return data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,39 @@

package org.geysermc.geyser.translator.protocol.java.entity;

import org.geysermc.geyser.level.EffectType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundRemoveMobEffectPacket;
import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.level.EffectType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundRemoveMobEffectPacket;

import java.util.Collections;

@Translator(packet = ClientboundRemoveMobEffectPacket.class)
public class JavaRemoveMobEffectTranslator extends PacketTranslator<ClientboundRemoveMobEffectPacket> {

@Override
public void translate(GeyserSession session, ClientboundRemoveMobEffectPacket packet) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (entity == null) {
return;
}

if (entity == session.getPlayerEntity()) {
session.getEffectCache().removeEffect(packet.getEffect());

if (packet.getEffect() == Effect.DOLPHINS_GRACE) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(entity.geyserId());
attributesPacket.setAttributes(Collections.singletonList(
session.getPlayerEntity().updateDolphinsGrace(false)));
session.sendUpstreamPacket(attributesPacket);
}
} else if (entity instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().removeEffect(packet.getEffect());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.level.EffectType;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.level.EffectType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.EntityEffectCache;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
Expand Down Expand Up @@ -81,6 +81,7 @@ public void translate(GeyserSession session, ClientboundUpdateMobEffectPacket pa
case ABSORPTION -> session.getPlayerEntity().getAttributes().get(GeyserAttributeType.ABSORPTION);
// Fixes https://github.com/GeyserMC/Geyser/issues/5388
case SPEED -> session.getPlayerEntity().getAttributes().get(GeyserAttributeType.MOVEMENT_SPEED);
case DOLPHINS_GRACE -> session.getPlayerEntity().updateDolphinsGrace(true);
default -> null;
};

Expand Down
12 changes: 11 additions & 1 deletion core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@
import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.level.EffectType;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.level.EffectType;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;

import java.util.Collections;
import java.util.Set;

public class DimensionUtils {
Expand Down Expand Up @@ -75,6 +77,14 @@ public static void switchDimension(GeyserSession session, JavaDimension javaDime
mobEffectPacket.setRuntimeEntityId(player.geyserId());
mobEffectPacket.setEffectId(EffectType.fromJavaEffect(effect).getBedrockId());
session.sendUpstreamPacket(mobEffectPacket);

if (effect == Effect.DOLPHINS_GRACE) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(player.geyserId());
attributesPacket.setAttributes(Collections.singletonList(
session.getPlayerEntity().updateDolphinsGrace(false)));
session.sendUpstreamPacket(attributesPacket);
}
}
// Effects are re-sent from server
entityEffects.clear();
Expand Down
Loading