From c42984239f39875807c245dab106c646ef90bea1 Mon Sep 17 00:00:00 2001 From: Jannis Guhl <128991638+guhlutions@users.noreply.github.com> Date: Fri, 13 Nov 2026 22:21:11 +0100 Subject: [PATCH] Translate villager data and horse flags Combine Bedrock villager professions, biomes, and trade tiers into Java villager data. Send horse taming and pose flags without resolving owner IDs for entities that lack a Java owner metadata field. --- .../rewriter/EntityMetadataRewriter.java | 69 ++++++++++++++++++- .../data/bedrock/entity_data_types.json | 2 +- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/raphimc/viabedrock/protocol/rewriter/EntityMetadataRewriter.java b/src/main/java/net/raphimc/viabedrock/protocol/rewriter/EntityMetadataRewriter.java index 1f7d7982c..932d0a62f 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/rewriter/EntityMetadataRewriter.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/rewriter/EntityMetadataRewriter.java @@ -21,6 +21,7 @@ import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.BlockPosition; import com.viaversion.viaversion.api.minecraft.EulerAngle; +import com.viaversion.viaversion.api.minecraft.VillagerData; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes26_3; import com.viaversion.viaversion.api.minecraft.entitydata.EntityData; import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType; @@ -31,6 +32,7 @@ import net.raphimc.viabedrock.api.model.entity.LivingEntity; import net.raphimc.viabedrock.api.model.entity.properties.EntityProperty; import net.raphimc.viabedrock.api.model.entity.properties.EntityPropertyList; +import net.raphimc.viabedrock.api.util.MathUtil; import net.raphimc.viabedrock.protocol.BedrockProtocol; import net.raphimc.viabedrock.protocol.data.enums.bedrock.ActorDataIds; import net.raphimc.viabedrock.protocol.data.enums.bedrock.ActorFlags; @@ -186,6 +188,20 @@ public static boolean rewrite(final UserConnection user, final Entity entity, fi javaEntityData.add(new EntityData(entity.getJavaEntityDataIndex(EntityDataFields.LAYING_EGG), VersionedTypes.V26_3.entityDataTypes().booleanType, bedrockFlags.contains(ActorFlags.LAYING_EGG))); } + if (entity.javaType().isOrHasParent(EntityTypes26_3.ABSTRACT_HORSE)) { + byte horseFlags = 0; + if (bedrockFlags.contains(ActorFlags.TAMED)) { + horseFlags |= 0x02; + } + if (bedrockFlags.contains(ActorFlags.EATING)) { + horseFlags |= 0x10; + } + if (bedrockFlags.contains(ActorFlags.STANDING)) { + horseFlags |= 0x20; + } + javaEntityData.add(new EntityData(entity.getJavaEntityDataIndex(EntityDataFields.FLAGS), VersionedTypes.V26_3.entityDataTypes().byteType, horseFlags)); + } + if (entity.javaType().isOrHasParent(EntityTypes26_3.ABSTRACT_CHESTED_HORSE)) { javaEntityData.add(new EntityData(entity.getJavaEntityDataIndex(EntityDataFields.CHEST), VersionedTypes.V26_3.entityDataTypes().booleanType, bedrockFlags.contains(ActorFlags.CHESTED))); } @@ -254,7 +270,15 @@ public static boolean rewrite(final UserConnection user, final Entity entity, fi } } - case VARIANT -> { + case VARIANT, MARK_VARIANT, TRADE_TIER -> { + // Legacy Bedrock villagers use different profession IDs. + if (entity.type().equals("minecraft:villager_v2") || entity.type().equals("minecraft:zombie_villager_v2")) { + rewriteVillagerData(entity, javaEntityData); + break; + } + if (id != ActorDataIds.VARIANT) { + return false; + } final int variant = readNumber(entityData).intValue(); switch (entity.javaType()) { @@ -369,6 +393,9 @@ public static boolean rewrite(final UserConnection user, final Entity entity, fi } } case OWNER -> { + if (entity.javaType().isOrHasParent(EntityTypes26_3.ABSTRACT_HORSE)) { + break; // The Java client gets horse taming state from the horse flags. + } final long ownerId = readNumber(entityData).longValue(); if (ownerId == -1) { break; // No owner @@ -742,6 +769,46 @@ public static boolean rewrite(final UserConnection user, final Entity entity, fi return true; } + private static void rewriteVillagerData(final Entity entity, final List javaEntityData) { + // Java 26.3 uses the built-in villager type and profession registry IDs. + final int type = switch (getIntEntityData(entity, ActorDataIds.MARK_VARIANT)) { + case 1 -> 0; // DESERT + case 2 -> 1; // JUNGLE + case 3 -> 3; // SAVANNA + case 4 -> 4; // SNOW + case 5 -> 5; // SWAMP + case 6 -> 6; // TAIGA + default -> 2; // PLAINS + }; + final int profession = switch (getIntEntityData(entity, ActorDataIds.VARIANT)) { + case 1 -> 5; // FARMER + case 2 -> 6; // FISHERMAN + case 3 -> 12; // SHEPHERD + case 4 -> 7; // FLETCHER + case 5 -> 9; // LIBRARIAN + case 6 -> 3; // CARTOGRAPHER + case 7 -> 4; // CLERIC + case 8 -> 1; // ARMORER + case 9 -> 14; // WEAPONSMITH + case 10 -> 13; // TOOLSMITH + case 11 -> 2; // BUTCHER + case 12 -> 8; // LEATHERWORKER + case 13 -> 10; // MASON + case 14 -> 11; // NITWIT + default -> 0; // NONE + }; + final int level = MathUtil.clamp(getIntEntityData(entity, ActorDataIds.TRADE_TIER), 0, 4) + 1; + final int index = entity.getJavaEntityDataIndex(EntityDataFields.VILLAGER_DATA); + // Several Bedrock fields can change in the same packet. Send only the latest combined value. + javaEntityData.removeIf(data -> data.id() == index); + javaEntityData.add(new EntityData(index, VersionedTypes.V26_3.entityDataTypes().villagerDataType, new VillagerData(type, profession, level))); + } + + private static int getIntEntityData(final Entity entity, final ActorDataIds id) { + final EntityData data = entity.entityData().get(id); + return data != null ? data.value() : 0; + } + public static void rewriteProperties(final UserConnection user, final Entity entity, final EntityProperties properties, final List javaEntityData) { final EntityPropertyTracker epTracker = user.get(EntityPropertyTracker.class); final CompoundTag registries = BedrockProtocol.MAPPINGS.getJavaRegistries(); diff --git a/src/main/resources/assets/viabedrock/data/bedrock/entity_data_types.json b/src/main/resources/assets/viabedrock/data/bedrock/entity_data_types.json index b4beacfd4..8a389e233 100644 --- a/src/main/resources/assets/viabedrock/data/bedrock/entity_data_types.json +++ b/src/main/resources/assets/viabedrock/data/bedrock/entity_data_types.json @@ -100,7 +100,7 @@ "DATA_CHANGE_ON_PICKUP": null, "DATA_PICKUP_COUNT": null, "INTERACT_TEXT": null, - "TRADE_TIER": null, + "TRADE_TIER": "Int", "MAX_TRADE_TIER": null, "TRADE_EXPERIENCE": null, "SKIN_ID": null,