From f91a6f8b804221a9727129ba5b565bff58c7acfa Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 15 Apr 2024 01:42:43 -0400 Subject: [PATCH] Fix client bug when updating absorption and health in the same tick --- .../entity/attribute/GeyserAttributeType.java | 6 +++++- .../entity/type/player/PlayerEntity.java | 2 +- .../type/player/SessionPlayerEntity.java | 21 ++++++++++++++++--- .../geyser/session/GeyserSession.java | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/attribute/GeyserAttributeType.java b/core/src/main/java/org/geysermc/geyser/entity/attribute/GeyserAttributeType.java index 234c1afe9..88d493275 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/attribute/GeyserAttributeType.java +++ b/core/src/main/java/org/geysermc/geyser/entity/attribute/GeyserAttributeType.java @@ -51,7 +51,7 @@ public enum GeyserAttributeType { MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f), // Bedrock Attributes - ABSORPTION(null, "minecraft:absorption", 0f, Float.MAX_VALUE, 0f), + ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f), EXHAUSTION(null, "minecraft:player.exhaustion", 0f, 5f, 0f), EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f), EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 0f), @@ -66,6 +66,10 @@ public enum GeyserAttributeType { private final float maximum; private final float defaultValue; + public AttributeData getAttribute() { + return getAttribute(defaultValue); + } + public AttributeData getAttribute(float value) { return getAttribute(value, maximum); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 7bbe4f648..20819f75e 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -284,7 +284,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { attributesPacket.setRuntimeEntityId(geyserId); // Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit attributesPacket.setAttributes(Collections.singletonList( - GeyserAttributeType.ABSORPTION.getAttribute(entityMetadata.getPrimitiveValue(), 1024f))); + GeyserAttributeType.ABSORPTION.getAttribute(entityMetadata.getPrimitiveValue()))); session.sendUpstreamPacket(attributesPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 32e0b3310..89aa540d8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeTyp import com.github.steveice10.mc.protocol.data.game.entity.metadata.GlobalPos; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; @@ -255,6 +256,14 @@ public class SessionPlayerEntity extends PlayerEntity { return session.getAuthData().uuid(); } + @Override + public void setAbsorptionHearts(FloatEntityMetadata entityMetadata) { + // The bedrock client can glitch when sending a health and absorption attribute in the same tick + // This can happen when switching servers. Resending the absorption attribute fixes the issue + attributes.put(GeyserAttributeType.ABSORPTION, GeyserAttributeType.ABSORPTION.getAttribute(entityMetadata.getPrimitiveValue())); + super.setAbsorptionHearts(entityMetadata); + } + public void resetMetadata() { // Reset all metadata to their default values // This is used when a player respawns @@ -268,10 +277,12 @@ public class SessionPlayerEntity extends PlayerEntity { setParrot(null, true); setParrot(null, false); + // Absorption is metadata in java edition + attributes.remove(GeyserAttributeType.ABSORPTION); UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); attributesPacket.setRuntimeEntityId(geyserId); attributesPacket.setAttributes(Collections.singletonList( - GeyserAttributeType.ABSORPTION.getAttribute(0f, 1024f))); + GeyserAttributeType.ABSORPTION.getAttribute(0f))); session.sendUpstreamPacket(attributesPacket); dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, 0); @@ -284,8 +295,12 @@ public class SessionPlayerEntity extends PlayerEntity { public void resetAttributes() { attributes.clear(); maxHealth = GeyserAttributeType.MAX_HEALTH.getDefaultValue(); - // Relying on the server to resend speed attribute - // Armor attribute reset would go here + + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(geyserId); + attributesPacket.setAttributes(Collections.singletonList( + GeyserAttributeType.MOVEMENT_SPEED.getAttribute())); + session.sendUpstreamPacket(attributesPacket); } public void resetAir() { diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 7a4a8ff6f..98475d65e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -712,7 +712,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // Default move speed // Bedrock clients move very fast by default until they get an attribute packet correcting the speed attributesPacket.setAttributes(Collections.singletonList( - new AttributeData("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f))); + GeyserAttributeType.MOVEMENT_SPEED.getAttribute())); upstream.sendPacket(attributesPacket); GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket();