1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-27 18:59:17 +00:00

Actually fix item frame/painting entities

This commit is contained in:
Eclipse
2025-06-19 21:40:27 +00:00
parent 130496fdc4
commit 91c007daa5
5 changed files with 100 additions and 32 deletions

View File

@@ -37,6 +37,7 @@ import org.geysermc.geyser.entity.type.BoatEntity;
import org.geysermc.geyser.entity.type.ChestBoatEntity;
import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity;
import org.geysermc.geyser.entity.type.DisplayBaseEntity;
import org.geysermc.geyser.entity.type.HangingEntity;
import org.geysermc.geyser.entity.type.ThrowableEggEntity;
import org.geysermc.geyser.entity.type.EnderCrystalEntity;
import org.geysermc.geyser.entity.type.EnderEyeEntity;
@@ -523,17 +524,17 @@ public final class EntityDefinitions {
.addTranslator(MetadataTypes.BOOLEAN, (tridentEntity, entityMetadata) -> tridentEntity.setFlag(EntityFlag.ENCHANTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.build();
EntityDefinition<Entity> hangingEntityBase = EntityDefinition.inherited(null, entityBase)
.addTranslator(null) // Direction
EntityDefinition<HangingEntity> hangingEntityBase = EntityDefinition.<HangingEntity>inherited(null, entityBase)
.addTranslator(MetadataTypes.DIRECTION, HangingEntity::setDirectionMetadata)
.build();
PAINTING = EntityDefinition.<PaintingEntity>inherited(null, hangingEntityBase)
PAINTING = EntityDefinition.inherited(PaintingEntity::new, hangingEntityBase)
.type(EntityType.PAINTING)
.addTranslator(MetadataTypes.PAINTING_VARIANT, PaintingEntity::setPaintingType)
.build();
// Item frames are handled differently as they are blocks, not items, in Bedrock
ITEM_FRAME = EntityDefinition.<ItemFrameEntity>inherited(null, hangingEntityBase)
ITEM_FRAME = EntityDefinition.inherited(ItemFrameEntity::new, hangingEntityBase)
.type(EntityType.ITEM_FRAME)
.addTranslator(MetadataTypes.ITEM_STACK, ItemFrameEntity::setItemInFrame)
.addTranslator(MetadataTypes.INT, ItemFrameEntity::setItemRotation)

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import java.util.UUID;
public abstract class HangingEntity extends Entity {
public HangingEntity(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);
}
public void setDirectionMetadata(EntityMetadata<Direction, ?> direction) {
setDirection(direction.getValue());
}
public abstract void setDirection(Direction direction);
}

View File

@@ -51,7 +51,7 @@ import java.util.UUID;
/**
* Item frames are an entity in Java but a block entity in Bedrock.
*/
public class ItemFrameEntity extends Entity {
public class ItemFrameEntity extends HangingEntity {
/**
* Used for getting the Bedrock block position.
* Blocks deal with integers whereas entities deal with floats.
@@ -60,7 +60,7 @@ public class ItemFrameEntity extends Entity {
/**
* Specific block 'state' we are emulating in Bedrock.
*/
private final BlockDefinition blockDefinition;
private BlockDefinition blockDefinition;
/**
* Rotation of item in frame.
*/
@@ -75,22 +75,14 @@ public class ItemFrameEntity extends Entity {
@Getter
private ItemStack heldItem = null;
/**
* Determines if this entity needs updated on the client end/
* Determines if this entity needs to be updated on the client end.
*/
private boolean changed = true;
public ItemFrameEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw, Direction direction) {
public ItemFrameEntity(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);
NbtMapBuilder blockBuilder = NbtMap.builder()
.putString("name", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame");
NbtMapBuilder statesBuilder = NbtMap.builder()
.putInt("facing_direction", direction.ordinal())
.putByte("item_frame_map_bit", (byte) 0)
.putByte("item_frame_photo_bit", (byte) 0);
blockBuilder.put("states", statesBuilder.build());
blockDefinition = session.getBlockMappings().getItemFrame(blockBuilder.build());
blockDefinition = buildBlockDefinition(Direction.SOUTH); // Default to SOUTH direction, like on Java - entity metadata should correct this when necessary
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
session.getItemFrameCache().put(bedrockPosition, this);
@@ -109,6 +101,12 @@ public class ItemFrameEntity extends Entity {
valid = true;
}
@Override
public void setDirection(Direction direction) {
blockDefinition = buildBlockDefinition(direction);
changed = true;
}
public void setItemInFrame(EntityMetadata<ItemStack, ?> entityMetadata) {
if (entityMetadata.getValue() != null) {
this.heldItem = entityMetadata.getValue();
@@ -222,6 +220,18 @@ public class ItemFrameEntity extends Entity {
return InventoryUtils.isEmpty(heldItem) && session.getPlayerInventory().getItemInHand(hand).isEmpty() ? InteractionResult.PASS : InteractionResult.SUCCESS;
}
private BlockDefinition buildBlockDefinition(Direction direction) {
NbtMapBuilder blockBuilder = NbtMap.builder()
.putString("name", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame");
NbtMapBuilder statesBuilder = NbtMap.builder()
.putInt("facing_direction", direction.ordinal())
.putByte("item_frame_map_bit", (byte) 0)
.putByte("item_frame_photo_bit", (byte) 0);
blockBuilder.put("states", statesBuilder.build());
return session.getBlockMappings().getItemFrame(blockBuilder.build());
}
/**
* Finds the Java entity ID of an item frame from its Bedrock position.
* @param position position of item frame in Bedrock.

View File

@@ -38,13 +38,13 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import java.util.UUID;
public class PaintingEntity extends Entity {
public class PaintingEntity extends HangingEntity {
private static final double OFFSET = -0.46875;
private final Direction direction;
private int paintingId = -1; // Ideally this would be the default painting Java uses in their metadata, but seems to depend on the current paintings loaded in the registry
private Direction direction = Direction.SOUTH; // Default to SOUTH direction, like on Java - entity metadata should correct this when necessary
public PaintingEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw, Direction direction) {
public PaintingEntity(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);
this.direction = direction;
}
@Override
@@ -52,11 +52,31 @@ public class PaintingEntity extends Entity {
// Wait until we get the metadata needed
}
@Override
public void setDirection(Direction direction) {
this.direction = direction;
updatePainting();
}
public void setPaintingType(ObjectEntityMetadata<Holder<PaintingVariant>> entityMetadata) {
if (!entityMetadata.getValue().isId()) {
return;
}
PaintingType type = session.getRegistryCache().registry(JavaRegistries.PAINTING_VARIANT).byId(entityMetadata.getValue().id());
paintingId = entityMetadata.getValue().id();
updatePainting();
}
private void updatePainting() {
if (paintingId == -1) {
return;
} else if (valid) {
despawnEntity();
}
PaintingType type = session.getRegistryCache().registry(JavaRegistries.PAINTING_VARIANT).byId(paintingId);
if (type == null) {
return;
}
AddPaintingPacket addPaintingPacket = new AddPaintingPacket();
addPaintingPacket.setUniqueEntityId(geyserId);
addPaintingPacket.setRuntimeEntityId(geyserId);

View File

@@ -31,8 +31,6 @@ import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.FallingBlockEntity;
import org.geysermc.geyser.entity.type.FishingHookEntity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.PaintingEntity;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
@@ -42,7 +40,6 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EnvironmentUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.FallingBlockData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.ProjectileData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.WardenData;
@@ -102,13 +99,6 @@ public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEnti
if (packet.getType() == EntityType.FALLING_BLOCK) {
entity = new FallingBlockEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(),
position, motion, yaw, pitch, headYaw, ((FallingBlockData) packet.getData()).getId());
} else if (packet.getType() == EntityType.ITEM_FRAME || packet.getType() == EntityType.GLOW_ITEM_FRAME) {
// Item frames need the hanging direction
entity = new ItemFrameEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(),
definition, position, motion, yaw, pitch, headYaw, (Direction) packet.getData());
} else if (packet.getType() == EntityType.PAINTING) {
entity = new PaintingEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(),
definition, position, motion, yaw, pitch, headYaw, (Direction) packet.getData());
} else if (packet.getType() == EntityType.FISHING_BOBBER) {
// Fishing bobbers need the owner for the line
int ownerEntityId = ((ProjectileData) packet.getData()).getOwnerId();