Skip to content

Entity refactor pt2#6108

Open
onebeastchris wants to merge 16 commits intomasterfrom
entity-refactor-pt2
Open

Entity refactor pt2#6108
onebeastchris wants to merge 16 commits intomasterfrom
entity-refactor-pt2

Conversation

@onebeastchris
Copy link
Member

@onebeastchris onebeastchris commented Jan 13, 2026

This refactor cleans up handling of Bedrock's entity offset. Specifically, Geyser currently stores the Bedrock position of an entity, which requires special handling for entities that have an offset. This PR splits up both positions cleanly to always store the unmodified Java position, and the Bedrock position which may contain an offset.

@onebeastchris onebeastchris linked an issue Jan 13, 2026 that may be closed by this pull request
# Conflicts:
#	core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java
#	core/src/main/java/org/geysermc/geyser/entity/type/MinecartEntity.java
#	core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java
@onebeastchris onebeastchris linked an issue Feb 27, 2026 that may be closed by this pull request
# Conflicts:
#	core/src/main/java/org/geysermc/geyser/entity/type/Entity.java
#	core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java
#	core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java
#	core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java
@onebeastchris onebeastchris marked this pull request as ready for review March 1, 2026 21:25
Copilot AI review requested due to automatic review settings March 1, 2026 21:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors entity coordinate handling to consistently store Java (unmodified) positions internally while deriving Bedrock (offset-applied) positions via a dedicated accessor, reducing ad-hoc offset math across translators and entity logic.

Changes:

  • Introduces Entity#bedrockPosition() (offset-applied) and updates packet sending/collision logic to use position() (Java) vs bedrockPosition() (Bedrock) appropriately.
  • Updates many translators and caches to stop manually adding/subtracting entity offsets and instead use the new position accessors.
  • Updates vehicle and display-entity movement to compute deltas/packets using Bedrock positions while storing Java positions internally.

Reviewed changes

