Skip to content

Commit 774d367

Browse files
committed
Horses are still weird, but boats are mostly working
1 parent 2025a2d commit 774d367

File tree

10 files changed

+138
-76
lines changed

10 files changed

+138
-76
lines changed

core/src/main/java/org/geysermc/geyser/entity/type/Entity.java

-5
Original file line numberDiff line numberDiff line change
@@ -701,9 +701,4 @@ public final void playEntityEvent(EntityEventType type, int data) {
701701
packet.setData(data);
702702
session.sendUpstreamPacket(packet);
703703
}
704-
705-
@SuppressWarnings("unchecked")
706-
public <I extends Entity> @Nullable I as(Class<I> entityClass) {
707-
return entityClass.isInstance(this) ? (I) this : null;
708-
}
709704
}

core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java

-1
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,6 @@ protected void moveVehicle(Vector3d javaPos) {
758758

759759
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), rotation.getX(), rotation.getY());
760760
vehicle.getSession().sendDownstreamPacket(moveVehiclePacket);
761-
vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis());
762761
}
763762

764763
protected double getGravity() {

core/src/main/java/org/geysermc/geyser/session/GeyserSession.java

-6
Original file line numberDiff line numberDiff line change
@@ -524,12 +524,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
524524
@Setter
525525
private boolean placedBucket;
526526

527-
/**
528-
* Used to send a ServerboundMoveVehiclePacket for every PlayerInputPacket after idling on a boat/horse for more than 100ms
529-
*/
530-
@Setter
531-
private long lastVehicleMoveTimestamp = System.currentTimeMillis();
532-
533527
/**
534528
* Counts how many ticks have occurred since an arm animation started.
535529
* -1 means there is no active arm swing; -2 means an arm swing will start in a tick.

core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java

+10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
package org.geysermc.geyser.session.cache;
2727

28+
import lombok.Getter;
29+
import lombok.Setter;
2830
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
2931
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
3032
import org.geysermc.geyser.session.GeyserSession;
@@ -37,6 +39,10 @@ public final class InputCache {
3739
private ServerboundPlayerInputPacket inputPacket = new ServerboundPlayerInputPacket(false, false, false, false, false, false, false);
3840
private boolean lastHorizontalCollision;
3941
private int ticksSinceLastMovePacket;
42+
@Getter @Setter
43+
private int jumpingTicks;
44+
@Getter @Setter
45+
private float jumpScale;
4046

4147
public InputCache(GeyserSession session) {
4248
this.session = session;
@@ -61,6 +67,10 @@ public void processInputs(PlayerAuthInputPacket packet) {
6167
}
6268
}
6369

70+
public boolean wasJumping() {
71+
return this.inputPacket.isJump();
72+
}
73+
6474
public void markPositionPacketSent() {
6575
this.ticksSinceLastMovePacket = 0;
6676
}

core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class TeleportCache {
4848
/**
4949
* How many move packets the teleport can be unconfirmed for before it gets resent to the client
5050
*/
51-
private static final int RESEND_THRESHOLD = 5;
51+
private static final int RESEND_THRESHOLD = 20; // Make it one full second with auth input
5252

5353
private final double x, y, z;
5454
private final float pitch, yaw;

core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMoveEntityAbsoluteTranslator.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,15 @@ public class BedrockMoveEntityAbsoluteTranslator extends PacketTranslator<MoveEn
4242

4343
@Override
4444
public void translate(GeyserSession session, MoveEntityAbsolutePacket packet) {
45-
session.setLastVehicleMoveTimestamp(System.currentTimeMillis());
46-
4745
Entity ridingEntity = session.getPlayerEntity().getVehicle();
4846
if (ridingEntity != null && session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), false)) {
4947
Vector3f position = Vector3f.from(ridingEntity.getPosition().getX(), packet.getPosition().getY(),
5048
ridingEntity.getPosition().getZ());
5149
if (ridingEntity instanceof BoatEntity) {
5250
// Undo the changes usually applied to the boat
53-
ridingEntity.as(BoatEntity.class)
54-
.moveAbsoluteWithoutAdjustments(position, ridingEntity.getYaw(),
55-
ridingEntity.isOnGround(), true);
51+
// ridingEntity.as(BoatEntity.class)
52+
// .moveAbsoluteWithoutAdjustments(position, ridingEntity.getYaw(),
53+
// ridingEntity.isOnGround(), true);
5654
} else {
5755
// This doesn't work if teleported is false
5856
ridingEntity.moveAbsolute(position,

core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java

-41
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,6 @@ public class BedrockPlayerInputTranslator extends PacketTranslator<PlayerInputPa
4444

4545
@Override
4646
public void translate(GeyserSession session, PlayerInputPacket packet) {
47-
// ServerboundPlayerInputPacket playerInputPacket = new ServerboundPlayerInputPacket(
48-
// packet.getInputMotion().getX(), packet.getInputMotion().getY(), packet.isJumping(), packet.isSneaking()
49-
// );
50-
//
51-
// session.sendDownstreamGamePacket(playerInputPacket);
5247

53-
session.getPlayerEntity().setVehicleInput(packet.getInputMotion());
54-
55-
// Bedrock only sends movement vehicle packets while moving
56-
// This allows horses to take damage while standing on magma
57-
Entity vehicle = session.getPlayerEntity().getVehicle();
58-
boolean sendMovement = false;
59-
if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) {
60-
sendMovement = vehicle.isOnGround();
61-
} else if (vehicle instanceof BoatEntity) {
62-
if (vehicle.getPassengers().size() == 1) {
63-
// The player is the only rider
64-
sendMovement = true;
65-
} else {
66-
// Check if the player is the front rider
67-
if (session.getPlayerEntity().isRidingInFront()) {
68-
sendMovement = true;
69-
}
70-
}
71-
}
72-
if (sendMovement) {
73-
long timeSinceVehicleMove = System.currentTimeMillis() - session.getLastVehicleMoveTimestamp();
74-
if (timeSinceVehicleMove >= 100) {
75-
Vector3f vehiclePosition = vehicle.getPosition();
76-
77-
if (vehicle instanceof BoatEntity && !vehicle.isOnGround()) {
78-
// Remove some Y position to prevents boats flying up
79-
vehiclePosition = vehiclePosition.down(vehicle.getDefinition().offset());
80-
}
81-
82-
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(
83-
vehiclePosition.getX(), vehiclePosition.getY(), vehiclePosition.getZ(),
84-
vehicle.getYaw() - 90, vehicle.getPitch()
85-
);
86-
session.sendDownstreamGamePacket(moveVehiclePacket);
87-
}
88-
}
8948
}
9049
}

core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java

+11-16
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
3232
import org.geysermc.geyser.entity.EntityDefinitions;
3333
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
34-
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
3534
import org.geysermc.geyser.level.physics.CollisionResult;
3635
import org.geysermc.geyser.session.GeyserSession;
3736
import org.geysermc.geyser.text.ChatColor;
@@ -48,17 +47,17 @@ static void translate(GeyserSession session, PlayerAuthInputPacket packet) {
4847
SessionPlayerEntity entity = session.getPlayerEntity();
4948
if (!session.isSpawned()) return;
5049

50+
// Ignore movement packets until Bedrock's position matches the teleported position
51+
if (session.getUnconfirmedTeleport() != null) {
52+
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0));
53+
return;
54+
}
55+
5156
boolean actualPositionChanged = !entity.getPosition().equals(packet.getPosition());
5257

