Skip to content

Commit 65fdb4c

Browse files
oryxel1AJ-Ferguson
andauthored
Add support for server-controlled boat/horse riding on 1.21.130+ (#6033)
* Initial work, fixed boat on 1.21.130. * Implement client-sided horse movement. * Don't allow jumping in water. * Fixed horse riding for older version, don't send movement in auth for boat. * Removed some code. * Oops, fixed jumping xD. * Fixed boat paddling animation. * Some boat fixes Fix buoyancy Handle relative move packets in Entity now; fixes vanilla movement Slightly expand vehicle block cache to account for floating point error; fixes cache misses * Address review. --------- Co-authored-by: AJ Ferguson <AJ-Ferguson@users.noreply.github.com>
1 parent e4c1140 commit 65fdb4c

File tree

12 files changed

+674
-55
lines changed

12 files changed

+674
-55
lines changed

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

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@
2626
package org.geysermc.geyser.entity.type;
2727

2828
import lombok.Getter;
29+
import org.cloudburstmc.math.vector.Vector2f;
2930
import org.cloudburstmc.math.vector.Vector3f;
3031
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
3132
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
3233
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
3334
import org.geysermc.geyser.entity.EntityDefinition;
3435
import org.geysermc.geyser.entity.EntityDefinitions;
36+
import org.geysermc.geyser.entity.vehicle.BoatVehicleComponent;
37+
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
38+
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
3539
import org.geysermc.geyser.session.GeyserSession;
3640
import org.geysermc.geyser.util.InteractionResult;
3741
import org.geysermc.geyser.util.InteractiveTag;
@@ -41,7 +45,7 @@
4145

4246
import java.util.UUID;
4347

44-
public class BoatEntity extends Entity implements Leashable, Tickable {
48+
public class BoatEntity extends Entity implements Tickable, Leashable, ClientVehicle {
4549

4650
/**
4751
* 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 {
5357
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
5458
"\"minecraft:flowing_water\"],\"simulate_waves\":false}";
5559

60+
private final BoatVehicleComponent vehicleComponent = new BoatVehicleComponent(this, 0);
61+
5662
private boolean isPaddlingLeft;
5763
private float paddleTimeLeft;
5864
private boolean isPaddlingRight;
@@ -67,8 +73,8 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
6773

6874
private long leashHolderBedrockId = -1;
6975

70-
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
71-
private final float ROWING_SPEED = 0.1f;
76+
// 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.
77+
private final float ROWING_SPEED = 0.04f;
7278

7379
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) {
7480
// Initial rotation is incorrect
@@ -192,8 +198,13 @@ public void tick() {
192198
// For packet timing accuracy, we'll send the packets here, as that's what Java Edition 1.21.3 does.
193199
ServerboundPaddleBoatPacket steerPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
194200
session.sendDownstreamGamePacket(steerPacket);
195-
return;
201+
202+
// 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!
203+
if (session.isInClientPredictedVehicle()) {
204+
return;
205+
}
196206
}
207+
197208
doTick = !doTick; // Run every other tick
198209
if (!doTick || passengers.isEmpty()) {
199210
return;
@@ -212,11 +223,35 @@ public void tick() {
212223
paddleTimeRight += ROWING_SPEED;
213224
dirtyMetadata.put(EntityDataTypes.ROW_TIME_RIGHT, paddleTimeRight);
214225
}
226+
227+
if (isPaddlingLeft || isPaddlingRight) {
228+
updateBedrockMetadata();
229+
}
215230
}
216231

217232
@Override
218233
public long leashHolderBedrockId() {
219-
return leashHolderBedrockId;
234+
return this.leashHolderBedrockId;
235+
}
236+
237+
@Override
238+
public VehicleComponent<?> getVehicleComponent() {
239+
return this.vehicleComponent;
240+
}
241+
242+
@Override
243+
public Vector3f getRiddenInput(Vector2f input) {
244+
return Vector3f.ZERO;
245+
}
246+
247+
@Override
248+
public float getVehicleSpeed() {
249+
return 0;
250+
}
251+
252+
@Override
253+
public boolean isClientControlled() {
254+
return !session.isInClientPredictedVehicle() && !passengers.isEmpty() && this.session.getPlayerEntity() == passengers.get(0);
220255
}
221256

222257
/**

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.geysermc.geyser.entity.properties.type.PropertyType;
5454
import org.geysermc.geyser.entity.type.living.MobEntity;
5555
import org.geysermc.geyser.entity.type.player.PlayerEntity;
56+
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
5657
import org.geysermc.geyser.item.Items;
5758
import org.geysermc.geyser.item.type.Item;
5859
import org.geysermc.geyser.level.physics.BoundingBox;
@@ -258,6 +259,13 @@ public void moveRelative(double relX, double relY, double relZ, float yaw, float
258259
}
259260

260261
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
262+
if (this instanceof ClientVehicle clientVehicle) {
263+
if (clientVehicle.isClientControlled()) {
264+
return;
265+
}
266+
clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ);
267+
}
268+
261269
position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
262270

263271
MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket();

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -356,18 +356,6 @@ public InteractionResult interact(Hand hand) {
356356
return super.interact(hand);
357357
}
358358

359-
@Override
360-
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
361-
if (this instanceof ClientVehicle clientVehicle) {
362-
if (clientVehicle.isClientControlled()) {
363-
return;
364-
}
365-
clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ);
366-
}
367-
368-
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
369-
}
370-
371359
@Override
372360
public boolean setBoundingBoxHeight(float height) {
373361
if (valid && this instanceof ClientVehicle clientVehicle) {

core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727

2828
import org.checkerframework.checker.nullness.qual.NonNull;
2929
import org.checkerframework.checker.nullness.qual.Nullable;
30+
import org.cloudburstmc.math.vector.Vector2f;
3031
import org.cloudburstmc.math.vector.Vector3f;
32+
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
3133
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
3234
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
3335
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@@ -37,6 +39,9 @@
3739
import org.geysermc.geyser.entity.EntityDefinition;
3840
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
3941
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
42+
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
43+
import org.geysermc.geyser.entity.vehicle.HorseVehicleComponent;
44+
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
4045
import org.geysermc.geyser.input.InputLocksFlag;
4146
import org.geysermc.geyser.inventory.GeyserItemStack;
4247
import org.geysermc.geyser.item.Items;
@@ -47,12 +52,16 @@
4752
import org.geysermc.geyser.util.InteractionResult;
4853
import org.geysermc.geyser.util.InteractiveTag;
4954
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
55+
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute;
56+
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
5057
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
5158
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
5259

5360
import java.util.UUID;
5461

55-
public class AbstractHorseEntity extends AnimalEntity {
62+
public class AbstractHorseEntity extends AnimalEntity implements ClientVehicle {
63+
64+
private final HorseVehicleComponent vehicleComponent = new HorseVehicleComponent(this);
5665

5766
public AbstractHorseEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
5867
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
@@ -94,6 +103,15 @@ public void updateSaddled(boolean saddled) {
94103
}
95104
}
96105

106+
@Override
107+
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
108+
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
109+
if (javaAttribute.getType() == AttributeType.Builtin.JUMP_STRENGTH) {
110+
vehicleComponent.setHorseJumpStrength(attributeData.getValue());
111+
}
112+
return attributeData;
113+
}
114+
97115
@Override
98116
public boolean doesJumpDismount() {
99117
return !this.getFlag(EntityFlag.SADDLED);
@@ -309,4 +327,25 @@ protected boolean canUseSlot(EquipmentSlot slot) {
309327
return isAlive() && !isBaby() && getFlag(EntityFlag.TAMED);
310328
}
311329
}
330+
331+
@Override
332+
public VehicleComponent<?> getVehicleComponent() {
333+
return this.vehicleComponent;
334+
}
335+
336+
@Override
337+
public Vector3f getRiddenInput(Vector2f input) {
338+
input = input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f);
339+
return Vector3f.from(input.getX(), 0.0, input.getY());
340+
}
341+
342+
@Override
343+
public float getVehicleSpeed() {
344+
return vehicleComponent.getMoveSpeed();
345+
}
346+
347+
@Override
348+
public boolean isClientControlled() {
349+
return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity() && !session.isInClientPredictedVehicle();
350+
}
312351
}

core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
package org.geysermc.geyser.entity.type.living.animal.horse;
2727

28-
import org.cloudburstmc.math.vector.Vector2f;
2928
import org.checkerframework.checker.nullness.qual.Nullable;
3029
import org.cloudburstmc.math.vector.Vector3f;
3130
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
@@ -152,12 +151,6 @@ public VehicleComponent<?> getVehicleComponent() {
152151
return vehicleComponent;
153152
}
154153

155-
@Override
156-
public Vector3f getRiddenInput(Vector2f input) {
157-
input = input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f);
158-
return Vector3f.from(input.getX(), 0.0, input.getY());
159-
}
160-
161154
@Override
162155
public boolean isClientControlled() {
163156
return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity();

0 commit comments

Comments
 (0)