Copilot reviewed 57 out of 58 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java Uses Bedrock position accessor for world-space checks; adjusts placement math.
core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java Sends empty chunks based on Java position accessor.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java Targets vibration particles using Bedrock entity position.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelChunkWithLightTranslator.java Updates chunk position tracking based on Java player position.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java Uses Bedrock position/rotation accessors for move/sound packets.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetHealthTranslator.java Respawn packet now uses Bedrock player position.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java Removes manual player offset math; uses Java vs Bedrock accessors.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerLookAtTranslator.java Computes look-at deltas from Java positions; clarifies origin handling.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTakeItemEntityTranslator.java XP pickup sound uses Bedrock collected-entity position.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSoundEntityTranslator.java Entity-attached sounds use Bedrock entity position.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java Teleport confirmation now uses Java player position accessor.
core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java Particles/sounds/motion updated to use Java vs Bedrock positions consistently.
core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java Vehicle input path updated to convert Bedrock packet pos -> Java pos via entity offset.
core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java Player movement validation and state updates use Bedrock vs Java positions explicitly.
core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java Interaction range checks use Java position + eye height; entity click math updated.
core/src/main/java/org/geysermc/geyser/translator/level/event/PlaySoundEventTranslator.java Non-relative sounds now play at Bedrock player position; simplifies vec conversion.
core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java Uses Java player position for interaction-distance checks.
core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java Inventory open/close packets use Bedrock player block position.
core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java World border checks and corrections now use Java player position accessor.
core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java Skull culling uses Java player movement comparisons.
core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java Bossbar helper entity positioned relative to Java player position.
core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java Block-break interaction range uses Java position + eye height.
core/src/main/java/org/geysermc/geyser/session/GeyserSession.java Updates session docs and uses Bedrock player position where required by Bedrock packets.
core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java Formatting/indentation adjustment in NBT collision box conversion.
core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java Recalculation/movement packets use Bedrock position/rotation accessors.
core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java Interaction-distance check uses Java player position accessor.
core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java Vehicle delta movement compares old/new Bedrock positions while storing Java pos.
core/src/main/java/org/geysermc/geyser/entity/vehicle/BoatVehicleComponent.java Boat delta movement updated to new Bedrock position derivation model.
core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java Stops storing offset-applied position; adds Bedrock->Java setter for auth input.
core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java Removes overridden position() offset adjustment (position is now Java).
core/src/main/java/org/geysermc/geyser/entity/type/player/AvatarEntity.java Uses Bedrock position/rotation accessors for spawn/move; stores Java pos.
core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WardenEntity.java Heartbeat sound plays at Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java Stare sound plays at Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonPartEntity.java Movement packet uses Bedrock position/rotation accessors.
core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonEntity.java Updates part movement and effects to use Java vs Bedrock positions correctly.
core/src/main/java/org/geysermc/geyser/entity/type/living/monster/CreeperEntity.java Ignite sound uses Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/living/monster/CreakingEntity.java Beam particle tag uses Bedrock position values.
core/src/main/java/org/geysermc/geyser/entity/type/living/merchant/VillagerEntity.java Relative movement uses Java position math; packet pos includes offset.
core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/CowEntity.java Milk sound uses Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java Dig particles computed from Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java Interaction distance checks use Java player position.
core/src/main/java/org/geysermc/geyser/entity/type/living/animal/GoatEntity.java Milk sound uses Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java Renames rotation accessor to bedrockRotation().
core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java Converts y-offset handling to entity offset field; fixes onGround argument usage.
core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java Visibility logic updated to compare against Java positions.
core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java Delta packets now emit Bedrock positions after setting Java position; rotation sync updated.
core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java Moves line-centering to offset field; updates secondary armor-stand positioning.
core/src/main/java/org/geysermc/geyser/entity/type/TNTEntity.java Removes special moveAbsoluteRaw offset override in favor of offset accessor.
core/src/main/java/org/geysermc/geyser/entity/type/MinecartEntity.java Lerp and delta packets emit Bedrock position via accessor while storing Java pos.
core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java Lerp delta packets now set X/Y/Z from bedrockPosition().
core/src/main/java/org/geysermc/geyser/entity/type/LightningEntity.java Thunder/impact sounds play at Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java Frame cache position uses Java position() (toInt) consistently.
core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java Spawn/move uses bedrockPosition() and offset field for water adjustments.
core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java Splash sound plays at Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/EvokerFangsEntity.java Attack sound plays at Bedrock position.
core/src/main/java/org/geysermc/geyser/entity/type/Entity.java Adds offset state, bedrockPosition(), renames rotation accessor, and standardizes Java position storage.
core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java Boat movement now stores Java pos and emits Bedrock pos via accessor; removes adjustment helper.
core/src/main/java/org/geysermc/geyser/entity/spawn/EntitySpawnContext.java Stores Java spawn position and a separate initial offset value.
Comments suppressed due to low confidence (3)