5358
if (actualPositionChanged) {
5459
// Send book update before the player moves
5560
session.getBookEditCache().checkForSend();
56-
57-
// Ignore movement packets until Bedrock's position matches the teleported position
58-
if (session.getUnconfirmedTeleport() != null) {
59-
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0));
60-
return;
61-
}
6261
}
6362

6463
if (entity.getBedPosition() != null) {
@@ -72,9 +71,11 @@ static void translate(GeyserSession session, PlayerAuthInputPacket packet) {
7271
float pitch = packet.getRotation().getX();
7372
float headYaw = packet.getRotation().getY();
7473

75-
// shouldSendPositionReminder also increments a tick counter, so make sure it's always called.
76-
boolean positionChanged = session.getInputCache().shouldSendPositionReminder() || actualPositionChanged;
77-
boolean rotationChanged = entity.getYaw() != yaw || entity.getPitch() != pitch || entity.getHeadYaw() != headYaw;
74+
boolean hasVehicle = entity.getVehicle() != null;
75+
76+
// shouldSendPositionReminder also increments a tick counter, so make sure it's always called unless the player is on a vehicle.
77+
boolean positionChanged = !hasVehicle && session.getInputCache().shouldSendPositionReminder() || actualPositionChanged;
78+
boolean rotationChanged = hasVehicle || (entity.getYaw() != yaw || entity.getPitch() != pitch || entity.getHeadYaw() != headYaw);
7879

7980
if (session.getLookBackScheduledFuture() != null) {
8081
// Resend the rotation if it was changed by Geyser
@@ -99,12 +100,6 @@ static void translate(GeyserSession session, PlayerAuthInputPacket packet) {
99100

100101
session.sendDownstreamGamePacket(playerRotationPacket);
101102
} else if (positionChanged) {
102-
// World border collision will be handled by client vehicle
103-
if (!(entity.getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled())
104-
&& session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), true)) {
105-
return;
106-
}
107-
108103
if (isValidMove(session, entity.getPosition(), packet.getPosition())) {
109104
CollisionResult result = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT));
110105
if (result != null) { // A null return value cancels the packet

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

+102-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
2727

28+
import org.cloudburstmc.math.vector.Vector2f;
2829
import org.cloudburstmc.math.vector.Vector3f;
2930
import org.cloudburstmc.math.vector.Vector3i;
3031
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
@@ -36,19 +37,25 @@
3637
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
3738
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
3839
import org.geysermc.geyser.entity.EntityDefinitions;
40+
import org.geysermc.geyser.entity.type.BoatEntity;
3941
import org.geysermc.geyser.entity.type.Entity;
4042
import org.geysermc.geyser.entity.type.ItemFrameEntity;
43+
import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
44+
import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
4145
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
46+
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
4247
import org.geysermc.geyser.level.block.type.Block;
4348
import org.geysermc.geyser.session.GeyserSession;
4449
import org.geysermc.geyser.translator.protocol.PacketTranslator;
4550
import org.geysermc.geyser.translator.protocol.Translator;
4651
import org.geysermc.geyser.translator.protocol.bedrock.BedrockInventoryTransactionTranslator;
52+
import org.geysermc.geyser.util.MathUtils;
4753
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
4854
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
4955
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction;
5056
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
5157
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState;
58+
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
5259
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
5360
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
5461
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
@@ -57,13 +64,17 @@
5764
import java.util.Set;
5865

5966
@Translator(packet = PlayerAuthInputPacket.class)
60-
public class BedrockPlayerAuthInputTranslator extends PacketTranslator<PlayerAuthInputPacket> {
67+
public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<PlayerAuthInputPacket> {
6168

6269
@Override
6370
public void translate(GeyserSession session, PlayerAuthInputPacket packet) {
6471
SessionPlayerEntity entity = session.getPlayerEntity();
72+
73+
boolean wasJumping = session.getInputCache().wasJumping();
6574
session.getInputCache().processInputs(packet);
6675

76+
processVehicleInput(session, packet, wasJumping);
77+
6778
BedrockMovePlayerTranslator.translate(session, packet);
6879

6980
Set<PlayerAuthInputData> inputData = packet.getInputData();
@@ -202,4 +213,94 @@ private static void processItemUseTransaction(GeyserSession session, ItemUseTran
202213
session.getGeyser().getLogger().error("Unhandled item use transaction type!");
203214
}
204215
}
216+
217+
private static void processVehicleInput(GeyserSession session, PlayerAuthInputPacket packet, boolean wasJumping) {
218+
Entity vehicle = session.getPlayerEntity().getVehicle();
219+
if (vehicle == null) {
220+
return;
221+
}
222+
if (vehicle instanceof ClientVehicle) {
223+
session.getPlayerEntity().setVehicleInput(packet.getAnalogMoveVector());
224+
}
225+
226+
boolean sendMovement = false;
227+
if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) {
228+
sendMovement = vehicle.isOnGround();
229+
} else if (vehicle instanceof BoatEntity) {
230+
if (vehicle.getPassengers().size() == 1) {
231+
// The player is the only rider
232+
sendMovement = true;
233+
} else {
234+
// Check if the player is the front rider
235+
if (session.getPlayerEntity().isRidingInFront()) {
236+
sendMovement = true;
237+
}
238+
}
239+
}
240+
241+
if (vehicle instanceof AbstractHorseEntity) {
242+
// Behavior verified as of Java Edition 1.21.3
243+
int currentJumpingTicks = session.getInputCache().getJumpingTicks();
244+
if (currentJumpingTicks < 0) {
245+
session.getInputCache().setJumpingTicks(++currentJumpingTicks);
246+
if (currentJumpingTicks == 0) {
247+
session.getPlayerEntity().setVehicleJumpStrength(0);
248+
}
249+
}
250+
251+
boolean holdingJump = packet.getInputData().contains(PlayerAuthInputData.JUMPING);
252+
if (wasJumping && !holdingJump) {
253+
// Jump released
254+
// Yes, I'm fairly certain that entity ID is correct.
255+
session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(session.getPlayerEntity().getEntityId(),
256+
PlayerState.START_HORSE_JUMP, MathUtils.floor(session.getInputCache().getJumpScale() * 100f)));
257+
session.getInputCache().setJumpingTicks(-10);
258+
} else if (!wasJumping && holdingJump) {
259+
session.getInputCache().setJumpingTicks(0);
260+
session.getInputCache().setJumpScale(0);
261+
} else if (holdingJump) {
262+
session.getInputCache().setJumpingTicks(++currentJumpingTicks);
263+
if (currentJumpingTicks < 10) {
264+
session.getInputCache().setJumpScale(session.getInputCache().getJumpScale() * 0.1F);
265+
} else {
266+
session.getInputCache().setJumpScale(0.8f + 2.0f / (currentJumpingTicks - 9) * 0.1f);
267+
}
268+
}
269+
} else {
270+
session.getInputCache().setJumpScale(0);
271+
}
272+
273+
if (sendMovement) {
274+
Vector3f vehiclePosition = packet.getPosition();
275+
Vector2f vehicleRotation = packet.getVehicleRotation();
276+
if (vehicleRotation == null) {
277+
return; // If the client just got in or out of a vehicle for example.
278+
}
279+
280+
if (session.getWorldBorder().isPassingIntoBorderBoundaries(vehiclePosition, false)) {
281+
Vector3f position = vehicle.getPosition();
282+
if (vehicle instanceof BoatEntity boat) {
283+
// Undo the changes usually applied to the boat
284+
boat.moveAbsoluteWithoutAdjustments(position, vehicle.getYaw(), vehicle.isOnGround(), true);
285+
} else {
286+
// This doesn't work if teleported is false
287+
vehicle.moveAbsolute(position,
288+
vehicle.getYaw(), vehicle.getPitch(), vehicle.getHeadYaw(),
289+
vehicle.isOnGround(), true);
290+
}
291+
return;
292+
}
293+
294+
if (vehicle instanceof BoatEntity && !vehicle.isOnGround()) {
295+
// Remove some Y position to prevents boats flying up
296+
vehiclePosition = vehiclePosition.down(vehicle.getDefinition().offset());
297+
}
298+
299+
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(
300+
vehiclePosition.getX(), vehiclePosition.getY(), vehiclePosition.getZ(),
301+
vehicleRotation.getY() - 90, vehiclePosition.getX() // TODO I wonder if this is related to the horse spinning bugs...
302+
);
303+
session.sendDownstreamGamePacket(moveVehiclePacket);
304+
}
305+
}
205306
}

core/src/main/java/org/geysermc/geyser/util/MathUtils.java

+11
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ public static int ceil(float floatNumber) {
105105
return floatNumber > truncated ? truncated + 1 : truncated;
106106
}
107107

108+
/**
109+
* Round the given float to the previous whole number
110+
*
111+
* @param floatNumber Float to round
112+
* @return Rounded number
113+
*/
114+
public static int floor(float floatNumber) {
115+
int truncated = (int) floatNumber;
116+
return floatNumber < truncated ? truncated - 1 : truncated;
117+
}
118+
108119
/**
109120
* If number is greater than the max, set it to max, and if number is lower than low, set it to low.
110121
*

0 commit comments

Comments
 (0)