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 EntityDefinition<AbstractNautilusEntity> abstractNautilusBase = EntityDefinition.<AbstractNautilusEntity>inherited(null, tameableEntityBase) // No factory, is abstract
.width(0.95f).height(0.875f) .width(0.95f).height(0.875f)
.addTranslator(null) .addTranslator(MetadataTypes.BOOLEAN, AbstractNautilusEntity::setBoost)
.build(); .build();
NAUTILUS = EntityDefinition.inherited(NautilusEntity::new, abstractNautilusBase) NAUTILUS = EntityDefinition.inherited(NautilusEntity::new, abstractNautilusBase)

View File

@@ -536,6 +536,11 @@ public class LivingEntity extends Entity {
clientVehicle.getVehicleComponent().setWaterMovementEfficiency(AttributeUtils.calculateValue(javaAttribute)); 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.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.NautilusVehicleComponent; import org.geysermc.geyser.entity.vehicle.NautilusVehicleComponent;
import org.geysermc.geyser.entity.vehicle.VehicleComponent; import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.input.InputLocksFlag;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.EnchantmentComponent; import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.Item; 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.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.ItemUtils; import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; 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.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; 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.player.Hand;
@@ -112,8 +115,27 @@ public abstract class AbstractNautilusEntity extends TameableEntity implements C
return super.testMobInteraction(hand, itemInHand); return super.testMobInteraction(hand, itemInHand);
} }
public void setBoost(IntEntityMetadata entityMetadata) { @Override
vehicleComponent.startBoost(entityMetadata.getPrimitiveValue()); 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 @Override
@@ -137,25 +159,16 @@ public abstract class AbstractNautilusEntity extends TameableEntity implements C
} }
} }
if (session.getInputCache().wasJumping()) { return Vector3f.from(x, y, z);
y += 0.5f;
}
return Vector3f.from(x, y, z).mul(3.9f * vehicleComponent.getMoveSpeed());
} }
@Override @Override
public float getVehicleSpeed() { public float getVehicleSpeed() {
return 0.0f; // Unused return vehicleComponent.isInWater() ? 0.0325F * vehicleComponent.getMoveSpeed() : 0.02F * vehicleComponent.getMoveSpeed();
} }
@Override @Override
public boolean isClientControlled() { public boolean isClientControlled() {
return false; return !this.passengers.isEmpty() && this.passengers.get(0) == session.getPlayerEntity();
}
@Override
public boolean canClimb() {
return false;
} }
} }

View File

@@ -25,49 +25,76 @@
package org.geysermc.geyser.entity.vehicle; 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.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; 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) { public NautilusVehicleComponent(AbstractNautilusEntity vehicle, float stepHeight, float defSpeed) {
super(vehicle, stepHeight); super(vehicle, stepHeight);
this.moveSpeed = defSpeed; 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 @Override
protected void updateRotation() { 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.setYaw(yaw);
vehicle.setHeadYaw(yaw); vehicle.setHeadYaw(yaw);
} }
/** @Override
* Called every session tick while the player is mounted on the vehicle. protected void waterMovement(VehicleComponent<AbstractNautilusEntity>.VehicleContext ctx) {
*/ travel(ctx, vehicle.getVehicleSpeed());
public void tickVehicle() { this.vehicle.setMotion(this.vehicle.getMotion().mul(0.9f));
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));
} }
// AbstractNautilus#getRiddenSpeed public void setDashCooldown(int cooldown) {
private float getRiddenSpeed(Fluid fluid) { this.dashCooldown = this.dashCooldown == 0 ? cooldown : this.dashCooldown;
return fluid == Fluid.WATER ? 0.0325F * this.getMoveSpeed() :
0.02F * this.getMoveSpeed(); 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 float moveSpeed;
protected double gravity; protected double gravity;
@Getter @Setter @Getter @Setter
protected double waterMovementEfficiency; protected double waterMovementEfficiency, movementEfficiency;
protected int effectLevitation; protected int effectLevitation;
protected boolean effectSlowFalling; protected boolean effectSlowFalling;
protected boolean effectWeaving; protected boolean effectWeaving;
@@ -85,6 +85,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef(); this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef();
this.gravity = AttributeType.Builtin.GRAVITY.getDef(); this.gravity = AttributeType.Builtin.GRAVITY.getDef();
this.waterMovementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef(); this.waterMovementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef();
this.movementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef();
double width = vehicle.getBoundingBoxWidth(); double width = vehicle.getBoundingBoxWidth();
double height = vehicle.getBoundingBoxHeight(); 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. * 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(); ctx.loadSurroundingBlocks();
ObjectDoublePair<Fluid> fluidHeight = updateFluidMovement(ctx); ObjectDoublePair<Fluid> fluidHeight = updateFluidMovement(ctx);
inWater = fluidHeight.left() == Fluid.WATER;
switch (fluidHeight.left()) { switch (fluidHeight.left()) {
case WATER -> waterMovement(ctx); case WATER -> waterMovement(ctx);
case LAVA -> { 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. * 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++; fluidBlocks++;
} }
if (!totalVelocity.equals(Vector3d.ZERO)) { if (!totalVelocity.equals(Vector3d.ZERO) && isPushedByFluid()) {
Vector3f motion = vehicle.getMotion(); Vector3f motion = vehicle.getMotion();
totalVelocity = javaNormalize(totalVelocity.mul(1.0 / fluidBlocks)); 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.Entity;
import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; 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.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.type.player.SessionPlayerEntity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.level.physics.BoundingBox; 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(); 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 // Behavior verified as of Java Edition 1.21.3
int currentJumpingTicks = session.getInputCache().getJumpingTicks(); int currentJumpingTicks = session.getInputCache().getJumpingTicks();
if (currentJumpingTicks < 0) { if (currentJumpingTicks < 0) {

View File

@@ -25,6 +25,7 @@
package org.geysermc.geyser.util; package org.geysermc.geyser.util;
import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.TrigMath; import org.cloudburstmc.math.TrigMath;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
@@ -47,6 +48,16 @@ public class MathUtils {
return Vector3f.from(d1, e, i1); 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. * Wrap the given float degrees to be between -180.0 and 180.0.
* *