Skip to content

Commit

Permalink
Fix banners on entity heads (GeyserMC#2110)
Browse files Browse the repository at this point in the history
On Bedrock, a banner must be placed in the chestplate slot in order to be visible. On Java Edition, banners are placed in the helmet slot. This commit fixes the issue by migrating banners to the chestplate spot if the chestplate spot is empty.

This commit also fixes pillager poses if they're not holding a crossbow, along with a couple other optimizations.
  • Loading branch information
Camotoy authored Apr 8, 2021
1 parent e762448 commit 7a7cf18
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,28 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
super.updateBedrockMetadata(entityMetadata, session);
}

public void updateEquipment(GeyserSession session) {
if (!valid)
return;
public void updateAllEquipment(GeyserSession session) {
if (!valid) return;

updateArmor(session);
updateMainHand(session);
updateOffHand(session);
}

public void updateArmor(GeyserSession session) {
if (!valid) return;

ItemData helmet = this.helmet;
ItemData chestplate = this.chestplate;
// If an entity has a banner on them, it will be in the helmet slot in Java but the chestplate spot in Bedrock
// But don't overwrite the chestplate if it isn't empty
if (chestplate.getId() == ItemData.AIR.getId() && helmet.getId() == ItemRegistry.BANNER.getBedrockId()) {
chestplate = this.helmet;
helmet = ItemData.AIR;
} else if (chestplate.getId() == ItemRegistry.BANNER.getBedrockId()) {
// Prevent chestplate banners from showing erroneously
chestplate = ItemData.AIR;
}

MobArmorEquipmentPacket armorEquipmentPacket = new MobArmorEquipmentPacket();
armorEquipmentPacket.setRuntimeEntityId(geyserId);
Expand All @@ -124,22 +143,32 @@ public void updateEquipment(GeyserSession session) {
armorEquipmentPacket.setLeggings(leggings);
armorEquipmentPacket.setBoots(boots);

session.sendUpstreamPacket(armorEquipmentPacket);
}

public void updateMainHand(GeyserSession session) {
if (!valid) return;

MobEquipmentPacket handPacket = new MobEquipmentPacket();
handPacket.setRuntimeEntityId(geyserId);
handPacket.setItem(hand);
handPacket.setHotbarSlot(-1);
handPacket.setInventorySlot(0);
handPacket.setContainerId(ContainerId.INVENTORY);

session.sendUpstreamPacket(handPacket);
}

public void updateOffHand(GeyserSession session) {
if (!valid) return;

MobEquipmentPacket offHandPacket = new MobEquipmentPacket();
offHandPacket.setRuntimeEntityId(geyserId);
offHandPacket.setItem(offHand);
offHandPacket.setHotbarSlot(-1);
offHandPacket.setInventorySlot(0);
offHandPacket.setContainerId(ContainerId.OFFHAND);

session.sendUpstreamPacket(armorEquipmentPacket);
session.sendUpstreamPacket(handPacket);
session.sendUpstreamPacket(offHandPacket);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,13 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
}

@Override
public void updateEquipment(GeyserSession session) {
// Check if the Piglin is holding Gold and set the ADMIRING flag accordingly
metadata.getFlags().setFlag(EntityFlag.ADMIRING, offHand.getId() == ItemRegistry.GOLD.getBedrockId());
super.updateBedrockMetadata(session);
public void updateOffHand(GeyserSession session) {
// Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates
boolean changed = metadata.getFlags().setFlag(EntityFlag.ADMIRING, offHand.getId() == ItemRegistry.GOLD.getBedrockId());
if (changed) {
super.updateBedrockMetadata(session);
}

super.updateEquipment(session);
super.updateOffHand(session);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@

package org.geysermc.connector.entity.living.monster.raid;

import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;

public class PillagerEntity extends AbstractIllagerEntity {

Expand All @@ -38,12 +38,30 @@ public PillagerEntity(long entityId, long geyserId, EntityType entityType, Vecto
}

@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 16) {
// Java Edition always has the Pillager entity as positioning the crossbow
metadata.getFlags().setFlag(EntityFlag.USING_ITEM, true);
metadata.getFlags().setFlag(EntityFlag.CHARGED, true);
public void updateMainHand(GeyserSession session) {
checkForCrossbow(session);

super.updateMainHand(session);
}

@Override
public void updateOffHand(GeyserSession session) {
checkForCrossbow(session);

super.updateOffHand(session);
}

/**
* Check for a crossbow in either the mainhand or offhand. If one exists, indicate that the pillager should be posing
*/
protected void checkForCrossbow(GeyserSession session) {
boolean hasCrossbow = this.hand.getId() == ItemRegistry.CROSSBOW.getBedrockId()
|| this.offHand.getId() == ItemRegistry.CROSSBOW.getBedrockId();
boolean usingItemChanged = metadata.getFlags().setFlag(EntityFlag.USING_ITEM, hasCrossbow);
boolean chargedChanged = metadata.getFlags().setFlag(EntityFlag.CHARGED, hasCrossbow);

if (usingItemChanged || chargedChanged) {
updateBedrockMetadata(session);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public void spawnEntity(GeyserSession session) {
valid = true;
session.sendUpstreamPacket(addPlayerPacket);

updateEquipment(session);
updateAllEquipment(session);
updateBedrockAttributes(session);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void spawnEntity(GeyserSession session) {
valid = true;
session.sendUpstreamPacket(addPlayerPacket);

updateEquipment(session);
updateAllEquipment(session);
updateBedrockAttributes(session);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
Expand Down Expand Up @@ -79,14 +79,22 @@ public class ItemRegistry {
* Bamboo item entry, used in PandaEntity.java
*/
public static ItemEntry BAMBOO;
/**
* Banner item entry, used in LivingEntity.java
*/
public static ItemEntry BANNER;
/**
* Boat item entries, used in BedrockInventoryTransactionTranslator.java
*/
public static IntList BOATS = new IntArrayList();
public static final IntSet BOATS = new IntArraySet();
/**
* Bucket item entries (excluding the milk bucket), used in BedrockInventoryTransactionTranslator.java
*/
public static IntList BUCKETS = new IntArrayList();
public static final IntSet BUCKETS = new IntArraySet();
/**
* Crossbow item entry, used in PillagerEntity.java
*/
public static ItemEntry CROSSBOW;
/**
* Empty item bucket, used in BedrockInventoryTransactionTranslator.java
*/
Expand Down Expand Up @@ -305,6 +313,9 @@ public static void init() {
case "minecraft:bamboo":
BAMBOO = itemEntry;
break;
case "minecraft:crossbow":
CROSSBOW = itemEntry;
break;
case "minecraft:egg":
EGG = itemEntry;
break;
Expand All @@ -320,6 +331,9 @@ public static void init() {
case "minecraft:wheat":
WHEAT = itemEntry;
break;
case "minecraft:white_banner": // As of 1.16.220, all banners share the same Bedrock ID and differ their colors through their damage value
BANNER = itemEntry;
break;
case "minecraft:writable_book":
WRITABLE_BOOK = itemEntry;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ public class JavaEntityEquipmentTranslator extends PacketTranslator<ServerEntity

@Override
public void translate(ServerEntityEquipmentPacket packet, GeyserSession session) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
Entity entity;
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
} else {
entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
}

if (entity == null)
Expand All @@ -54,30 +56,48 @@ public void translate(ServerEntityEquipmentPacket packet, GeyserSession session)
return;
}

boolean armorUpdated = false;
boolean mainHandUpdated = false;
boolean offHandUpdated = false;
LivingEntity livingEntity = (LivingEntity) entity;
for (Equipment equipment : packet.getEquipment()) {
ItemData item = ItemTranslator.translateToBedrock(session, equipment.getItem());
switch (equipment.getSlot()) {
case HELMET:
livingEntity.setHelmet(item);
armorUpdated = true;
break;
case CHESTPLATE:
livingEntity.setChestplate(item);
armorUpdated = true;
break;
case LEGGINGS:
livingEntity.setLeggings(item);
armorUpdated = true;
break;
case BOOTS:
livingEntity.setBoots(item);
armorUpdated = true;
break;
case MAIN_HAND:
livingEntity.setHand(item);
mainHandUpdated = true;
break;
case OFF_HAND:
livingEntity.setOffHand(item);
offHandUpdated = true;
break;
}
}
livingEntity.updateEquipment(session);

if (armorUpdated) {
livingEntity.updateArmor(session);
}
if (mainHandUpdated) {
livingEntity.updateMainHand(session);
}
if (offHandUpdated) {
livingEntity.updateOffHand(session);
}
}
}

0 comments on commit 7a7cf18

Please sign in to comment.