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

Implement proper nautilus vehicle movement.

This commit is contained in:
oryxel1
2025-12-12 00:08:12 +07:00
parent 5b16f26fc0
commit 7b92716986
7 changed files with 112 additions and 46 deletions

View File

@@ -1234,7 +1234,7 @@ public final class EntityDefinitions {
{
EntityDefinition<AbstractNautilusEntity> abstractNautilusBase = EntityDefinition.<AbstractNautilusEntity>inherited(null, tameableEntityBase) // No factory, is abstract
.width(0.95f).height(0.875f)
.addTranslator(null)
.addTranslator(MetadataTypes.BOOLEAN, AbstractNautilusEntity::setBoost)
.build();
NAUTILUS = EntityDefinition.inherited(NautilusEntity::new, abstractNautilusBase)

View File

@@ -536,6 +536,11 @@ public class LivingEntity extends Entity {
clientVehicle.getVehicleComponent().setWaterMovementEfficiency(AttributeUtils.calculateValue(javaAttribute));
}
}
case MOVEMENT_EFFICIENCY -> {
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setMovementEfficiency(AttributeUtils.calculateValue(javaAttribute));
}
}
}
}
}

View File

@@ -37,16 +37,19 @@ import org.geysermc.geyser.entity.type.living.animal.tameable.TameableEntity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.NautilusVehicleComponent;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.input.InputLocksFlag;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.block.Fluid;
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.InteractiveTag;
import org.geysermc.geyser.util.ItemUtils;
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.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
@@ -112,8 +115,27 @@ public abstract class AbstractNautilusEntity extends TameableEntity implements C
return super.testMobInteraction(hand, itemInHand);
}
public void setBoost(IntEntityMetadata entityMetadata) {
vehicleComponent.startBoost(entityMetadata.getPrimitiveValue());
@Override
protected void updateSaddled(boolean saddled) {
setFlag(EntityFlag.CAN_DASH, saddled);
super.updateSaddled(saddled);
if (this.passengers.contains(session.getPlayerEntity())) {
// We want to allow player to press jump again if pressing jump doesn't dismount the entity.
this.session.setLockInput(InputLocksFlag.JUMP, this.doesJumpDismount());
this.session.updateInputLocks();
}
}
@Override
public boolean doesJumpDismount() {
return !this.getFlag(EntityFlag.SADDLED);
}
public void setBoost(BooleanEntityMetadata entityMetadata) {
if (entityMetadata.getPrimitiveValue()) {
vehicleComponent.setDashCooldown(40);
}
}
@Override
@@ -137,25 +159,16 @@ public abstract class AbstractNautilusEntity extends TameableEntity implements C
}
}
if (session.getInputCache().wasJumping()) {
y += 0.5f;
}
return Vector3f.from(x, y, z).mul(3.9f * vehicleComponent.getMoveSpeed());
return Vector3f.from(x, y, z);
}
@Override
public float getVehicleSpeed() {
return 0.0f; // Unused
return vehicleComponent.isInWater() ? 0.0325F * vehicleComponent.getMoveSpeed() : 0.02F * vehicleComponent.getMoveSpeed();
}
@Override
public boolean isClientControlled() {
return false;
}
@Override
public boolean canClimb() {
return false;
return !this.passengers.isEmpty() && this.passengers.get(0) == session.getPlayerEntity();
}
}

View File

