From bd16b3be34bae595531bbd775f7ad9e7175d59f9 Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Tue, 24 Jun 2025 01:46:19 +0200 Subject: [PATCH 01/10] Start on implementing happy ghast flying --- .../geyser/entity/type/LivingEntity.java | 11 ++- .../type/living/animal/HappyGhastEntity.java | 61 ++++++++++++++-- .../geyser/entity/vehicle/ClientVehicle.java | 3 + .../vehicle/HappyGhastVehicleComponent.java | 69 +++++++++++++++++++ .../org/geysermc/geyser/util/EntityUtils.java | 33 +++++++-- 5 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java 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 363ce6008..4c5cb9f8a 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 @@ -41,7 +41,9 @@ import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; +import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; import org.geysermc.geyser.entity.vehicle.ClientVehicle; +import org.geysermc.geyser.entity.vehicle.HappyGhastVehicleComponent; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.registry.type.ItemMapping; @@ -510,7 +512,14 @@ public class LivingEntity extends Entity { } } case ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE)); - case FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED)); + case FLYING_SPEED -> { + AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED); + newAttributes.add(attributeData); + if (this instanceof HappyGhastEntity happyGhast && + happyGhast.getVehicleComponent() instanceof HappyGhastVehicleComponent vehicleComponent) { + vehicleComponent.setFlyingSpeed(attributeData.getValue()); + } + } case FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); case KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE)); case JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH)); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 38d6f1eb4..44ad0b7e8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -27,9 +27,15 @@ package org.geysermc.geyser.entity.type.living.animal; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; +import org.geysermc.geyser.entity.vehicle.HappyGhastVehicleComponent; +import org.geysermc.geyser.entity.vehicle.VehicleComponent; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; @@ -43,20 +49,30 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import java.util.UUID; -public class HappyGhastEntity extends AnimalEntity { +public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { + + private final HappyGhastVehicleComponent vehicleComponent = new HappyGhastVehicleComponent(this, 0.0f); + private boolean staysStill; + private float speed; + public HappyGhastEntity(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); + } + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + // BDS 1.21.90 setFlag(EntityFlag.CAN_FLY, true); - setFlag(EntityFlag.TAMED, true); setFlag(EntityFlag.CAN_WALK, true); + setFlag(EntityFlag.TAMED, true); + setFlag(EntityFlag.BODY_ROTATION_ALWAYS_FOLLOWS_HEAD, true); + setFlag(EntityFlag.COLLIDABLE, true); setFlag(EntityFlag.WASD_AIR_CONTROLLED, true); setFlag(EntityFlag.DOES_SERVER_AUTH_ONLY_DISMOUNT, true); - // TODO: verify which flags are necessary - - setAirSupply(100); + propertyManager.add("minecraft:can_move", true); } @Override @@ -71,6 +87,7 @@ public class HappyGhastEntity extends AnimalEntity { } public void setStaysStill(BooleanEntityMetadata entityMetadata) { + staysStill = entityMetadata.getPrimitiveValue(); propertyManager.add("minecraft:can_move", !entityMetadata.getPrimitiveValue()); updateBedrockEntityProperties(); } @@ -124,4 +141,38 @@ public class HappyGhastEntity extends AnimalEntity { } } } + + @Override + public VehicleComponent getVehicleComponent() { + return vehicleComponent; + } + + @Override + public Vector2f getAdjustedInput(Vector2f input) { + // not used; calculations look a bit different for the happy ghast + return Vector2f.ZERO; + } + + @Override + public float getVehicleSpeed() { + return speed; // TODO this doesnt seem right? + } + + @Override + public boolean isClientControlled() { + // TODO proper check, just lazy + if (body == null) { + return false; + } + // TODO must have ai check + if (staysStill) { + return false; + } + + return getFirstPassenger() instanceof SessionPlayerEntity; + } + + private Entity getFirstPassenger() { + return passengers.isEmpty() ? null : passengers.get(0); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index e6aaf1daa..00ea802df 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -30,10 +30,13 @@ import org.cloudburstmc.math.vector.Vector2f; public interface ClientVehicle { VehicleComponent getVehicleComponent(); + // LivingEntity#getRiddenInput Vector2f getAdjustedInput(Vector2f input); + // MojMap LivingEntity#getRiddenSpeed float getVehicleSpeed(); + // MojMap Mob#getControllingPassenger boolean isClientControlled(); default boolean canWalkOnLava() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java new file mode 100644 index 000000000..92f219d44 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java @@ -0,0 +1,69 @@ +/* + * 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.vehicle; + +import lombok.Setter; +import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.math.vector.Vector3f; +import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; + +public class HappyGhastVehicleComponent extends VehicleComponent { + + @Setter + private float flyingSpeed; + + public HappyGhastVehicleComponent(HappyGhastEntity vehicle, float stepHeight) { + super(vehicle, stepHeight); + } + + protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { + Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); + input = input.mul(0.98f); // ? + + float x = input.getX(); + float y = 0.0f; + float z = 0.0f; + + float playerZ = input.getY(); + if (playerZ != 0.0F) { + float i = Mth.cos(player.getXRot() * (float) (Math.PI / 180.0)); + float j = -Mth.sin(player.getXRot() * (float) (Math.PI / 180.0)); + if (playerZ < 0.0F) { + i *= -0.5F; + j *= -0.5F; + } + + y = j; + z = i; + } + + if (session.isJumping()) { + y += 0.5F; + } + + return Vector3f.from((double) x, (double) y, (double)z).mul(3.9F * flyingSpeed); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java index 388162a49..c6797ab79 100644 --- a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java @@ -39,6 +39,7 @@ import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.TextDisplayEntity; import org.geysermc.geyser.entity.type.living.ArmorStandEntity; import org.geysermc.geyser.entity.type.living.animal.AnimalEntity; +import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; @@ -221,6 +222,13 @@ public final class EntityUtils { } } } + case HAPPY_GHAST -> { + // TODO seat index matters here, likely + // 0.0, 5.02001, 1.7 BDS + xOffset = 0; + yOffset = 3.4f; + zOffset = 1.7f; + } } if (mount instanceof ChestBoatEntity) { xOffset = 0.15F; @@ -268,17 +276,30 @@ public final class EntityUtils { } public static void updateRiderRotationLock(Entity passenger, Entity mount, boolean isRiding) { - if (isRiding && mount instanceof BoatEntity) { - // Head rotation is locked while riding in a boat - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, true); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 90f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, true); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, -90f); + if (isRiding) { + if (mount instanceof BoatEntity) { + // Head rotation is locked while riding in a boat + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, true); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 90f); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, true); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, -90f); + } else if (mount instanceof HappyGhastEntity) { + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 181f); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, 8f); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, 6f); + + passenger.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0); + } } else { passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 0f); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, 0f); + // TODO what are defaults here??? + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, 8f); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, 6f); + passenger.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0); } } From 62ca90cf58b7115e5b37d9859e5b4a6be74611d6 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Tue, 24 Jun 2025 08:00:01 +0000 Subject: [PATCH 02/10] Let it build --- .../entity/vehicle/HappyGhastVehicleComponent.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java index 92f219d44..82fadb7f1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java @@ -44,13 +44,13 @@ public class HappyGhastVehicleComponent extends VehicleComponent Date: Tue, 24 Jun 2025 08:42:13 +0000 Subject: [PATCH 03/10] Fix happy ghast mount offsets --- .../geyser/entity/type/DisplayBaseEntity.java | 2 +- .../org/geysermc/geyser/entity/type/Entity.java | 4 ++-- .../type/living/animal/HappyGhastEntity.java | 3 +++ .../java/entity/JavaSetPassengersTranslator.java | 14 ++++++++++---- .../java/org/geysermc/geyser/util/EntityUtils.java | 12 ++++++------ 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/DisplayBaseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/DisplayBaseEntity.java index 16587d125..414ed0541 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/DisplayBaseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/DisplayBaseEntity.java @@ -66,7 +66,7 @@ public class DisplayBaseEntity extends Entity { this.setRiderSeatPosition(this.baseTranslation); this.moveRelative(this.baseTranslation.getX(), this.baseTranslation.getY(), this.baseTranslation.getZ(), yaw, pitch, headYaw, false); } else { - EntityUtils.updateMountOffset(this, this.vehicle, true, true, false); + EntityUtils.updateMountOffset(this, this.vehicle, true, true, 0, 1); this.updateBedrockMetadata(); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index fa1f05417..b03dea3ed 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -621,7 +621,7 @@ public class Entity implements GeyserEntity { Entity passenger = passengers.get(i); if (passenger != null) { boolean rider = i == 0; - EntityUtils.updateMountOffset(passenger, this, rider, true, passengers.size() > 1); + EntityUtils.updateMountOffset(passenger, this, rider, true, i, passengers.size()); passenger.updateBedrockMetadata(); } } @@ -633,7 +633,7 @@ public class Entity implements GeyserEntity { protected void updateMountOffset() { if (vehicle != null) { boolean rider = vehicle.getPassengers().get(0) == this; - EntityUtils.updateMountOffset(this, vehicle, rider, true, vehicle.getPassengers().size() > 1); + EntityUtils.updateMountOffset(this, vehicle, rider, true, vehicle.getPassengers().indexOf(this), vehicle.getPassengers().size()); updateBedrockMetadata(); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 44ad0b7e8..99eac9a8b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -51,6 +51,9 @@ import java.util.UUID; public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { + public static final float[] X_OFFSETS = {0.0F, -1.7F, 0.0F, 1.7F}; + public static final float[] Z_OFFSETS = {1.7F, 0.0F, -1.7F, 0.0F}; + private final HappyGhastVehicleComponent vehicleComponent = new HappyGhastVehicleComponent(this, 0.0f); private boolean staysStill; private float speed; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java index 1ca9e268a..3bc25c986 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.translator.protocol.java.entity; +import lombok.NonNull; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; @@ -50,7 +51,10 @@ public class JavaSetPassengersTranslator extends PacketTranslator newPassengers = new ArrayList<>(); - for (int passengerId : packet.getPassengerIds()) { + int @NonNull [] passengerIds = packet.getPassengerIds(); + for (int i = 0; i < passengerIds.length; i++) { + int passengerId = passengerIds[i]; + Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId); if (passenger == session.getPlayerEntity()) { session.getPlayerEntity().setVehicle(entity); @@ -76,13 +80,15 @@ public class JavaSetPassengersTranslator extends PacketTranslator 1)); + EntityUtils.updateMountOffset(passenger, entity, rider, true, i, packet.getPassengerIds().length); // Force an update to the passenger metadata passenger.updateBedrockMetadata(); } // Handle passengers that were removed - for (Entity passenger : entity.getPassengers()) { + List passengers = entity.getPassengers(); + for (int i = 0; i < passengers.size(); i++) { + Entity passenger = passengers.get(i); if (passenger == null) { continue; } @@ -93,7 +99,7 @@ public class JavaSetPassengersTranslator extends PacketTranslator 1)); + EntityUtils.updateMountOffset(passenger, entity, false, false, i, packet.getPassengerIds().length); // Force an update to the passenger metadata passenger.updateBedrockMetadata(); diff --git a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java index c6797ab79..9cdcbed9f 100644 --- a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java @@ -167,7 +167,7 @@ public final class EntityUtils { /** * Adjust an entity's height if they have mounted/dismounted an entity. */ - public static void updateMountOffset(Entity passenger, Entity mount, boolean rider, boolean riding, boolean moreThanOneEntity) { + public static void updateMountOffset(Entity passenger, Entity mount, boolean rider, boolean riding, int index, int passengers) { passenger.setFlag(EntityFlag.RIDING, riding); if (riding) { // Without the Y offset, Bedrock players will find themselves in the floor when mounting @@ -180,7 +180,7 @@ public final class EntityUtils { switch (mount.getDefinition().entityType()) { case CAMEL -> { zOffset = 0.5f; - if (moreThanOneEntity) { + if (passengers > 1) { if (!rider) { zOffset = -0.7f; } @@ -223,18 +223,18 @@ public final class EntityUtils { } } case HAPPY_GHAST -> { - // TODO seat index matters here, likely // 0.0, 5.02001, 1.7 BDS - xOffset = 0; + int seatingIndex = Math.min(index, 4); + xOffset = HappyGhastEntity.X_OFFSETS[seatingIndex]; yOffset = 3.4f; - zOffset = 1.7f; + zOffset = HappyGhastEntity.Z_OFFSETS[seatingIndex]; } } if (mount instanceof ChestBoatEntity) { xOffset = 0.15F; } else if (mount instanceof BoatEntity) { // Without the X offset, more than one entity on a boat is stacked on top of each other - if (moreThanOneEntity) { + if (passengers > 1) { xOffset = rider ? 0.2f : -0.6f; if (passenger instanceof AnimalEntity) { xOffset += 0.2f; From 135eb44a7f9e28b15f90e8bcf58a41e47ede35fe Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Tue, 24 Jun 2025 19:33:20 +0200 Subject: [PATCH 04/10] A few comments / start implementing happy ghast movements --- .../geyser/entity/type/LivingEntity.java | 5 +++ .../vehicle/HappyGhastVehicleComponent.java | 35 ++++++++++++++++++ .../entity/vehicle/VehicleComponent.java | 37 +++++++++++++------ 3 files changed, 65 insertions(+), 12 deletions(-) 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 4c5cb9f8a..1a6da0d91 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 @@ -528,6 +528,11 @@ public class LivingEntity extends Entity { setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute)); updateBedrockMetadata(); } + case WATER_MOVEMENT_EFFICIENCY -> { + if (this instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().setWaterMovementEfficiency(AttributeUtils.calculateValue(javaAttribute)); + } + } } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java index 82fadb7f1..7d361d6fa 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java @@ -25,10 +25,13 @@ package org.geysermc.geyser.entity.vehicle; +import it.unimi.dsi.fastutil.objects.ObjectDoublePair; import lombok.Setter; import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; +import org.geysermc.geyser.level.block.Fluid; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; public class HappyGhastVehicleComponent extends VehicleComponent { @@ -37,6 +40,35 @@ public class HappyGhastVehicleComponent extends VehicleComponent fluidHeight = updateFluidMovement(ctx); + + // HappyGhast#travel + float speed = flyingSpeed * 5.0f / 3.0f; + + // TODO implement LivingEntity#travelFlying cases +// switch (fluidHeight.left()) { +// default -> { +// throw new GoodLuckImplementingThisException(); +// } +// } } protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { @@ -66,4 +98,7 @@ public class HappyGhastVehicleComponent extends VehicleComponent { private static final float MIN_VELOCITY = 0.003f; protected final T vehicle; + @Getter protected final BoundingBox boundingBox; protected float stepHeight; + @Getter @Setter protected float moveSpeed; protected double gravity; + @Getter @Setter + protected double waterMovementEfficiency; protected int effectLevitation; protected boolean effectSlowFalling; protected boolean effectWeaving; @@ -78,6 +84,7 @@ public class VehicleComponent { this.stepHeight = stepHeight; this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef(); this.gravity = AttributeType.Builtin.GRAVITY.getDef(); + this.waterMovementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef(); double width = vehicle.getBoundingBoxWidth(); double height = vehicle.getBoundingBoxHeight(); @@ -117,10 +124,6 @@ public class VehicleComponent { boundingBox.translate(vec); } - public BoundingBox getBoundingBox() { - return this.boundingBox; - } - public void setEffect(Effect effect, int effectAmplifier) { switch (effect) { case LEVITATION -> effectLevitation = effectAmplifier + 1; @@ -137,14 +140,6 @@ public class VehicleComponent { } } - public void setMoveSpeed(float moveSpeed) { - this.moveSpeed = moveSpeed; - } - - public float getMoveSpeed() { - return moveSpeed; - } - public void setStepHeight(float stepHeight) { this.stepHeight = MathUtils.clamp(stepHeight, 1.0f, 10.0f); } @@ -208,6 +203,7 @@ public class VehicleComponent { BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ()); + // Mojmap Entity#updateInWaterStateAndDoFluidPushing double waterHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.WATER, 0.014, min.getY()); double lavaHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY()); @@ -373,6 +369,7 @@ public class VehicleComponent { return BlockUtils.getCollision(adjacentBlockId) instanceof SolidCollision; } + // Mojmap: LivingEntity#travelInFluid protected void waterMovement(VehicleContext ctx) { double gravity = getGravity(); float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier @@ -380,6 +377,21 @@ public class VehicleComponent { boolean falling = vehicle.getMotion().getY() <= 0; // NOT IMPLEMENTED: depth strider and dolphins grace +// float g = 0.02f; +// float waterMovementEfficiencyMultiplier = (float) waterMovementEfficiency; +// if (!vehicle.isOnGround()) { +// // TODO test +// waterMovementEfficiencyMultiplier *= 0.5f; +// } +// +// if (waterMovementEfficiencyMultiplier > 0.0F) { +// drag += (0.54600006F - drag) * waterMovementEfficiencyMultiplier; +// g += (this.getSpeed() - g) * waterMovementEfficiencyMultiplier; +// } + +// if (this.hasEffect(MobEffects.DOLPHINS_GRACE)) { +// drag = 0.96F; +// } boolean horizontalCollision = travel(ctx, 0.02f); @@ -570,6 +582,7 @@ public class VehicleComponent { * * @return true if there was a horizontal collision */ + // Mojmap: LivingEntity#moveRelative protected boolean travel(VehicleContext ctx, float speed) { Vector3f motion = vehicle.getMotion(); From 47c8ad1c1d6b2283d5415f9430200963faffe851 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Wed, 25 Jun 2025 07:01:36 -0400 Subject: [PATCH 05/10] Progress on happy ghast movement --- .../type/living/animal/HappyGhastEntity.java | 27 +++++-- .../type/living/animal/StriderEntity.java | 4 +- .../type/living/animal/farm/PigEntity.java | 4 +- .../type/living/animal/horse/CamelEntity.java | 5 +- .../entity/vehicle/CamelVehicleComponent.java | 8 +- .../geyser/entity/vehicle/ClientVehicle.java | 3 +- .../vehicle/HappyGhastVehicleComponent.java | 80 +++++++++---------- .../entity/vehicle/VehicleComponent.java | 80 +++++++++---------- 8 files changed, 115 insertions(+), 96 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 99eac9a8b..5c2d9d474 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.math.TrigMath; import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; @@ -56,7 +57,6 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { private final HappyGhastVehicleComponent vehicleComponent = new HappyGhastVehicleComponent(this, 0.0f); private boolean staysStill; - private float speed; public HappyGhastEntity(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); @@ -151,14 +151,31 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { } @Override - public Vector2f getAdjustedInput(Vector2f input) { - // not used; calculations look a bit different for the happy ghast - return Vector2f.ZERO; + public Vector3f getRiddenInput(Vector2f input) { + float x = input.getX(); + float y = 0.0f; + float z = 0.0f; + + if (input.getY() != 0.0f) { + float pitch = session.getPlayerEntity().getPitch(); + z = TrigMath.cos(pitch * TrigMath.DEG_TO_RAD); + y = -TrigMath.sin(pitch * TrigMath.DEG_TO_RAD); + if (input.getY() < 0.0f) { + z *= -0.5f; + y *= -0.5f; + } + } + + if (session.getInputCache().wasJumping()) { + y += 0.5f; + } + + return Vector3f.from(x, y, z).mul(3.9f * vehicleComponent.getFlyingSpeed()); } @Override public float getVehicleSpeed() { - return speed; // TODO this doesnt seem right? + return 1; // TODO this doesnt seem right? } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index 236b22c51..a2febc068 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -169,8 +169,8 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic } @Override - public Vector2f getAdjustedInput(Vector2f input) { - return Vector2f.UNIT_Y; + public Vector3f getRiddenInput(Vector2f input) { + return Vector3f.UNIT_Z; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java index d6a8ece7c..60b549a60 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java @@ -128,8 +128,8 @@ public class PigEntity extends TemperatureVariantAnimal implements Tickable, Cli } @Override - public Vector2f getAdjustedInput(Vector2f input) { - return Vector2f.UNIT_Y; + public Vector3f getRiddenInput(Vector2f input) { + return Vector3f.UNIT_Z; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index 6239122f4..db72b8ea2 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -155,8 +155,9 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle { } @Override - public Vector2f getAdjustedInput(Vector2f input) { - return input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f); + public Vector3f getRiddenInput(Vector2f input) { + input = input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f); + return Vector3f.from(input.getX(), 0.0, input.getY()); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index 7d022ed7c..1ed9328f0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -90,13 +90,13 @@ public class CamelVehicleComponent extends VehicleComponent { } @Override - protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { + protected Vector3f getInputVector(VehicleContext ctx, float speed, Vector3f input) { if (isStationary()) { return Vector3f.ZERO; } SessionPlayerEntity player = vehicle.getSession().getPlayerEntity(); - Vector3f inputVelocity = super.getInputVelocity(ctx, speed); + Vector3f inputVelocity = super.getInputVector(ctx, speed, input); float jumpStrength = player.getVehicleJumpStrength(); if (jumpStrength > 0) { @@ -117,11 +117,11 @@ public class CamelVehicleComponent extends VehicleComponent { } @Override - protected Vector2f getVehicleRotation() { + protected Vector2f getRiddenRotation() { if (isStationary()) { return Vector2f.from(vehicle.getYaw(), vehicle.getPitch()); } - return super.getVehicleRotation(); + return super.getRiddenRotation(); } /** diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index 00ea802df..e7617e109 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -26,12 +26,13 @@ package org.geysermc.geyser.entity.vehicle; import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.math.vector.Vector3f; public interface ClientVehicle { VehicleComponent getVehicleComponent(); // LivingEntity#getRiddenInput - Vector2f getAdjustedInput(Vector2f input); + Vector3f getRiddenInput(Vector2f input); // MojMap LivingEntity#getRiddenSpeed float getVehicleSpeed(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java index 7d361d6fa..0f666738a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java @@ -25,22 +25,33 @@ package org.geysermc.geyser.entity.vehicle; -import it.unimi.dsi.fastutil.objects.ObjectDoublePair; +import lombok.Getter; import lombok.Setter; import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; +import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; +import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.Fluid; -import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; +import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.level.physics.BoundingBox; +import org.geysermc.geyser.util.MathUtils; public class HappyGhastVehicleComponent extends VehicleComponent { - @Setter + @Getter @Setter private float flyingSpeed; public HappyGhastVehicleComponent(HappyGhastEntity vehicle, float stepHeight) { super(vehicle, stepHeight); - flyingSpeed = (float) AttributeType.Builtin.FLYING_SPEED.getDef(); + flyingSpeed = 0.05f; // Happy Ghast has different default + } + + @Override + protected void updateRotation() { + float addYaw = MathUtils.wrapDegrees(getRiddenRotation().getX() - vehicle.getYaw()) * 0.08f; + vehicle.setYaw(vehicle.getYaw() + addYaw); } /** @@ -51,52 +62,41 @@ public class HappyGhastVehicleComponent extends VehicleComponent fluidHeight = updateFluidMovement(ctx); - - // HappyGhast#travel - float speed = flyingSpeed * 5.0f / 3.0f; - - // TODO implement LivingEntity#travelFlying cases -// switch (fluidHeight.left()) { -// default -> { -// throw new GoodLuckImplementingThisException(); -// } -// } + // LivingEntity#travelFlying + Fluid fluid = checkForFluid(ctx); + float drag = switch (fluid) { + case WATER -> 0.8f; + case LAVA -> 0.5f; + case EMPTY -> 0.91f; + }; + travel(ctx, flyingSpeed * 5.0f / 3.0f); + vehicle.setMotion(vehicle.getMotion().mul(drag)); } - protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { - Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); - input = input.mul(0.98f); // ? + private Fluid checkForFluid(VehicleContext ctx) { + Fluid result = Fluid.EMPTY; - float x = input.getX(); - float y = 0.0F; - float z = 0.0F; + BoundingBox box = boundingBox.clone(); + box.expand(-0.001); - float playerZ = input.getY(); - if (playerZ != 0.0F) { - float i = (float) Math.cos(vehicle.getSession().getPlayerEntity().getPitch() * (Math.PI / 180.0F)); - float j = (float) -Math.sin(vehicle.getSession().getPlayerEntity().getPitch() * (Math.PI / 180.0F)); - if (playerZ < 0.0F) { - i *= -0.5F; - j *= -0.5F; + Vector3d min = box.getMin(); + Vector3d max = box.getMax(); + + BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ()); + for (iter.reset(); iter.hasNext(); iter.next()) { + BlockState blockState = ctx.getBlock(iter); + if (blockState.is(Blocks.WATER)) { + return Fluid.WATER; // Water takes priority over lava + } + if (blockState.is(Blocks.LAVA)) { + result = Fluid.LAVA; } - - y = j; - z = i; } - if (vehicle.getSession().getInputCache().wasJumping()) { - y += 0.5F; - } - - return Vector3f.from(x, y, z).mul(3.9F * flyingSpeed); + return result; } public class GoodLuckImplementingThisException extends RuntimeException { diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 981dd6d91..ab8cefc4c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -68,6 +68,7 @@ public class VehicleComponent { protected final T vehicle; @Getter protected final BoundingBox boundingBox; + protected Vector2f lastSentRotation; // (yaw, pitch) protected float stepHeight; @Getter @Setter @@ -81,6 +82,7 @@ public class VehicleComponent { public VehicleComponent(T vehicle, float stepHeight) { this.vehicle = vehicle; + this.lastSentRotation = Vector2f.from(vehicle.getYaw(), vehicle.getPitch()); this.stepHeight = stepHeight; this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef(); this.gravity = AttributeType.Builtin.GRAVITY.getDef(); @@ -188,6 +190,13 @@ public class VehicleComponent { } } + protected void updateRotation() { + Vector2f rot = getRiddenRotation(); + vehicle.setYaw(rot.getX()); + vehicle.setHeadYaw(rot.getX()); + vehicle.setPitch(rot.getY()); + } + /** * Adds velocity of all colliding fluids to the vehicle, and returns the height of the fluid to use for movement. * @@ -337,11 +346,12 @@ public class VehicleComponent { } /** - * Java edition returns the zero vector if the length of the input vector is less than 0.0001 + * Java edition returns the zero vector if the length of the input vector is less than 0.00001f */ protected Vector3d javaNormalize(Vector3d vec) { double len = vec.length(); - return len < 1.0E-4 ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len); + // Used to be 1.0E-4 + return len < 1.0E-5F ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len); } protected float getWorldFluidHeight(Fluid fluidType, int blockId) { @@ -595,9 +605,13 @@ public class VehicleComponent { Math.abs(motion.getZ()) < MIN_VELOCITY ? 0 : motion.getZ() ); + updateRotation(); + Vector2f playerInput = vehicle.getSession().getPlayerEntity().getVehicleInput(); + Vector3f riddenInput = vehicle.getRiddenInput(playerInput.mul(0.98f)); + // !isImmobile if (vehicle.isAlive()) { - motion = motion.add(getInputVelocity(ctx, speed)); + motion = motion.add(getInputVector(ctx, speed, riddenInput)); } Vector3f movementMultiplier = getBlockMovementMultiplier(ctx); @@ -683,41 +697,30 @@ public class VehicleComponent { return false; } - /** - * Translates the player's input into velocity. - * - * @param ctx context - * @param speed multiplier for input - * @return velocity - */ - protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { - Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); - input = input.mul(0.98f); - input = vehicle.getAdjustedInput(input); - input = normalizeInput(input); + protected Vector3f getInputVector(VehicleContext ctx, float speed, Vector3f input) { + double lenSquared = input.lengthSquared(); + if (lenSquared < 1.0E-7) { + return Vector3f.ZERO; + } + + if (lenSquared > 1.0f) { + input = input.normalize(); + } input = input.mul(speed); - // Match player rotation - float yaw = vehicle.getSession().getPlayerEntity().getYaw(); + // Match vehicle rotation + float yaw = vehicle.getYaw(); float sin = TrigMath.sin(yaw * TrigMath.DEG_TO_RAD); float cos = TrigMath.cos(yaw * TrigMath.DEG_TO_RAD); - return Vector3f.from(input.getX() * cos - input.getY() * sin, 0, input.getY() * cos + input.getX() * sin); - } - - protected Vector2f normalizeInput(Vector2f input) { - float lenSquared = input.lengthSquared(); - if (lenSquared < 1.0E-7) { - return Vector2f.ZERO; - } else if (lenSquared > 1.0) { - return input.normalize(); - } - return input; + return Vector3f.from(input.getX() * cos - input.getZ() * sin, input.getY(), input.getZ() * cos + input.getX() * sin); } /** * Gets the rotation to use for the vehicle. This is based on the player's head rotation. + * + * @return (yaw, pitch) */ - protected Vector2f getVehicleRotation() { + protected Vector2f getRiddenRotation() { LivingEntity player = vehicle.getSession().getPlayerEntity(); return Vector2f.from(player.getYaw(), player.getPitch() * 0.5f); } @@ -730,7 +733,6 @@ public class VehicleComponent { */ protected void moveVehicle(Vector3d javaPos) { Vector3f bedrockPos = javaPos.toFloat(); - Vector2f rotation = getVehicleRotation(); MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket(); moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId()); @@ -753,27 +755,25 @@ public class VehicleComponent { } vehicle.setPosition(bedrockPos); - if (vehicle.getYaw() != rotation.getX()) { + if (vehicle.getYaw() != lastSentRotation.getX()) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW); - moveEntityDeltaPacket.setYaw(rotation.getX()); - vehicle.setYaw(rotation.getX()); + moveEntityDeltaPacket.setYaw(vehicle.getYaw()); } - if (vehicle.getPitch() != rotation.getY()) { + if (vehicle.getPitch() != lastSentRotation.getY()) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH); - moveEntityDeltaPacket.setPitch(rotation.getY()); - vehicle.setPitch(rotation.getY()); + moveEntityDeltaPacket.setPitch(vehicle.getPitch()); } - if (vehicle.getHeadYaw() != rotation.getX()) { // Same as yaw + if (vehicle.getHeadYaw() != lastSentRotation.getX()) { // Same as yaw moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW); - moveEntityDeltaPacket.setHeadYaw(rotation.getX()); - vehicle.setHeadYaw(rotation.getX()); + moveEntityDeltaPacket.setHeadYaw(vehicle.getYaw()); } + lastSentRotation = Vector2f.from(vehicle.getYaw(), vehicle.getPitch()); if (!moveEntityDeltaPacket.getFlags().isEmpty()) { vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket); } - ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, rotation.getX(), rotation.getY(), vehicle.isOnGround()); + ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, vehicle.getYaw(), vehicle.getPitch(), vehicle.isOnGround()); vehicle.getSession().sendDownstreamPacket(moveVehiclePacket); } From 3a68150582ec9078862b81ab70c1845c65498b79 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Wed, 25 Jun 2025 13:11:00 +0000 Subject: [PATCH 06/10] Add registry entity_type to JavaRegistries --- .../session/cache/registry/JavaRegistries.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java index 722c65ac0..f6a3b4c4e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java @@ -48,9 +48,12 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.dialog.Dialog; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Optional; @@ -64,6 +67,9 @@ public class JavaRegistries { Block::javaId, Block::javaIdentifier, key -> Optional.ofNullable(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get(key.asString())).orElse(-1)); public static final JavaRegistryKey ITEM = createHardcoded("item", Registries.JAVA_ITEMS, Item::javaId, Item::javaKey, key -> Optional.ofNullable(Registries.JAVA_ITEM_IDENTIFIERS.get(key.asString())).map(Item::javaId).orElse(-1)); + public static JavaRegistryKey ENTITY_TYPE = createHardcoded("entity_type", Arrays.asList(EntityType.values()), EntityType::ordinal, + type -> MinecraftKey.key(type.name().toLowerCase(Locale.ROOT)), + key -> EntityType.valueOf(key.value().toUpperCase(Locale.ROOT)).ordinal()); public static final JavaRegistryKey CHAT_TYPE = create("chat_type"); public static final JavaRegistryKey DIMENSION_TYPE = create("dimension_type"); @@ -95,6 +101,11 @@ public class JavaRegistries { private static JavaRegistryKey createHardcoded(String key, ListRegistry registry, RegistryNetworkMapper networkSerializer, RegistryIdentifierMapper identifierMapper, RegistryIdMapper idMapper) { + return createHardcoded(key, registry.get(), networkSerializer, identifierMapper, idMapper); + } + + private static JavaRegistryKey createHardcoded(String key, List registry, RegistryNetworkMapper networkSerializer, + RegistryIdentifierMapper identifierMapper, RegistryIdMapper idMapper) { return create(key, new HardcodedLookup<>(registry, networkSerializer, identifierMapper, idMapper)); } @@ -130,9 +141,13 @@ public class JavaRegistries { int get(Key key); } - private record HardcodedLookup(ListRegistry registry, RegistryNetworkMapper networkMapper, RegistryIdentifierMapper identifierMapper, + private record HardcodedLookup(List registry, RegistryNetworkMapper networkMapper, RegistryIdentifierMapper identifierMapper, RegistryIdMapper idMapper) implements JavaRegistryKey.RegistryLookup { + public HardcodedLookup { + System.out.println(registry); + } + @Override public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, int networkId) { return Optional.ofNullable(registry.get(networkId)) From 1bf7ae3ec244b2e8ab74b592b292bd78d7785996 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Wed, 25 Jun 2025 13:14:55 +0000 Subject: [PATCH 07/10] Make it a bit safer --- .../geyser/session/cache/registry/JavaRegistries.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java index f6a3b4c4e..9440bfb09 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java @@ -68,8 +68,13 @@ public class JavaRegistries { public static final JavaRegistryKey ITEM = createHardcoded("item", Registries.JAVA_ITEMS, Item::javaId, Item::javaKey, key -> Optional.ofNullable(Registries.JAVA_ITEM_IDENTIFIERS.get(key.asString())).map(Item::javaId).orElse(-1)); public static JavaRegistryKey ENTITY_TYPE = createHardcoded("entity_type", Arrays.asList(EntityType.values()), EntityType::ordinal, - type -> MinecraftKey.key(type.name().toLowerCase(Locale.ROOT)), - key -> EntityType.valueOf(key.value().toUpperCase(Locale.ROOT)).ordinal()); + type -> MinecraftKey.key(type.name().toLowerCase(Locale.ROOT)), key -> { + try { + return EntityType.valueOf(key.value().toUpperCase(Locale.ROOT)).ordinal(); + } catch (IllegalArgumentException exception) { + return -1; // Non-existent entity type + } + }); public static final JavaRegistryKey CHAT_TYPE = create("chat_type"); public static final JavaRegistryKey DIMENSION_TYPE = create("dimension_type"); From d6897a73e5e11645fb50ecd98e49a3ff3379cb76 Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Wed, 25 Jun 2025 17:37:33 +0200 Subject: [PATCH 08/10] Fixup HappyGhastEntity#isClientControlled check --- .../geyser/entity/type/LivingEntity.java | 27 +++++++++++++ .../type/living/animal/HappyGhastEntity.java | 34 ++++++++++++++--- .../type/living/animal/StriderEntity.java | 6 +++ .../type/living/animal/farm/PigEntity.java | 6 +++ .../animal/horse/AbstractHorseEntity.java | 10 +++++ .../type/living/animal/horse/HorseEntity.java | 8 +++- .../type/living/animal/horse/LlamaEntity.java | 6 +++ .../geyser/entity/vehicle/ClientVehicle.java | 2 +- .../vehicle/HappyGhastVehicleComponent.java | 38 +++++++++++++++---- .../entity/vehicle/VehicleComponent.java | 2 +- .../org/geysermc/geyser/util/EntityUtils.java | 23 +++++------ 11 files changed, 135 insertions(+), 27 deletions(-) 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 1a6da0d91..507b726e0 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 @@ -51,6 +51,7 @@ import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.geyser.util.AttributeUtils; +import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.MathUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; @@ -537,6 +538,32 @@ public class LivingEntity extends Entity { } } + protected boolean hasBodyArmor() { + return this.hasValidEquippableItemForSlot(EquipmentSlot.BODY); + } + + private boolean hasValidEquippableItemForSlot(EquipmentSlot slot) { + // MojMap LivingEntity#hasItemInSlot + GeyserItemStack itemInSlot = equipment.get(slot); + if (itemInSlot != null) { + // MojMap LivingEntity#isEquippableInSlot + Equippable equippable = itemInSlot.getComponent(DataComponentTypes.EQUIPPABLE); + if (equippable != null) { + return slot == equippable.slot() && + canUseSlot(slot) && + EntityUtils.equipmentUsableByEntity(session, equippable, this.definition.entityType()); + } else { + return slot == EquipmentSlot.MAIN_HAND && canUseSlot(EquipmentSlot.MAIN_HAND); + } + } + + return false; + } + + protected boolean canUseSlot(EquipmentSlot slot) { + return true; + } + /** * Calculates the complete attribute value to send to Bedrock. Will be overriden if attributes need to be cached. */ diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 5c2d9d474..9babe06f3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -30,8 +30,10 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.TrigMath; import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.AttributeData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.vehicle.ClientVehicle; @@ -42,12 +44,16 @@ import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.Tag; +import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import java.util.List; import java.util.UUID; public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { @@ -180,12 +186,7 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { @Override public boolean isClientControlled() { - // TODO proper check, just lazy - if (body == null) { - return false; - } - // TODO must have ai check - if (staysStill) { + if (!hasBodyArmor() || getFlag(EntityFlag.NO_AI) || staysStill) { return false; } @@ -195,4 +196,25 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { private Entity getFirstPassenger() { return passengers.isEmpty() ? null : passengers.get(0); } + + @Override + protected void updateAttribute(Attribute javaAttribute, List newAttributes) { + super.updateAttribute(javaAttribute, newAttributes); + if (javaAttribute.getType() instanceof AttributeType.Builtin type) { + switch (type) { + case FLYING_SPEED -> { + AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED); + vehicleComponent.setFlyingSpeed(attributeData.getValue()); + } + case CAMERA_DISTANCE -> { + vehicleComponent.setCameraDistance((float) AttributeUtils.calculateValue(javaAttribute)); + } + } + } + } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.BODY ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index a2febc068..36a126687 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -47,6 +47,7 @@ import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -195,4 +196,9 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic public boolean canWalkOnLava() { return true; } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.SADDLE ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java index 60b549a60..7ae672bcc 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java @@ -48,6 +48,7 @@ import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -154,4 +155,9 @@ public class PigEntity extends TemperatureVariantAnimal implements Tickable, Cli public JavaRegistryKey variantRegistry() { return JavaRegistries.PIG_VARIANT; } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.SADDLE ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java index 7b6184579..8b0e77c73 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java @@ -45,6 +45,7 @@ import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -286,4 +287,13 @@ public class AbstractHorseEntity extends AnimalEntity { return InteractionResult.SUCCESS; } } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + if (slot != EquipmentSlot.SADDLE) { + return super.canUseSlot(slot); + } else { + return isAlive() && !isBaby() && getFlag(EntityFlag.TAMED); + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java index b8a9a8f28..478f89aeb 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java @@ -29,6 +29,7 @@ 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.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import java.util.UUID; @@ -44,4 +45,9 @@ public class HorseEntity extends AbstractHorseEntity { dirtyMetadata.put(EntityDataTypes.VARIANT, value & 255); dirtyMetadata.put(EntityDataTypes.MARK_VARIANT, (value >> 8) % 5); } -} \ No newline at end of file + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return true; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java index d27a1fff3..6ef1cd57a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java @@ -35,6 +35,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.MathUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import java.util.UUID; @@ -61,4 +62,9 @@ public class LlamaEntity extends ChestedHorseEntity { protected @Nullable Tag getFoodTag() { return ItemTag.LLAMA_FOOD; } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index e7617e109..c0480ee90 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -31,7 +31,7 @@ import org.cloudburstmc.math.vector.Vector3f; public interface ClientVehicle { VehicleComponent getVehicleComponent(); - // LivingEntity#getRiddenInput + // MojMap LivingEntity#getRiddenInput Vector3f getRiddenInput(Vector2f input); // MojMap LivingEntity#getRiddenSpeed diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java index 0f666738a..88045064a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java @@ -27,25 +27,31 @@ package org.geysermc.geyser.entity.vehicle; import lombok.Getter; import lombok.Setter; -import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3d; -import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.Fluid; import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.util.MathUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; +@Setter +@Getter public class HappyGhastVehicleComponent extends VehicleComponent { - @Getter @Setter private float flyingSpeed; + private float cameraDistance; public HappyGhastVehicleComponent(HappyGhastEntity vehicle, float stepHeight) { super(vehicle, stepHeight); - flyingSpeed = 0.05f; // Happy Ghast has different default + // Happy Ghast has different defaults + flyingSpeed = 0.05f; + moveSpeed = 0.05f; + cameraDistance = 8.0f; } @Override @@ -54,6 +60,26 @@ public class HappyGhastVehicleComponent extends VehicleComponent 0.5f; case EMPTY -> 0.91f; }; + // HappyGhast#travel travel(ctx, flyingSpeed * 5.0f / 3.0f); vehicle.setMotion(vehicle.getMotion().mul(drag)); } @@ -98,7 +125,4 @@ public class HappyGhastVehicleComponent extends VehicleComponent { * * @return true if there was a horizontal collision */ - // Mojmap: LivingEntity#moveRelative + // Mojmap: LivingEntity#moveRelative / LivingEntity#move protected boolean travel(VehicleContext ctx, float speed) { Vector3f motion = vehicle.getMotion(); diff --git a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java index 9cdcbed9f..9126b424d 100644 --- a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java @@ -44,11 +44,14 @@ import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; +import org.geysermc.geyser.session.cache.tags.GeyserHolderSet; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable; import java.util.Locale; @@ -283,23 +286,12 @@ public final class EntityUtils { passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 90f); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, true); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, -90f); - } else if (mount instanceof HappyGhastEntity) { - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 181f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, 8f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, 6f); - - passenger.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0); } } else { passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 0f); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, 0f); - // TODO what are defaults here??? - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, 8f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, 6f); - passenger.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0); } } @@ -363,6 +355,15 @@ public final class EntityUtils { return translatedEntityName("minecraft", typeName, session); } + public static boolean equipmentUsableByEntity(GeyserSession session, Equippable equippable, EntityType entity) { + if (equippable.allowedEntities() == null) { + return true; + } + + GeyserHolderSet holderSet = GeyserHolderSet.fromHolderSet(JavaRegistries.ENTITY_TYPE, equippable.allowedEntities()); + return session.getTagCache().is(holderSet, entity); + } + private EntityUtils() { } } From 39b15f945cb3a05a3d77c6792878b6e1fbe2a54e Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Wed, 25 Jun 2025 17:40:58 +0200 Subject: [PATCH 09/10] Minor cleanup --- .../entity/type/living/animal/HappyGhastEntity.java | 11 ++--------- .../org/geysermc/geyser/item/hashing/MapHasher.java | 3 --- .../geyser/session/cache/registry/JavaRegistries.java | 4 ---- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 9babe06f3..e8cb59e21 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -33,7 +33,6 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.AttributeData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.vehicle.ClientVehicle; @@ -201,14 +200,8 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { protected void updateAttribute(Attribute javaAttribute, List newAttributes) { super.updateAttribute(javaAttribute, newAttributes); if (javaAttribute.getType() instanceof AttributeType.Builtin type) { - switch (type) { - case FLYING_SPEED -> { - AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED); - vehicleComponent.setFlyingSpeed(attributeData.getValue()); - } - case CAMERA_DISTANCE -> { - vehicleComponent.setCameraDistance((float) AttributeUtils.calculateValue(javaAttribute)); - } + if (type == AttributeType.Builtin.CAMERA_DISTANCE) { + vehicleComponent.setCameraDistance((float) AttributeUtils.calculateValue(javaAttribute)); } } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java index c055b825c..2072ace29 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java @@ -213,9 +213,6 @@ public class MapHasher { } public HashCode build() { - if (unhashed != null) { - System.out.println(unhashed); - } return encoder.map(map); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java index 9440bfb09..25acb493f 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java @@ -149,10 +149,6 @@ public class JavaRegistries { private record HardcodedLookup(List registry, RegistryNetworkMapper networkMapper, RegistryIdentifierMapper identifierMapper, RegistryIdMapper idMapper) implements JavaRegistryKey.RegistryLookup { - public HardcodedLookup { - System.out.println(registry); - } - @Override public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, int networkId) { return Optional.ofNullable(registry.get(networkId)) From 9fb0dfd9750329377db839f34f29613a0322b0ca Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Wed, 25 Jun 2025 19:43:37 +0200 Subject: [PATCH 10/10] Small fixes --- .../geysermc/geyser/entity/type/LivingEntity.java | 5 ++--- .../java/entity/JavaSetPassengersTranslator.java | 3 +-- .../org/geysermc/geyser/util/EntityUtils.java | 15 ++++++--------- 3 files changed, 9 insertions(+), 14 deletions(-) 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 507b726e0..92048eb25 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 @@ -516,9 +516,8 @@ public class LivingEntity extends Entity { case FLYING_SPEED -> { AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED); newAttributes.add(attributeData); - if (this instanceof HappyGhastEntity happyGhast && - happyGhast.getVehicleComponent() instanceof HappyGhastVehicleComponent vehicleComponent) { - vehicleComponent.setFlyingSpeed(attributeData.getValue()); + if (this instanceof HappyGhastEntity ghast && ghast.getVehicleComponent() instanceof HappyGhastVehicleComponent component) { + component.setFlyingSpeed(attributeData.getValue()); } } case FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java index 3bc25c986..1c3ac4688 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.translator.protocol.java.entity; -import lombok.NonNull; +import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; @@ -54,7 +54,6 @@ public class JavaSetPassengersTranslator extends PacketTranslator { - // 0.0, 5.02001, 1.7 BDS int seatingIndex = Math.min(index, 4); xOffset = HappyGhastEntity.X_OFFSETS[seatingIndex]; yOffset = 3.4f; @@ -279,14 +278,12 @@ public final class EntityUtils { } public static void updateRiderRotationLock(Entity passenger, Entity mount, boolean isRiding) { - if (isRiding) { - if (mount instanceof BoatEntity) { - // Head rotation is locked while riding in a boat - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, true); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 90f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, true); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, -90f); - } + if (isRiding && mount instanceof BoatEntity) { + // Head rotation is locked while riding in a boat + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, true); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 90f); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, true); + passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, -90f); } else { passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 0f);