core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java:242

  • flatPlayerPosition.down(3) changes the fallback placement from Y-4 (previous sub(0, 4, 0)) to Y-3. If the intent is to preserve existing behavior, this should be down(4) (or equivalent) so the fake block search checks the same block below the player as before.
    core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java:337
  • When the vehicle would pass into the world border, the code moves the vehicle back via moveAbsoluteRaw(...) but then continues on to vehicle.setPosition(vehiclePosition) and sends a ServerboundMoveVehiclePacket, effectively re-applying the out-of-bounds position. This should likely return immediately after reverting (or otherwise skip updating/sending movement) to prevent border bypass/desync.
    core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java:175
  • TextDisplayEntity now applies line centering via setOffset(calculateLineOffset()), which affects bedrockPosition() for this entity, but the secondary armor stand (custom-name renderer) is still moved/spawned using the un-offset Java position (only adjusted by LINE_HEIGHT_OFFSET). This will desync the relative placement between the text display and the secondary nametag when the line count changes. The secondary entity should incorporate the same centering offset (e.g., adjust its position by the offset or apply the offset to the second entity) so both stay aligned.
    public void moveAbsoluteRaw(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
        if (secondEntity != null) {
            secondEntity.moveAbsoluteRaw(position.down(LINE_HEIGHT_OFFSET), yaw, pitch, headYaw, isOnGround, teleported);
        }
        super.moveAbsoluteRaw(position, yaw, pitch, headYaw, isOnGround, teleported);
    }

    public void setText(EntityMetadata<Component, ?> entityMetadata) {
        this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue(), session.locale()));
        int oldLineCount = this.lineCount;
        this.lineCount = calculateLineCount(entityMetadata.getValue());

        // If the line count changed, update the position to account for the new offset
        if (this.lineCount != oldLineCount) {
            setOffset(calculateLineOffset());
            moveAbsoluteRaw(position, yaw, pitch, headYaw, onGround, false);
        }
    }

    private int calculateLineCount(@Nullable Component text) {
        if (text == null) {
            return 0;
        }
        return PlainTextComponentSerializer.plainText().serialize(text).split("\n").length;
    }

    @Override
    public void updateBedrockMetadata() {
        // Bundle metadata updates to ensure they aren't ignored
        if (secondEntity != null) {
            if (!secondEntity.valid) { // Spawn the entity once
                secondEntity.spawnEntity();
            } else {
                secondEntity.updateBedrockMetadata();
            }
        }
        super.updateBedrockMetadata();
    }

    public void updateNameTag() {
        // Text displays are special: customNameVisible must be set for the custom name to ever show
        if (this.nametag.isBlank() || isInvisible || !customNameVisible) {
            if (secondEntity != null) {
                secondEntity.despawnEntity();
                secondEntity = null;
            }
            return;
        }

        if (this.secondEntity == null) {
            secondEntity = new ArmorStandEntity(EntitySpawnContext.inherited(session, EntityDefinitions.ARMOR_STAND, this, position.down(LINE_HEIGHT_OFFSET)));
        }
        secondEntity.getDirtyMetadata().put(EntityDataTypes.NAME, this.nametag);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings March 2, 2026 14:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 57 out of 58 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (4)

core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java:337

  • isPassingIntoBorderBoundaries(vehiclePosition, false) returns true when the move would cross the border, but the code still continues and sets vehicle.setPosition(vehiclePosition) (and sends the move packet) using the out-of-bounds position. This effectively undoes the border rollback. Return immediately after reverting, or update vehiclePosition to the reverted position before continuing.
    core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java:242
  • flatPlayerPosition.sub(0, 4, 0) was replaced with flatPlayerPosition.down(3), which changes the fallback Y offset by 1 block. If the intent was a direct API swap, this should be down(4) to preserve the previous placement location for the virtual inventory block.
    core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java:220
  • For Bedrock packets, this should likely use the entity's Bedrock position (offset applied). Most other sound/particle packets in this translator were updated to entity.bedrockPosition(), but this branch uses entity.position() which can be incorrect for entities with a non-zero offset.
    core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java:121
  • AddEntityPacket is sent to the Bedrock client, but this uses session.getPlayerEntity().position() (Java position). To keep Bedrock-side coordinates consistent with the rest of this refactor, use the player's bedrockPosition() (and then apply the -10 Y offset) so the bossbar entity spawns at the correct Bedrock Y when the player offset is non-zero.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +332 to 336
vehicle.moveAbsoluteRaw(position, vehicle.getYaw(), vehicle.getPitch(), vehicle.getHeadYaw(),
vehicle.isOnGround(), true);
}

vehicle.setPosition(vehiclePosition);
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this block, after calculating vehicleRotation, the code builds a ServerboundMoveVehiclePacket using vehiclePosition.getX() as the third constructor argument (immediately after vehicle.setPosition(vehiclePosition)). Given other call sites use (javaPos, yaw, pitch, onGround), this appears to send X-position as pitch. Use the pitch component from vehicleRotation instead.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/rotate command has errors on Bedrock Edition players. Running into the world border can glitch you down blocks

2 participants