mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Merge master
This commit is contained in:
@@ -26,12 +26,16 @@
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.vehicle.BoatVehicleComponent;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
@@ -41,7 +45,7 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.Serve
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BoatEntity extends Entity implements Leashable, Tickable {
|
||||
public class BoatEntity extends Entity implements Tickable, Leashable, ClientVehicle {
|
||||
|
||||
/**
|
||||
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
|
||||
@@ -53,6 +57,8 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
||||
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
|
||||
"\"minecraft:flowing_water\"],\"simulate_waves\":false}";
|
||||
|
||||
private final BoatVehicleComponent vehicleComponent = new BoatVehicleComponent(this, 0);
|
||||
|
||||
private boolean isPaddlingLeft;
|
||||
private float paddleTimeLeft;
|
||||
private boolean isPaddlingRight;
|
||||
@@ -67,8 +73,8 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
||||
|
||||
private long leashHolderBedrockId = -1;
|
||||
|
||||
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
|
||||
private final float ROWING_SPEED = 0.1f;
|
||||
// This is the best value, I can't really found any value that doesn't look choppy and laggy or that is not too slow, blame bedrock.
|
||||
private final float ROWING_SPEED = 0.04f;
|
||||
|
||||
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) {
|
||||
// Initial rotation is incorrect
|
||||
@@ -192,8 +198,13 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
||||
// For packet timing accuracy, we'll send the packets here, as that's what Java Edition 1.21.3 does.
|
||||
ServerboundPaddleBoatPacket steerPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
|
||||
session.sendDownstreamGamePacket(steerPacket);
|
||||
return;
|
||||
|
||||
// If the vehicle is not controlled by the client, then we will have to send the client the rowing time else the animation won't play!
|
||||
if (session.isInClientPredictedVehicle()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
doTick = !doTick; // Run every other tick
|
||||
if (!doTick || passengers.isEmpty()) {
|
||||
return;
|
||||
@@ -212,11 +223,35 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
||||
paddleTimeRight += ROWING_SPEED;
|
||||
dirtyMetadata.put(EntityDataTypes.ROW_TIME_RIGHT, paddleTimeRight);
|
||||
}
|
||||
|
||||
if (isPaddlingLeft || isPaddlingRight) {
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long leashHolderBedrockId() {
|
||||
return leashHolderBedrockId;
|
||||
return this.leashHolderBedrockId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleComponent<?> getVehicleComponent() {
|
||||
return this.vehicleComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f getRiddenInput(Vector2f input) {
|
||||
return Vector3f.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVehicleSpeed() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientControlled() {
|
||||
return !session.isInClientPredictedVehicle() && !passengers.isEmpty() && this.session.getPlayerEntity() == passengers.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
|
||||
import org.geysermc.geyser.entity.properties.type.PropertyType;
|
||||
import org.geysermc.geyser.entity.type.living.MobEntity;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
@@ -258,6 +259,13 @@ public class Entity implements GeyserEntity {
|
||||
}
|
||||
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
if (this instanceof ClientVehicle clientVehicle) {
|
||||
if (clientVehicle.isClientControlled()) {
|
||||
return;
|
||||
}
|
||||
clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ);
|
||||
}
|
||||
|
||||
position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||
|
||||
MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket();
|
||||
|
||||
@@ -356,18 +356,6 @@ public class LivingEntity extends Entity {
|
||||
return super.interact(hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
if (this instanceof ClientVehicle clientVehicle) {
|
||||
if (clientVehicle.isClientControlled()) {
|
||||
return;
|
||||
}
|
||||
clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ);
|
||||
}
|
||||
|
||||
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBoundingBoxHeight(float height) {
|
||||
if (valid && this instanceof ClientVehicle clientVehicle) {
|
||||
|
||||
@@ -27,7 +27,9 @@ package org.geysermc.geyser.entity.type.living.animal.horse;
|
||||
|
||||
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.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
@@ -37,6 +39,9 @@ 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.AnimalEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.HorseVehicleComponent;
|
||||
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;
|
||||
@@ -47,12 +52,16 @@ 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.attribute.Attribute;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class AbstractHorseEntity extends AnimalEntity {
|
||||
public class AbstractHorseEntity extends AnimalEntity implements ClientVehicle {
|
||||
|
||||
private final HorseVehicleComponent vehicleComponent = new HorseVehicleComponent(this);
|
||||
|
||||
public AbstractHorseEntity(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);
|
||||
@@ -94,6 +103,15 @@ public class AbstractHorseEntity extends AnimalEntity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
|
||||
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.JUMP_STRENGTH) {
|
||||
vehicleComponent.setHorseJumpStrength(attributeData.getValue());
|
||||
}
|
||||
return attributeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesJumpDismount() {
|
||||
return !this.getFlag(EntityFlag.SADDLED);
|
||||
@@ -309,4 +327,25 @@ public class AbstractHorseEntity extends AnimalEntity {
|
||||
return isAlive() && !isBaby() && getFlag(EntityFlag.TAMED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleComponent<?> getVehicleComponent() {
|
||||
return this.vehicleComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public float getVehicleSpeed() {
|
||||
return vehicleComponent.getMoveSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientControlled() {
|
||||
return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity() && !session.isInClientPredictedVehicle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.geyser.entity.type.living.animal.horse;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
@@ -152,12 +151,6 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
|
||||
return vehicleComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public boolean isClientControlled() {
|
||||
return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity();
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* 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 org.cloudburstmc.math.GenericMath;
|
||||
import org.cloudburstmc.math.TrigMath;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.entity.type.BoatEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.Fluid;
|
||||
import org.geysermc.geyser.level.block.type.BedBlock;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.Axis;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
|
||||
|
||||
public class BoatVehicleComponent extends VehicleComponent<BoatEntity> {
|
||||
private Status status, oldStatus;
|
||||
private double waterLevel;
|
||||
private float landFriction;
|
||||
private double lastYd;
|
||||
private float deltaRotation;
|
||||
|
||||
public BoatVehicleComponent(BoatEntity vehicle, float stepHeight) {
|
||||
super(vehicle, stepHeight);
|
||||
|
||||
this.gravity = 0.04;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickVehicle() {
|
||||
if (!vehicle.isClientControlled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final VehicleContext context = new VehicleContext();
|
||||
context.loadSurroundingBlocks();
|
||||
|
||||
this.oldStatus = this.status;
|
||||
this.status = getStatus(context);
|
||||
|
||||
floatBoat(context);
|
||||
|
||||
final Vector3f lastRotation = vehicle.getBedrockRotation();
|
||||
controlBoat();
|
||||
|
||||
Vector3f motion = vehicle.getMotion();
|
||||
Vector3f movementMultiplier = getBlockMovementMultiplier(context);
|
||||
if (movementMultiplier != null) {
|
||||
motion = motion.mul(movementMultiplier);
|
||||
}
|
||||
|
||||
Vector3d correctedMovement = vehicle.getSession().getWorldBorder().correctMovement(boundingBox, motion.toDouble());
|
||||
correctedMovement = vehicle.getSession().getCollisionManager().correctMovement(
|
||||
correctedMovement, boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava()
|
||||
);
|
||||
|
||||
// Non-zero values indicate a collision on that axis
|
||||
Vector3d moveDiff = motion.toDouble().sub(correctedMovement);
|
||||
|
||||
boundingBox.translate(correctedMovement);
|
||||
context.loadSurroundingBlocks(); // Context must be reloaded after vehicle is moved
|
||||
|
||||
boolean verticalCollision = moveDiff.getY() != 0;
|
||||
vehicle.setOnGround(verticalCollision && motion.getY() < 0);
|
||||
|
||||
boolean bounced = false;
|
||||
if (vehicle.isOnGround()) {
|
||||
Block landingBlock = getLandingBlock(context).block();
|
||||
|
||||
if (landingBlock == Blocks.SLIME_BLOCK) {
|
||||
motion = Vector3f.from(motion.getX(), -motion.getY(), motion.getZ());
|
||||
bounced = true;
|
||||
|
||||
// Slow horizontal movement
|
||||
float absY = Math.abs(motion.getY());
|
||||
if (absY < 0.1f) {
|
||||
float mul = 0.4f + absY * 0.2f;
|
||||
motion = motion.mul(mul, 1.0f, mul);
|
||||
}
|
||||
} else if (landingBlock instanceof BedBlock) {
|
||||
motion = Vector3f.from(motion.getX(), -motion.getY() * 0.66f, motion.getZ());
|
||||
bounced = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Set motion to 0 if a movement multiplier was used, else set to 0 on each axis with a collision
|
||||
if (movementMultiplier != null) {
|
||||
motion = Vector3f.ZERO;
|
||||
} else {
|
||||
motion = motion.mul(
|
||||
moveDiff.getX() == 0 ? 1 : 0,
|
||||
!verticalCollision || bounced ? 1 : 0,
|
||||
moveDiff.getZ() == 0 ? 1 : 0
|
||||
);
|
||||
}
|
||||
|
||||
// Send the new position to the bedrock client and java server
|
||||
moveVehicle(context.centerPos(), lastRotation);
|
||||
vehicle.setMotion(motion);
|
||||
|
||||
// This got ran twice in Boat entity for certain reason?
|
||||
applyBlockCollisionEffects(context);
|
||||
applyBlockCollisionEffects(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveVehicle(Vector3d javaPos, Vector3f lastRotation) {
|
||||
Vector3f bedrockPos = javaPos.toFloat();
|
||||
|
||||
MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket();
|
||||
moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId());
|
||||
|
||||
if (vehicle.isOnGround()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND);
|
||||
}
|
||||
|
||||
if (vehicle.getPosition().getX() != bedrockPos.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
|
||||
moveEntityDeltaPacket.setX(bedrockPos.getX());
|
||||
}
|
||||
if (vehicle.getPosition().getY() != bedrockPos.getY()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
|
||||
moveEntityDeltaPacket.setY(bedrockPos.getY() + vehicle.getDefinition().offset());
|
||||
}
|
||||
if (vehicle.getPosition().getZ() != bedrockPos.getZ()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
|
||||
moveEntityDeltaPacket.setZ(bedrockPos.getZ());
|
||||
}
|
||||
vehicle.setPosition(bedrockPos);
|
||||
|
||||
if (vehicle.getPitch() != lastRotation.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
|
||||
moveEntityDeltaPacket.setPitch(vehicle.getPitch());
|
||||
}
|
||||
if (vehicle.getYaw() != lastRotation.getY()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
|
||||
moveEntityDeltaPacket.setYaw(vehicle.getYaw());
|
||||
}
|
||||
if (vehicle.getHeadYaw() != lastRotation.getZ()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW);
|
||||
moveEntityDeltaPacket.setHeadYaw(vehicle.getHeadYaw());
|
||||
}
|
||||
|
||||
if (!moveEntityDeltaPacket.getFlags().isEmpty()) {
|
||||
vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket);
|
||||
}
|
||||
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, vehicle.getYaw() - 90, vehicle.getPitch(), vehicle.isOnGround());
|
||||
vehicle.getSession().sendDownstreamPacket(moveVehiclePacket);
|
||||
}
|
||||
|
||||
private void controlBoat() {
|
||||
float acceleration = 0.0F;
|
||||
|
||||
final Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput();
|
||||
boolean up = input.getY() > 0;
|
||||
boolean down = input.getY() < 0;
|
||||
boolean left = input.getX() > 0;
|
||||
boolean right = input.getX() < 0;
|
||||
|
||||
if (left) this.deltaRotation--;
|
||||
if (right) this.deltaRotation++;
|
||||
|
||||
if (right != left && !up && !down) acceleration += 0.005F;
|
||||
|
||||
vehicle.setYaw(vehicle.getYaw() + this.deltaRotation);
|
||||
vehicle.setHeadYaw(vehicle.getYaw());
|
||||
|
||||
if (up) acceleration += 0.04F;
|
||||
if (down) acceleration -= 0.005F;
|
||||
|
||||
float yaw = vehicle.getYaw() - 90;
|
||||
final Vector3f motion = Vector3f.from(TrigMath.sin((-yaw * 0.017453292F)) * acceleration, 0, TrigMath.cos((yaw * 0.017453292F)) * acceleration);
|
||||
vehicle.setMotion(vehicle.getMotion().add(motion));
|
||||
|
||||
vehicle.getSession().setSteeringLeft(right && !left || up);
|
||||
vehicle.getSession().setSteeringRight(left && !right || up);
|
||||
}
|
||||
|
||||
private void floatBoat(final VehicleContext context) {
|
||||
double gravity = -getGravity();
|
||||
double buoyancy = 0.0D;
|
||||
float frictionMutiplier = 0.05F;
|
||||
|
||||
if (this.oldStatus == Status.IN_AIR && this.status != Status.IN_AIR && this.status != Status.ON_LAND) {
|
||||
this.waterLevel = getBoundingBox().getMax(Axis.Y);
|
||||
double targetY = (getWaterLevelAbove(context) - this.vehicle.getBoundingBoxHeight()) + 0.101D;
|
||||
|
||||
BoundingBox box = boundingBox.clone();
|
||||
box.translate(0, targetY - getBoundingBox().getMin(Axis.Y), 0);
|
||||
|
||||
boolean empty = true;
|
||||
for (BlockPositionIterator iter = vehicle.getSession().getCollisionManager().collidableBlocksIterator(box); iter.hasNext(); iter.next()) {
|
||||
final BlockCollision collision = BlockUtils.getCollision(context.getBlockId(iter.getX(), iter.getY(), iter.getZ()));
|
||||
if (collision != null && collision.checkIntersection(Vector3i.from(iter.getX(), iter.getY(), iter.getZ()), box)) {
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
vehicle.setMotion(vehicle.getMotion().mul(1, 0, 1));
|
||||
boundingBox.setMiddleY(targetY + (boundingBox.getSizeY() / 2));
|
||||
this.lastYd = 0;
|
||||
}
|
||||
|
||||
this.status = Status.IN_WATER;
|
||||
} else {
|
||||
if (this.status == Status.IN_WATER) {
|
||||
buoyancy = (this.waterLevel - getBoundingBox().getMin(Axis.Y)) / vehicle.getBoundingBoxHeight();
|
||||
frictionMutiplier = 0.9F;
|
||||
} else if (this.status == Status.UNDER_FLOWING_WATER) {
|
||||
gravity = -7.0E-4D;
|
||||
frictionMutiplier = 0.9F;
|
||||
} else if (this.status == Status.UNDER_WATER) {
|
||||
buoyancy = 0.009999999776482582D;
|
||||
frictionMutiplier = 0.45F;
|
||||
} else if (this.status == Status.IN_AIR) {
|
||||
frictionMutiplier = 0.9F;
|
||||
} else if (this.status == Status.ON_LAND) {
|
||||
frictionMutiplier = this.landFriction;
|
||||
this.landFriction /= 2.0F;
|
||||
}
|
||||
|
||||
vehicle.setMotion(vehicle.getMotion().mul(frictionMutiplier, 1, frictionMutiplier).up((float) gravity));
|
||||
|
||||
this.deltaRotation *= frictionMutiplier;
|
||||
if (buoyancy > 0.0D) {
|
||||
Vector3f motion = vehicle.getMotion();
|
||||
vehicle.setMotion(Vector3f.from(
|
||||
motion.getX(),
|
||||
(float) (motion.getY() + buoyancy * (this.gravity / 0.65)) * 0.75f,
|
||||
motion.getZ()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float getWaterLevelAbove(final VehicleContext context) {
|
||||
BoundingBox aabb = getBoundingBox();
|
||||
|
||||
int minX = GenericMath.floor(aabb.getMin(Axis.X));
|
||||
int maxX = GenericMath.ceil(aabb.getMax(Axis.X));
|
||||
int minY = GenericMath.floor(aabb.getMax(Axis.Y));
|
||||
int maxY = GenericMath.ceil(aabb.getMax(Axis.Y) - this.lastYd);
|
||||
int minZ = GenericMath.floor(aabb.getMin(Axis.Z));
|
||||
int maxZ = GenericMath.ceil(aabb.getMax(Axis.Z));
|
||||
|
||||
for (int y = minY; y < maxY; y++) {
|
||||
float blockHeight = 0.0F;
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
for (int z = minZ; z < maxZ; z++) {
|
||||
final float fluidHeight = getLogicalFluidHeight(Fluid.WATER, context.getBlockId(x, y, z));
|
||||
if (fluidHeight > 0) {
|
||||
blockHeight = Math.max(blockHeight, fluidHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blockHeight < 1.0F) {
|
||||
return y + blockHeight;
|
||||
}
|
||||
}
|
||||
return (maxY + 1);
|
||||
}
|
||||
|
||||
private float getGroundFriction(final VehicleContext context) {
|
||||
BoundingBox boatShape = getBoundingBox().clone();
|
||||
// 0.001 high box extending downwards from the boat
|
||||
boatShape.setMiddleY(boatShape.getMin(Axis.Y) - 0.0005);
|
||||
boatShape.setSizeY(0.001);
|
||||
|
||||
if (boatShape.isEmpty()) {
|
||||
return Float.NaN;
|
||||
}
|
||||
|
||||
int x0 = GenericMath.floor(boatShape.getMin(Axis.X)) - 1;
|
||||
int x1 = GenericMath.ceil(boatShape.getMax(Axis.X)) + 1;
|
||||
int y0 = GenericMath.floor(boatShape.getMin(Axis.Y)) - 1;
|
||||
int y1 = GenericMath.ceil(boatShape.getMax(Axis.Y)) + 1;
|
||||
int z0 = GenericMath.floor(boatShape.getMin(Axis.Z)) - 1;
|
||||
int z1 = GenericMath.ceil(boatShape.getMax(Axis.Z)) + 1;
|
||||
|
||||
float friction = 0.0F;
|
||||
int count = 0;
|
||||
|
||||
for (int x = x0; x < x1; x++) {
|
||||
for (int z = z0; z < z1; z++) {
|
||||
int edges = ((x == x0 || x == x1 - 1) ? 1 : 0) + ((z == z0 || z == z1 - 1) ? 1 : 0);
|
||||
if (edges == 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int y = y0; y < y1; y++) {
|
||||
if (edges > 0 && !(y != y0 && y != y1 - 1)) {
|
||||
continue;
|
||||
}
|
||||
final BlockState state = context.getBlock(x, y, z);
|
||||
if (state.is(Blocks.LILY_PAD)) {
|
||||
continue;
|
||||
}
|
||||
final BlockCollision collision = BlockUtils.getCollision(state.javaId());
|
||||
if (collision == null || collision.getBoundingBoxes().length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (collision.checkIntersection(Vector3i.from(x, y, z), boatShape)) {
|
||||
friction += BlockStateValues.getSlipperiness(state);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return friction / count;
|
||||
}
|
||||
|
||||
private Status isUnderwater(final VehicleContext context) {
|
||||
BoundingBox boatShape = getBoundingBox().clone();
|
||||
double maxY = boatShape.getMax(Axis.Y) + 0.001D;
|
||||
|
||||
int x0 = GenericMath.floor(boatShape.getMin(Axis.X));
|
||||
int x1 = GenericMath.ceil(boatShape.getMax(Axis.X));
|
||||
int y0 = GenericMath.floor(boatShape.getMax(Axis.Y));
|
||||
int y1 = GenericMath.ceil(maxY);
|
||||
int z0 = GenericMath.floor(boatShape.getMin(Axis.Z));
|
||||
int z1 = GenericMath.ceil(boatShape.getMax(Axis.Z));
|
||||
|
||||
boolean underWater = false;
|
||||
for (int x = x0; x < x1; x++) {
|
||||
for (int y = y0; y < y1; y++) {
|
||||
for (int z = z0; z < z1; z++) {
|
||||
final float fluidHeight = getLogicalFluidHeight(Fluid.WATER, context.getBlockId(x, y, z));
|
||||
if (fluidHeight <= 0 || maxY > y + fluidHeight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fluidHeight == 1) {
|
||||
underWater = true;
|
||||
} else {
|
||||
return Status.UNDER_FLOWING_WATER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return underWater ? Status.UNDER_WATER : null;
|
||||
}
|
||||
|
||||
private boolean checkInWater(final VehicleContext context) {
|
||||
this.waterLevel = Double.MIN_VALUE;
|
||||
|
||||
final BoundingBox boatShape = getBoundingBox();
|
||||
int minX = GenericMath.floor(boatShape.getMin(Axis.X));
|
||||
int maxX = GenericMath.ceil(boatShape.getMax(Axis.X));
|
||||
int minY = GenericMath.floor(boatShape.getMin(Axis.Y));
|
||||
int maxY = GenericMath.ceil(boatShape.getMin(Axis.Y) + 0.001D);
|
||||
int minZ = GenericMath.floor(boatShape.getMin(Axis.Z));
|
||||
int maxZ = GenericMath.ceil(boatShape.getMax(Axis.Z));
|
||||
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
for (int y = minY; y < maxY; y++) {
|
||||
for (int z = minZ; z < maxZ; z++) {
|
||||
final float fluidHeight = getLogicalFluidHeight(Fluid.WATER, context.getBlockId(x, y, z));
|
||||
if (fluidHeight <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float height = y + fluidHeight;
|
||||
this.waterLevel = Math.max(height, this.waterLevel);
|
||||
if (boatShape.getMin(Axis.Y) < height) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Status getStatus(final VehicleContext context) {
|
||||
Status waterStatus = isUnderwater(context);
|
||||
if (waterStatus != null) {
|
||||
this.waterLevel = getBoundingBox().getMax(Axis.Y);
|
||||
return waterStatus;
|
||||
}
|
||||
if (checkInWater(context)) {
|
||||
return Status.IN_WATER;
|
||||
}
|
||||
float friction = getGroundFriction(context);
|
||||
if (friction > 0.0F) {
|
||||
this.landFriction = friction;
|
||||
return Status.ON_LAND;
|
||||
}
|
||||
return Status.IN_AIR;
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
IN_WATER, UNDER_WATER, UNDER_FLOWING_WATER, ON_LAND, IN_AIR;
|
||||
}
|
||||
}
|
||||
@@ -98,10 +98,9 @@ public class CamelVehicleComponent extends VehicleComponent<CamelEntity> {
|
||||
SessionPlayerEntity player = vehicle.getSession().getPlayerEntity();
|
||||
Vector3f inputVelocity = super.getInputVector(ctx, speed, input);
|
||||
float jumpStrength = player.getVehicleJumpStrength();
|
||||
player.setVehicleJumpStrength(0);
|
||||
|
||||
if (jumpStrength > 0) {
|
||||
player.setVehicleJumpStrength(0);
|
||||
|
||||
if (vehicle.isOnGround() && jumpStrength > 0) {
|
||||
if (jumpStrength >= 90) {
|
||||
jumpStrength = 1.0f;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.TrigMath;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.horse.SkeletonHorseEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
|
||||
|
||||
public class HorseVehicleComponent extends VehicleComponent<AbstractHorseEntity> {
|
||||
@Setter
|
||||
private float horseJumpStrength = 0.7f; // Not sent by vanilla Java server when spawned
|
||||
private int effectJumpBoost;
|
||||
@Setter
|
||||
private boolean allowStandSliding;
|
||||
|
||||
public HorseVehicleComponent(AbstractHorseEntity vehicle) {
|
||||
super(vehicle, 1.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickVehicle() {
|
||||
if (!vehicle.getFlag(EntityFlag.STANDING)) {
|
||||
this.allowStandSliding = false;
|
||||
}
|
||||
|
||||
super.tickVehicle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector3f getInputVector(VehicleComponent<AbstractHorseEntity>.VehicleContext ctx, float speed, Vector3f input) {
|
||||
SessionPlayerEntity player = vehicle.getSession().getPlayerEntity();
|
||||
float jumpLeapStrength = player.getVehicleJumpStrength();
|
||||
if (vehicle.isOnGround() && jumpLeapStrength == 0.0F && vehicle.getFlag(EntityFlag.STANDING) && !this.allowStandSliding) {
|
||||
return Vector3f.ZERO;
|
||||
}
|
||||
player.setVehicleJumpStrength(0);
|
||||
|
||||
Vector3f inputVelocity = super.getInputVector(ctx, speed, input);
|
||||
|
||||
if (vehicle.isOnGround() && jumpLeapStrength > 0) {
|
||||
if (jumpLeapStrength >= 90) {
|
||||
jumpLeapStrength = 1.0f;
|
||||
} else {
|
||||
jumpLeapStrength = 0.4f + 0.4f * jumpLeapStrength / 90.0f;
|
||||
}
|
||||
|
||||
float jumpStrength = this.horseJumpStrength * getJumpVelocityMultiplier(ctx) + (this.effectJumpBoost * 0.1f);
|
||||
inputVelocity = Vector3f.from(inputVelocity.getX(), jumpStrength, inputVelocity.getZ());
|
||||
|
||||
if (input.getZ() > 0.0) {
|
||||
inputVelocity = inputVelocity.add(-0.4F * TrigMath.sin(vehicle.getYaw() * 0.017453292F) * jumpLeapStrength, 0.0, 0.4F * TrigMath.cos(vehicle.getYaw() * 0.017453292F) * jumpLeapStrength);
|
||||
}
|
||||
}
|
||||
|
||||
return inputVelocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getWaterSlowDown() {
|
||||
return vehicle instanceof SkeletonHorseEntity ? 0.96f : super.getWaterSlowDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEffect(Effect effect, int effectAmplifier) {
|
||||
if (effect == Effect.JUMP_BOOST) {
|
||||
effectJumpBoost = effectAmplifier + 1;
|
||||
} else {
|
||||
super.setEffect(effect, effectAmplifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEffect(Effect effect) {
|
||||
if (effect == Effect.JUMP_BOOST) {
|
||||
effectJumpBoost = 0;
|
||||
} else {
|
||||
super.removeEffect(effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
@@ -59,7 +60,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeT
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
|
||||
|
||||
public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
public class VehicleComponent<T extends Entity & ClientVehicle> {
|
||||
private static final ObjectDoublePair<Fluid> EMPTY_FLUID_PAIR = ObjectDoublePair.of(Fluid.EMPTY, 0.0);
|
||||
private static final float MAX_LOGICAL_FLUID_HEIGHT = 8.0f / BlockStateValues.NUM_FLUID_LEVELS;
|
||||
private static final float BASE_SLIPPERINESS_CUBED = 0.6f * 0.6f * 0.6f;
|
||||
@@ -391,7 +392,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
// Mojmap: LivingEntity#travelInFluid
|
||||
protected void waterMovement(VehicleContext ctx) {
|
||||
double gravity = getGravity();
|
||||
float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier
|
||||
float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : getWaterSlowDown();
|
||||
double originalY = ctx.centerPos().getY();
|
||||
boolean falling = vehicle.getMotion().getY() <= 0;
|
||||
|
||||
@@ -426,6 +427,10 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
}
|
||||
}
|
||||
|
||||
protected float getWaterSlowDown() {
|
||||
return 0.8f;
|
||||
}
|
||||
|
||||
protected void lavaMovement(VehicleContext ctx, double lavaHeight) {
|
||||
double gravity = getGravity();
|
||||
double originalY = ctx.centerPos().getY();
|
||||
@@ -948,7 +953,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
// Reuse block cache if vehicle moved less than 1 block
|
||||
if (this.cachePos == null || this.cachePos.distanceSquared(this.centerPos) > 1) {
|
||||
BoundingBox box = boundingBox.clone();
|
||||
box.expand(2);
|
||||
box.expand(2.0001);
|
||||
|
||||
Vector3i min = box.getMin().toInt();
|
||||
Vector3i max = box.getMax().toInt();
|
||||
|
||||
@@ -194,6 +194,10 @@ public class BoundingBox implements Cloneable {
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getMax(Axis.X) - getMin(Axis.X) < 1.0E-7D || getMax(Axis.Y) - getMin(Axis.Y) < 1.0E-7D || getMax(Axis.Z) - getMin(Axis.Z) < 1.0E-7D;
|
||||
}
|
||||
|
||||
@SneakyThrows(CloneNotSupportedException.class)
|
||||
@Override
|
||||
public BoundingBox clone() {
|
||||
|
||||
@@ -577,6 +577,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
@Setter
|
||||
private boolean steeringRight;
|
||||
|
||||
/**
|
||||
* Stores whether the player is inside a client predicted vehicle.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean inClientPredictedVehicle;
|
||||
|
||||
/**
|
||||
* Store the last time the player interacted. Used to fix a right-click spam bug.
|
||||
* See <a href="https://github.com/GeyserMC/Geyser/issues/503">this</a> for context.
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.geysermc.geyser.entity.type.living.animal.nautilus.AbstractNautilusEn
|
||||
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.entity.vehicle.HorseVehicleComponent;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
@@ -70,6 +71,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
session.setClientTicks(packet.getTick());
|
||||
session.setInClientPredictedVehicle(packet.getInputData().contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE) && entity.getVehicle() != null);
|
||||
|
||||
boolean wasJumping = session.getInputCache().wasJumping();
|
||||
session.getInputCache().processInputs(entity, packet);
|
||||
@@ -213,7 +215,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
}
|
||||
|
||||
// Only set steering values when the vehicle is a boat and when the client is actually in it
|
||||
if (entity.getVehicle() instanceof BoatEntity && inputData.contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
|
||||
if (entity.getVehicle() instanceof BoatEntity && session.isInClientPredictedVehicle()) {
|
||||
boolean up = inputData.contains(PlayerAuthInputData.UP);
|
||||
// Yes. These are flipped. Welcome to Bedrock edition.
|
||||
// Hi random stranger. I am six days into updating for 1.21.3. How's it going?
|
||||
@@ -239,16 +241,19 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
if (vehicle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Should we also check for protocol version here? If yes then this should be test on multiple platform first.
|
||||
boolean inClientPredictedVehicle = packet.getInputData().contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE);
|
||||
if (vehicle instanceof ClientVehicle) {
|
||||
session.getPlayerEntity().setVehicleInput(packet.getMotion());
|
||||
}
|
||||
|
||||
boolean sendMovement = false;
|
||||
if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) {
|
||||
sendMovement = !(vehicle instanceof ClientVehicle);
|
||||
sendMovement = inClientPredictedVehicle;
|
||||
} else if (vehicle instanceof BoatEntity) {
|
||||
// The player is either the only or the front rider.
|
||||
sendMovement = vehicle.getPassengers().size() == 1 || session.getPlayerEntity().isRidingInFront();
|
||||
sendMovement = inClientPredictedVehicle && (vehicle.getPassengers().size() == 1 || session.getPlayerEntity().isRidingInFront());
|
||||
}
|
||||
|
||||
if ((vehicle instanceof AbstractHorseEntity || vehicle instanceof AbstractNautilusEntity) && !vehicle.getFlag(EntityFlag.HAS_DASH_COOLDOWN)) {
|
||||
@@ -270,6 +275,10 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
PlayerState.START_HORSE_JUMP, finalVehicleJumpStrength));
|
||||
session.getInputCache().setJumpingTicks(-10);
|
||||
session.getPlayerEntity().setVehicleJumpStrength(finalVehicleJumpStrength);
|
||||
|
||||
if (((AbstractHorseEntity) vehicle).getVehicleComponent() instanceof HorseVehicleComponent horseVehicleComponent) {
|
||||
horseVehicleComponent.setAllowStandSliding(true);
|
||||
}
|
||||
} else if (!wasJumping && holdingJump) {
|
||||
session.getInputCache().setJumpingTicks(0);
|
||||
session.getInputCache().setJumpScale(0);
|
||||
@@ -288,33 +297,24 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
if (sendMovement) {
|
||||
// We only need to determine onGround status this way for client predicted vehicles.
|
||||
// For other vehicle, Geyser already handle it in VehicleComponent or the Java server handle it.
|
||||
if (packet.getInputData().contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
|
||||
Vector3f position = vehicle.getPosition();
|
||||
Vector3f position = vehicle.getPosition();
|
||||
final BoundingBox box = new BoundingBox(
|
||||
position.down(vehicle instanceof BoatEntity ? vehicle.getDefinition().offset() : 0).up(vehicle.getBoundingBoxHeight() / 2f).toDouble(),
|
||||
vehicle.getBoundingBoxWidth(), vehicle.getBoundingBoxHeight(), vehicle.getBoundingBoxWidth()
|
||||
);
|
||||
|
||||
if (vehicle instanceof BoatEntity) {
|
||||
position = position.down(vehicle.getDefinition().offset());
|
||||
}
|
||||
|
||||
final BoundingBox box = new BoundingBox(
|
||||
position.up(vehicle.getBoundingBoxHeight() / 2f).toDouble(),
|
||||
vehicle.getBoundingBoxWidth(), vehicle.getBoundingBoxHeight(), vehicle.getBoundingBoxWidth()
|
||||
);
|
||||
|
||||
// Manually calculate the vertical collision ourselves, the VERTICAL_COLLISION input data is inaccurate inside a vehicle!
|
||||
Vector3d movement = session.getPlayerEntity().getLastTickEndVelocity().toDouble();
|
||||
Vector3d correctedMovement = session.getCollisionManager().correctMovementForCollisions(movement, box, true, false);
|
||||
|
||||
vehicle.setOnGround(correctedMovement.getY() != movement.getY() && session.getPlayerEntity().getLastTickEndVelocity().getY() < 0);
|
||||
}
|
||||
// Manually calculate the vertical collision ourselves, the VERTICAL_COLLISION input data is inaccurate inside a vehicle!
|
||||
Vector3d movement = session.getPlayerEntity().getLastTickEndVelocity().toDouble();
|
||||
Vector3d correctedMovement = session.getCollisionManager().correctMovementForCollisions(movement, box, true, false);
|
||||
vehicle.setOnGround(correctedMovement.getY() != movement.getY() && session.getPlayerEntity().getLastTickEndVelocity().getY() < 0);
|
||||
|
||||
Vector3f vehiclePosition = packet.getPosition();
|
||||
Vector2f vehicleRotation = packet.getVehicleRotation();
|
||||
if (vehicleRotation == null) {
|
||||
return; // If the client just got in or out of a vehicle for example. Or if this vehicle isn't client predicted.
|
||||
return; // If the client just got in or out of a vehicle for example.
|
||||
}
|
||||
|
||||
if (session.getWorldBorder().isPassingIntoBorderBoundaries(vehiclePosition, false)) {
|
||||
Vector3f position = vehicle.getPosition();
|
||||
if (vehicle instanceof BoatEntity boat) {
|
||||
// Undo the changes usually applied to the boat
|
||||
boat.moveAbsoluteWithoutAdjustments(position, vehicle.getYaw(), vehicle.isOnGround(), true);
|
||||
@@ -335,7 +335,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
vehicle.setPosition(vehiclePosition);
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(
|
||||
vehiclePosition.toDouble(),
|
||||
vehicleRotation.getY() - 90, vehiclePosition.getX(), // TODO I wonder if this is related to the horse spinning bugs...
|
||||
vehicle instanceof BoatEntity ? vehicleRotation.getY() - 90 : vehicleRotation.getY(), vehiclePosition.getX(),
|
||||
vehicle.isOnGround()
|
||||
);
|
||||
session.sendDownstreamGamePacket(moveVehiclePacket);
|
||||
|
||||
Reference in New Issue
Block a user