diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index fd0673062..ed2cda9b9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -673,7 +673,7 @@ public final class EntityDefinitions { SLIME = EntityDefinition.inherited(SlimeEntity::new, mobEntityBase) .type(EntityType.SLIME) .heightAndWidth(0.51f) - .addTranslator(MetadataType.INT, SlimeEntity::setScale) + .addTranslator(MetadataType.INT, SlimeEntity::setSlimeScale) .build(); MAGMA_CUBE = EntityDefinition.inherited(MagmaCubeEntity::new, SLIME) .type(EntityType.MAGMA_CUBE) 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 88d493275..f19912a8c 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 @@ -49,6 +49,7 @@ public enum GeyserAttributeType { ATTACK_KNOCKBACK("minecraft:generic.attack_knockback", null, 1.5f, Float.MAX_VALUE, 0f), ATTACK_SPEED("minecraft:generic.attack_speed", null, 0f, 1024f, 4f), MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f), + SCALE("minecraft:generic.scale", null, 0.0625f, 16f, 1f), // Unused. Do we need this? // Bedrock Attributes ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f), diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 58a3bf8e7..d27fa3cad 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -86,6 +86,19 @@ public class LivingEntity extends Entity { */ private boolean isMaxFrozenState = false; + /** + * The base scale entity data, without attributes applied. Used for such cases as baby variants. + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private float scale; + /** + * The scale sent through the Java attributes packet + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private float attributeScale; + public LivingEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } @@ -122,6 +135,9 @@ public class LivingEntity extends Entity { @Override protected void initializeMetadata() { + // Initialize here so overriding classes don't have 0 values + this.scale = 1f; + this.attributeScale = 1f; super.initializeMetadata(); // Matches Bedrock behavior; is always set to this dirtyMetadata.put(EntityDataTypes.STRUCTURAL_INTEGRITY, 1); @@ -230,6 +246,21 @@ public class LivingEntity extends Entity { return freezingPercentage; } + protected void setScale(float scale) { + this.scale = scale; + applyScale(); + } + + private void setAttributeScale(float scale) { + this.attributeScale = scale; + applyScale(); + } + + private void applyScale() { + // Take any adjustments Bedrock requires, and compute it alongside the attribute's additional changes + this.dirtyMetadata.put(EntityDataTypes.SCALE, scale * attributeScale); + } + /** * @return a Bedrock health attribute constructed from the data sent from the server */ @@ -366,6 +397,11 @@ public class LivingEntity extends Entity { case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE)); case GENERIC_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH)); + case GENERIC_SCALE -> { + // Attribute on Java, entity data on Bedrock + setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute)); + updateBedrockMetadata(); + } } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java index 5b1d682ce..8f84e051b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.entity.type.living; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -44,12 +43,12 @@ public class AgeableEntity extends CreatureEntity { protected void initializeMetadata() { super.initializeMetadata(); // Required as of 1.19.3 Java - dirtyMetadata.put(EntityDataTypes.SCALE, getAdultSize()); + setScale(getAdultSize()); } public void setBaby(BooleanEntityMetadata entityMetadata) { boolean isBaby = entityMetadata.getPrimitiveValue(); - dirtyMetadata.put(EntityDataTypes.SCALE, isBaby ? getBabySize() : getAdultSize()); + setScale(isBaby ? getBabySize() : getAdultSize()); setFlag(EntityFlag.BABY, isBaby); setBoundingBoxHeight(definition.height() * (isBaby ? getBabySize() : getAdultSize())); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java index fce51e741..d057f09c7 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java @@ -311,7 +311,7 @@ public class ArmorStandEntity extends LivingEntity { if (!isInvisible) { // The armor stand isn't invisible. We good. setFlag(EntityFlag.INVISIBLE, false); - dirtyMetadata.put(EntityDataTypes.SCALE, getScale()); + setScale(getScale()); updateOffsetRequirement(false); if (secondEntity != null) { @@ -327,7 +327,7 @@ public class ArmorStandEntity extends LivingEntity { if (!isNametagEmpty && (!helmet.equals(ItemData.AIR) || !chestplate.equals(ItemData.AIR) || !leggings.equals(ItemData.AIR) || !boots.equals(ItemData.AIR) || !hand.equals(ItemData.AIR) || !offhand.equals(ItemData.AIR))) { // Reset scale of the proper armor stand - this.dirtyMetadata.put(EntityDataTypes.SCALE, getScale()); + setScale(getScale()); // Set the proper armor stand to invisible to show armor setFlag(EntityFlag.INVISIBLE, true); // Update the position of the armor stand @@ -350,7 +350,7 @@ public class ArmorStandEntity extends LivingEntity { // Guarantee this copy is NOT invisible secondEntity.setFlag(EntityFlag.INVISIBLE, false); // Scale to 0 to show nametag - secondEntity.getDirtyMetadata().put(EntityDataTypes.SCALE, 0.0f); + secondEntity.setScale(0f); // No bounding box as we don't want to interact with this entity secondEntity.getDirtyMetadata().put(EntityDataTypes.WIDTH, 0.0f); secondEntity.getDirtyMetadata().put(EntityDataTypes.HEIGHT, 0.0f); @@ -360,7 +360,7 @@ public class ArmorStandEntity extends LivingEntity { } else if (isNametagEmpty) { // We can just make an invisible entity // Reset scale of the proper armor stand - dirtyMetadata.put(EntityDataTypes.SCALE, getScale()); + setScale(getScale()); // Set the proper armor stand to invisible to show armor setFlag(EntityFlag.INVISIBLE, true); // Update offset @@ -374,7 +374,7 @@ public class ArmorStandEntity extends LivingEntity { // Nametag is not empty and there is no armor // We don't need to make a new entity setFlag(EntityFlag.INVISIBLE, false); - dirtyMetadata.put(EntityDataTypes.SCALE, 0.0f); + setScale(0f); // As the above is applied, we need an offset updateOffsetRequirement(!isMarker); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/SlimeEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/SlimeEntity.java index 50095fe3f..3be2db1db 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/SlimeEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/SlimeEntity.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.entity.type.living; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; @@ -39,8 +38,8 @@ public class SlimeEntity extends MobEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } - public void setScale(IntEntityMetadata entityMetadata) { - dirtyMetadata.put(EntityDataTypes.SCALE, 0.10f + entityMetadata.getPrimitiveValue()); + public void setSlimeScale(IntEntityMetadata entityMetadata) { + setScale(0.10f + entityMetadata.getPrimitiveValue()); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GiantEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GiantEntity.java index e98c8f120..6bef3ae3e 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GiantEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GiantEntity.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -36,7 +35,11 @@ public class GiantEntity extends MonsterEntity { public GiantEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + } - dirtyMetadata.put(EntityDataTypes.SCALE, 6f); + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + setScale(6f); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PhantomEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PhantomEntity.java index cb4b7a8cf..18b7f6ae1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PhantomEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PhantomEntity.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.living.FlyingEntity; import org.geysermc.geyser.session.GeyserSession; @@ -46,7 +45,7 @@ public class PhantomEntity extends FlyingEntity { setBoundingBoxWidth(boundsScale * definition.width()); setBoundingBoxHeight(boundsScale * definition.height()); - dirtyMetadata.put(EntityDataTypes.SCALE, modelScale); + setScale(modelScale); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java index 9c43ab23a..f0f01272f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.entity.type.living.monster; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; @@ -49,7 +48,7 @@ public class PiglinEntity extends BasePiglinEntity { public void setBaby(BooleanEntityMetadata entityMetadata) { boolean isBaby = entityMetadata.getPrimitiveValue(); - dirtyMetadata.put(EntityDataTypes.SCALE, isBaby? .55f : 1f); + setScale(isBaby? .55f : 1f); setFlag(EntityFlag.BABY, isBaby); updateMountOffset(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java index efbb7753c..7eb950e21 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -43,7 +42,7 @@ public class ZoglinEntity extends MonsterEntity { public void setBaby(BooleanEntityMetadata entityMetadata) { boolean isBaby = entityMetadata.getPrimitiveValue(); if (isBaby != getFlag(EntityFlag.BABY)) { - dirtyMetadata.put(EntityDataTypes.SCALE, isBaby ? .55f : 1f); + setScale(isBaby ? .55f : 1f); setFlag(EntityFlag.BABY, isBaby); updatePassengerOffsets(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java index 11354fbf8..b8351089d 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -43,7 +42,7 @@ public class ZombieEntity extends MonsterEntity { public void setZombieBaby(BooleanEntityMetadata entityMetadata) { boolean isBaby = entityMetadata.getPrimitiveValue(); - dirtyMetadata.put(EntityDataTypes.SCALE, isBaby ? .55f : 1.0f); + setScale(isBaby ? .55f : 1.0f); setFlag(EntityFlag.BABY, isBaby); updateMountOffset(); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java b/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java index e6332bc41..88838d068 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.inventory; +import net.kyori.adventure.text.Component; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket; import lombok.Getter; @@ -72,10 +73,9 @@ public class AnvilContainer extends Container { String correctRename; newName = rename; - // TODO 1.20.5 fix properly - this name is apparently nullable?? - String originalName = MessageTranslator.convertMessage(ItemUtils.getCustomName(getInput().getComponents())); + Component originalName = ItemUtils.getCustomName(getInput().getComponents()); - String plainOriginalName = MessageTranslator.convertToPlainTextLenient(originalName, session.locale()); + String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale()); String plainNewName = MessageTranslator.convertToPlainText(rename); if (!plainOriginalName.equals(plainNewName)) { // Strip out formatting since Java Edition does not allow it @@ -85,7 +85,7 @@ public class AnvilContainer extends Container { session.sendDownstreamGamePacket(renameItemPacket); } else { // Restore formatting for item since we're not renaming - correctRename = MessageTranslator.convertMessageLenient(originalName); + correctRename = MessageTranslator.convertMessage(originalName, session.locale()); // Java Edition sends the original custom name when not renaming, // if there isn't a custom name an empty string is sent ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainOriginalName); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java index 1021a5fb9..d6a0d922b 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java @@ -118,8 +118,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater { // Changing the item in the input slot resets the name field on Bedrock, but // does not result in a FilterTextPacket - // TODO test - String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getComponents())); + String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getComponents()), session.locale()); ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(originalName); session.sendDownstreamGamePacket(renameItemPacket); @@ -388,7 +387,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater { } return enchantments; } - return Object2IntMaps.emptyMap(); + return new Object2IntOpenHashMap<>(); } private boolean isEnchantedBook(GeyserItemStack itemStack) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/Item.java b/core/src/main/java/org/geysermc/geyser/item/type/Item.java index 8fcb19ad5..cc49f3785 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/Item.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/Item.java @@ -146,6 +146,11 @@ public class Item { if (!enchantNbtList.isEmpty()) { builder.putList("ench", NbtType.COMPOUND, enchantNbtList); } + + Integer repairCost = components.get(DataComponentType.REPAIR_COST); + if (repairCost != null) { + builder.putInt("RepairCost", repairCost); + } } /** diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 3f1c902c7..884f05256 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -260,13 +260,17 @@ public class MessageTranslator { } /** - * Convert legacy format message to plain text + * Convert a Java message to plain text * * @param message Message to convert + * @param locale Locale to use for translation strings * @return The plain text of the message */ - public static String convertToPlainText(Component message) { - return PlainTextComponentSerializer.plainText().serialize(message); + public static String convertToPlainText(Component message, String locale) { + if (message == null) { + return ""; + } + return PlainTextComponentSerializer.plainText().serialize(RENDERER.render(message, locale)); } /** diff --git a/core/src/main/java/org/geysermc/geyser/util/MathUtils.java b/core/src/main/java/org/geysermc/geyser/util/MathUtils.java index 32f8b7ef5..08bed56f4 100644 --- a/core/src/main/java/org/geysermc/geyser/util/MathUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/MathUtils.java @@ -168,17 +168,6 @@ public class MathUtils { return value; } - /** - * Ensures the resulting object is a byte. Java Edition does not care whether a byte is encoded as an integer or not; - * it converts it into a byte anyway. - * - * @param value The value to convert - * @return The converted byte - */ - public static byte getNbtByte(Object value) { - return ((Number) value).byteValue(); - } - /** * Packs a chunk's X and Z coordinates into a single {@code long}. * diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6a88f0b0f..9e5a0a53f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ protocol-connection = "3.0.0.Beta1-20240411.165033-128" raknet = "1.0.0.CR3-20240416.144209-1" blockstateupdater="1.20.80-20240411.142413-1" mcauthlib = "d9d773e" -mcprotocollib = "400f1b4" # Revert from jitpack after release +mcprotocollib = "bc8526b" # Revert from jitpack after release adventure = "4.14.0" adventure-platform = "4.3.0" junit = "5.9.2"