Reset metadata and attributes if needed when respawning (#4566)

* Reset metadata and attributes if needed when respawning

* Minor edits

* Reset attributes in JavaLoginTranslator

* Fix client bug when updating absorption and health in the same tick
This commit is contained in:
AJ Ferguson 2024-04-18 00:16:41 -04:00 committed by GitHub
parent 576a1b7d7a
commit 815456c549
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 57 additions and 5 deletions

View file

@ -51,7 +51,7 @@ public enum GeyserAttributeType {
MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f), MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f),
// Bedrock Attributes // 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), EXHAUSTION(null, "minecraft:player.exhaustion", 0f, 5f, 0f),
EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f), EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f),
EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 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 maximum;
private final float defaultValue; private final float defaultValue;
public AttributeData getAttribute() {
return getAttribute(defaultValue);
}
public AttributeData getAttribute(float value) { public AttributeData getAttribute(float value) {
return getAttribute(value, maximum); return getAttribute(value, maximum);
} }

View file

@ -55,6 +55,7 @@ import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity;
@ -283,7 +284,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
attributesPacket.setRuntimeEntityId(geyserId); attributesPacket.setRuntimeEntityId(geyserId);
// Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit // Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit
attributesPacket.setAttributes(Collections.singletonList( attributesPacket.setAttributes(Collections.singletonList(
new AttributeData("minecraft:absorption", 0.0f, 1024f, entityMetadata.getPrimitiveValue(), 0.0f))); GeyserAttributeType.ABSORPTION.getAttribute(entityMetadata.getPrimitiveValue())));
session.sendUpstreamPacket(attributesPacket); session.sendUpstreamPacket(attributesPacket);
} }
@ -307,7 +308,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
* Sets the parrot occupying the shoulder. Bedrock Edition requires a full entity whereas Java Edition just * Sets the parrot occupying the shoulder. Bedrock Edition requires a full entity whereas Java Edition just
* spawns it from the NBT data provided * spawns it from the NBT data provided
*/ */
private void setParrot(CompoundTag tag, boolean isLeft) { protected void setParrot(CompoundTag tag, boolean isLeft) {
if (tag != null && !tag.isEmpty()) { if (tag != null && !tag.isEmpty()) {
if ((isLeft && leftParrot != null) || (!isLeft && rightParrot != null)) { if ((isLeft && leftParrot != null) || (!isLeft && rightParrot != null)) {
// No need to update a parrot's data when it already exists // No need to update a parrot's data when it already exists

View file

@ -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.GlobalPos;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; 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.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 com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
@ -255,13 +256,51 @@ public class SessionPlayerEntity extends PlayerEntity {
return session.getAuthData().uuid(); 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() { public void resetMetadata() {
// Reset all metadata to their default values // Reset all metadata to their default values
// This is used when a player respawns // This is used when a player respawns
this.flags.clear();
this.initializeMetadata(); this.initializeMetadata();
// Reset air // Reset air
this.resetAir(); this.resetAir();
// Explicitly reset all metadata not handled by initializeMetadata
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)));
session.sendUpstreamPacket(attributesPacket);
dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, 0);
dirtyMetadata.put(EntityDataTypes.EFFECT_AMBIENCE, (byte) 0);
dirtyMetadata.put(EntityDataTypes.FREEZING_EFFECT_STRENGTH, 0f);
silent = false;
}
public void resetAttributes() {
attributes.clear();
maxHealth = GeyserAttributeType.MAX_HEALTH.getDefaultValue();
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(geyserId);
attributesPacket.setAttributes(Collections.singletonList(
GeyserAttributeType.MOVEMENT_SPEED.getAttribute()));
session.sendUpstreamPacket(attributesPacket);
} }
public void resetAir() { public void resetAir() {

View file

@ -712,7 +712,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Default move speed // Default move speed
// Bedrock clients move very fast by default until they get an attribute packet correcting the speed // Bedrock clients move very fast by default until they get an attribute packet correcting the speed
attributesPacket.setAttributes(Collections.singletonList( attributesPacket.setAttributes(Collections.singletonList(
new AttributeData("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f))); GeyserAttributeType.MOVEMENT_SPEED.getAttribute()));
upstream.sendPacket(attributesPacket); upstream.sendPacket(attributesPacket);
GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket(); GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket();

View file

@ -71,7 +71,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
// Remove all bossbars // Remove all bossbars
session.getEntityCache().removeAllBossBars(); session.getEntityCache().removeAllBossBars();
// Remove extra hearts, hunger, etc. // Remove extra hearts, hunger, etc.
entity.getAttributes().clear(); entity.resetAttributes();
entity.resetMetadata(); entity.resetMetadata();
// Reset weather // Reset weather

View file

@ -49,6 +49,14 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
SessionPlayerEntity entity = session.getPlayerEntity(); SessionPlayerEntity entity = session.getPlayerEntity();
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo(); PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
if (!packet.isKeepMetadata()) {
entity.resetMetadata();
}
if (!packet.isKeepAttributes()) {
entity.resetAttributes();
}
session.setSpawned(false); session.setSpawned(false);
entity.setHealth(entity.getMaxHealth()); entity.setHealth(entity.getMaxHealth());