@@ -25,49 +25,76 @@
package org.geysermc.geyser.entity.vehicle;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.type.living.animal.nautilus.AbstractNautilusEntity;
import org.geysermc.geyser.level.block.Fluid;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.util.MathUtils;
public class NautilusVehicleComponent extends BoostableVehicleComponent<AbstractNautilusEntity> {
public class NautilusVehicleComponent extends VehicleComponent<AbstractNautilusEntity> {
private int dashCooldown;
public NautilusVehicleComponent(AbstractNautilusEntity vehicle, float stepHeight, float defSpeed) {
super(vehicle, stepHeight);
this.moveSpeed = defSpeed;
}
@Override
public boolean isPushedByFluid() {
return false;
}
@Override
public void tickVehicle() {
vehicle.setFlag(EntityFlag.CAN_DASH, vehicle.getFlag(EntityFlag.SADDLED));
vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, this.dashCooldown > 0);
vehicle.updateBedrockMetadata();
super.tickVehicle();
if (this.dashCooldown > 0) {
this.dashCooldown--;
}
}
@Override
protected Vector3f getInputVector(VehicleComponent<AbstractNautilusEntity>.VehicleContext ctx, float speed, Vector3f input) {
Vector3f inputVelocity = super.getInputVector(ctx, speed, input);
SessionPlayerEntity player = vehicle.getSession().getPlayerEntity();
float jumpStrength = player.getVehicleJumpStrength();
player.setVehicleJumpStrength(0);
if (this.dashCooldown <= 0 && jumpStrength > 0) {
final Vector3f viewVector = MathUtils.calculateViewVector(vehicle.getPitch(), vehicle.getYaw());
float movementMultiplier = getVelocityMultiplier(ctx);
float strength = (float) (movementMultiplier + movementEfficiency * (1 - movementMultiplier));
float actualJumpStrength = (jumpStrength >= 90) ? 1.0F : (0.4F + 0.4F * jumpStrength / 90.0F);
inputVelocity = inputVelocity.add(viewVector.mul(((this.isInWater() ? 1.2F : 0.5F) * actualJumpStrength) * getMoveSpeed() * strength));
setDashCooldown(40);
}
return inputVelocity;
}
@Override
protected void updateRotation() {
float yaw = vehicle.getYaw() + MathUtils.wrapDegrees(getRiddenRotation().getX() - vehicle.getYaw()) * 0.08f;
float yaw = vehicle.getYaw() + MathUtils.wrapDegrees(getRiddenRotation().getX() - vehicle.getYaw()) * 0.5F;
vehicle.setYaw(yaw);
vehicle.setHeadYaw(yaw);
}
/**
* Called every session tick while the player is mounted on the vehicle.
*/
public void tickVehicle() {
if (!vehicle.isClientControlled()) {
return;
}
VehicleContext ctx = new VehicleContext();
ctx.loadSurroundingBlocks();
// LivingEntity#travel
Fluid fluid = checkForFluid(ctx);
float drag = switch (fluid) {
case WATER -> 0.9f; // AbstractNautilus#travelInWater
case LAVA -> 0.5f; // LivingEntity#travelInLava
case EMPTY -> 1f; // TODO No drag it seems? Should probably check the block below, e.g. soul sand
};
travel(ctx, getRiddenSpeed(fluid));
vehicle.setMotion(vehicle.getMotion().mul(drag));
@Override
protected void waterMovement(VehicleComponent<AbstractNautilusEntity>.VehicleContext ctx) {
travel(ctx, vehicle.getVehicleSpeed());
this.vehicle.setMotion(this.vehicle.getMotion().mul(0.9f));
}
// AbstractNautilus#getRiddenSpeed
private float getRiddenSpeed(Fluid fluid) {
return fluid == Fluid.WATER ? 0.0325F * this.getMoveSpeed() :
0.02F * this.getMoveSpeed();
public void setDashCooldown(int cooldown) {
this.dashCooldown = this.dashCooldown == 0 ? cooldown : this.dashCooldown;
vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, this.dashCooldown > 0);
vehicle.updateBedrockMetadata();
}
}

View File

@@ -74,7 +74,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
protected float moveSpeed;
protected double gravity;
@Getter @Setter
protected double waterMovementEfficiency;
protected double waterMovementEfficiency, movementEfficiency;
protected int effectLevitation;
protected boolean effectSlowFalling;
protected boolean effectWeaving;
@@ -85,6 +85,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef();
this.gravity = AttributeType.Builtin.GRAVITY.getDef();
this.waterMovementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef();
this.movementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef();
double width = vehicle.getBoundingBoxWidth();
double height = vehicle.getBoundingBoxHeight();
@@ -163,6 +164,8 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
//
}
@Getter
private boolean inWater;
/**
* Called every session tick while the player is mounted on the vehicle.
*/
@@ -175,6 +178,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
ctx.loadSurroundingBlocks();
ObjectDoublePair<Fluid> fluidHeight = updateFluidMovement(ctx);
inWater = fluidHeight.left() == Fluid.WATER;
switch (fluidHeight.left()) {
case WATER -> waterMovement(ctx);
case LAVA -> {
@@ -188,6 +192,10 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
}
}
public boolean isPushedByFluid() {
return true;
}
/**
* Update the rotation of the vehicle. Should be called once per tick, and before getInputVector.
*/
@@ -330,7 +338,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
fluidBlocks++;
}
if (!totalVelocity.equals(Vector3d.ZERO)) {
if (!totalVelocity.equals(Vector3d.ZERO) && isPushedByFluid()) {
Vector3f motion = vehicle.getMotion();
totalVelocity = javaNormalize(totalVelocity.mul(1.0 / fluidBlocks));

View File

@@ -39,6 +39,8 @@ import org.geysermc.geyser.entity.type.BoatEntity;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
import org.geysermc.geyser.entity.type.living.animal.nautilus.AbstractNautilusEntity;
import org.geysermc.geyser.entity.type.living.animal.nautilus.NautilusEntity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.level.physics.BoundingBox;
@@ -249,7 +251,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
sendMovement = vehicle.getPassengers().size() == 1 || session.getPlayerEntity().isRidingInFront();
}
if (vehicle instanceof AbstractHorseEntity && !vehicle.getFlag(EntityFlag.HAS_DASH_COOLDOWN)) {
if ((vehicle instanceof AbstractHorseEntity || vehicle instanceof AbstractNautilusEntity) && !vehicle.getFlag(EntityFlag.HAS_DASH_COOLDOWN)) {
// Behavior verified as of Java Edition 1.21.3
int currentJumpingTicks = session.getInputCache().getJumpingTicks();
if (currentJumpingTicks < 0) {

View File

@@ -25,6 +25,7 @@
package org.geysermc.geyser.util;
import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.TrigMath;
import org.cloudburstmc.math.vector.Vector3f;
@@ -47,6 +48,16 @@ public class MathUtils {
return Vector3f.from(d1, e, i1);
}
public static Vector3f calculateViewVector(float pitch, float yaw) {
float var3 = pitch * 0.017453292F;
float var4 = -yaw * 0.017453292F;
float var5 = TrigMath.cos(var4);
float var6 = TrigMath.sin(var4);
float var7 = TrigMath.cos(var3);
float var8 = TrigMath.sin(var3);
return Vector3f.from(var6 * var7, -var8, var5 * var7);
}
/**
* Wrap the given float degrees to be between -180.0 and 180.0.
*