Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emulate client side vehicle movement #4648

Merged
merged 43 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ff0fe96
WIP client side vehicles
AJ-Ferguson Jun 15, 2023
8ffcde9
Merge remote-tracking branch 'refs/remotes/upstream/master' into clie…
AJ-Ferguson May 8, 2024
72309fb
Address reviews and remove use of Optional
AJ-Ferguson May 16, 2024
4417331
Only tick active vehicle
AJ-Ferguson May 16, 2024
967d0a9
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson May 16, 2024
b250874
Track world ticks
AJ-Ferguson May 17, 2024
8d72970
Fixes for Camel dash and pose transition
AJ-Ferguson May 17, 2024
021e3bf
Remove vehicle parameter
AJ-Ferguson May 17, 2024
91574ef
Merge remote-tracking branch 'upstream/dev' into client-vehicle
AJ-Ferguson May 17, 2024
b79a12e
Start using blocks refactor
AJ-Ferguson May 17, 2024
a21ca99
Update BlockRegistryPopulator
AJ-Ferguson May 17, 2024
66bdb3b
Merge remote-tracking branch 'upstream/dev' into client-vehicle
AJ-Ferguson May 18, 2024
fa7b9fe
Update blocks
AJ-Ferguson May 18, 2024
f490af0
Support step height attribute
AJ-Ferguson May 18, 2024
8147848
Merge remote-tracking branch 'upstream/dev' into client-vehicle
AJ-Ferguson May 20, 2024
30a37fa
Use climbable block tag and TrapDoorBlock
AJ-Ferguson May 20, 2024
16d766d
Lock camel rotation if stationary
AJ-Ferguson May 20, 2024
de5b1a2
Fix boost ticking
AJ-Ferguson May 20, 2024
62822e0
Keep cache of surrounding blocks
AJ-Ferguson May 22, 2024
376b7ea
Fix bug causing BoundingBox position to change in CollisionManager
AJ-Ferguson May 22, 2024
32d587a
Merge remote-tracking branch 'upstream/dev' into client-vehicle
AJ-Ferguson May 23, 2024
818bef0
Clamp user input
AJ-Ferguson May 23, 2024
b33a023
Support weaving status effect
AJ-Ferguson May 23, 2024
d3b4ad4
Merge remote-tracking branch 'upstream/dev' into client-vehicle
AJ-Ferguson May 24, 2024
1f84b62
Support gravity attribute
AJ-Ferguson May 25, 2024
c348928
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jun 11, 2024
e1aebca
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jun 14, 2024
1a6c70f
Piston support
AJ-Ferguson Jun 15, 2024
cdbb276
Tick boost for Pig and Strider if any player is controlling
AJ-Ferguson Jun 15, 2024
77c6d5d
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jun 17, 2024
97c42d3
Submodule
AJ-Ferguson Jun 17, 2024
d49a3ec
Address some reviews
AJ-Ferguson Jun 21, 2024
9a92206
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jun 21, 2024
727604f
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jun 22, 2024
c86b41d
Support world border
AJ-Ferguson Jul 2, 2024
264b925
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jul 2, 2024
3a18ca9
Optimize world border check
AJ-Ferguson Jul 3, 2024
4c95d52
Small optimizations
AJ-Ferguson Jul 9, 2024
dae371f
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jul 9, 2024
6441514
Add comments
AJ-Ferguson Jul 17, 2024
efe8bd6
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Jul 17, 2024
02b7477
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Aug 3, 2024
fd314de
Merge remote-tracking branch 'upstream/master' into client-vehicle
AJ-Ferguson Aug 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ public final class EntityDefinitions {
.type(EntityType.PIG)
.heightAndWidth(0.9f)
.addTranslator(MetadataType.BOOLEAN, (pigEntity, entityMetadata) -> pigEntity.setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(null) // Boost time
.addTranslator(MetadataType.INT, PigEntity::setBoost)
.build();
POLAR_BEAR = EntityDefinition.inherited(PolarBearEntity::new, ageableEntityBase)
.type(EntityType.POLAR_BEAR)
Expand All @@ -913,7 +913,7 @@ public final class EntityDefinitions {
STRIDER = EntityDefinition.inherited(StriderEntity::new, ageableEntityBase)
.type(EntityType.STRIDER)
.height(1.7f).width(0.9f)
.addTranslator(null) // Boost time
.addTranslator(MetadataType.INT, StriderEntity::setBoost)
.addTranslator(MetadataType.BOOLEAN, StriderEntity::setCold)
.addTranslator(MetadataType.BOOLEAN, StriderEntity::setSaddled)
.build();
Expand Down Expand Up @@ -954,7 +954,7 @@ public final class EntityDefinitions {
.type(EntityType.CAMEL)
.height(2.375f).width(1.7f)
.addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing)
.addTranslator(null) // Last pose change tick
.addTranslator(MetadataType.LONG, CamelEntity::setLastPoseTick)
.build();
HORSE = EntityDefinition.inherited(HorseEntity::new, abstractHorseEntityBase)
.type(EntityType.HORSE)
Expand Down
16 changes: 16 additions & 0 deletions core/src/main/java/org/geysermc/geyser/entity/type/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
Expand Down Expand Up @@ -223,6 +224,13 @@ public void moveRelative(double relX, double relY, double relZ, float yaw, float
}

public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
if (this instanceof ClientVehicle clientVehicle) {
AJ-Ferguson marked this conversation as resolved.
Show resolved Hide resolved
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();
Expand Down Expand Up @@ -458,6 +466,10 @@ public boolean setBoundingBoxHeight(float height) {
dirtyMetadata.put(EntityDataTypes.HEIGHT, boundingBoxHeight);

updatePassengerOffsets();

if (valid && this instanceof ClientVehicle clientVehicle) {
AJ-Ferguson marked this conversation as resolved.
Show resolved Hide resolved
clientVehicle.getVehicleComponent().setHeight(boundingBoxHeight);
}
return true;
}
return false;
Expand All @@ -467,6 +479,10 @@ public void setBoundingBoxWidth(float width) {
if (width != boundingBoxWidth) {
boundingBoxWidth = width;
dirtyMetadata.put(EntityDataTypes.WIDTH, boundingBoxWidth);

if (valid && this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setWidth(boundingBoxWidth);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
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.vehicle.ClientVehicle;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
Expand Down Expand Up @@ -400,9 +401,25 @@ protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newA
this.maxHealth = Math.max((float) AttributeUtils.calculateValue(javaAttribute), 1f);
newAttributes.add(createHealthAttribute());
}
case GENERIC_MOVEMENT_SPEED -> {
AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED);
newAttributes.add(attributeData);
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setMoveSpeed(attributeData.getValue());
}
}
case GENERIC_STEP_HEIGHT -> {
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setStepHeight((float) AttributeUtils.calculateValue(javaAttribute));
}
}
case GENERIC_GRAVITY -> {
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setGravity(AttributeUtils.calculateValue(javaAttribute));
}
}
case GENERIC_ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
case GENERIC_FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
case GENERIC_MOVEMENT_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED));
case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
case GENERIC_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,30 @@

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.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;

import java.util.UUID;

public class PigEntity extends AnimalEntity {
public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle {
private final BoostableVehicleComponent<PigEntity> vehicleComponent = new BoostableVehicleComponent<>(this, 1.0f);

public PigEntity(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);
Expand Down Expand Up @@ -84,4 +94,51 @@ protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemS
}
}
}

public void setBoost(IntEntityMetadata entityMetadata) {
vehicleComponent.startBoost(entityMetadata.getPrimitiveValue());
}

@Override
public void tick() {
PlayerEntity player = getPlayerPassenger();
if (player == session.getPlayerEntity()) {
if (session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK)) {
vehicleComponent.tickBoost();
}
} else if (player != null) { // getHand() for session player seems to always return air
ItemDefinition itemDefinition = session.getItemMappings().getStoredItems().carrotOnAStick().getBedrockDefinition();
if (player.getHand().getDefinition() == itemDefinition || player.getOffhand().getDefinition() == itemDefinition) {
vehicleComponent.tickBoost();
}
}
}

@Override
public VehicleComponent<?> getVehicleComponent() {
return vehicleComponent;
}

@Override
public Vector2f getAdjustedInput(Vector2f input) {
return Vector2f.UNIT_Y;
}

@Override
public float getVehicleSpeed() {
return vehicleComponent.getMoveSpeed() * 0.225f * vehicleComponent.getBoostMultiplier();
}

private @Nullable PlayerEntity getPlayerPassenger() {
if (getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) instanceof PlayerEntity playerEntity) {
return playerEntity;
}

return null;
}

@Override
public boolean isClientControlled() {
return getPlayerPassenger() == session.getPlayerEntity() && session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,33 @@

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.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
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.Hand;

import java.util.UUID;

public class StriderEntity extends AnimalEntity {
public class StriderEntity extends AnimalEntity implements Tickable, ClientVehicle {

private final BoostableVehicleComponent<StriderEntity> vehicleComponent = new BoostableVehicleComponent<>(this, 1.0f);
private boolean isCold = false;

public StriderEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
Expand Down Expand Up @@ -131,4 +141,56 @@ protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemS
}
}
}

public void setBoost(IntEntityMetadata entityMetadata) {
vehicleComponent.startBoost(entityMetadata.getPrimitiveValue());
}

@Override
public void tick() {
PlayerEntity player = getPlayerPassenger();
if (player == session.getPlayerEntity()) {
if (session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK)) {
vehicleComponent.tickBoost();
}
} else if (player != null) { // getHand() for session player seems to always return air
ItemDefinition itemDefinition = session.getItemMappings().getStoredItems().warpedFungusOnAStick().getBedrockDefinition();
if (player.getHand().getDefinition() == itemDefinition || player.getOffhand().getDefinition() == itemDefinition) {
vehicleComponent.tickBoost();
}
}
}

@Override
public VehicleComponent<?> getVehicleComponent() {
return vehicleComponent;
}

@Override
public Vector2f getAdjustedInput(Vector2f input) {
return Vector2f.UNIT_Y;
}

@Override
public float getVehicleSpeed() {
return vehicleComponent.getMoveSpeed() * (isCold ? 0.35f : 0.55f) * vehicleComponent.getBoostMultiplier();
}

private @Nullable PlayerEntity getPlayerPassenger() {
if (getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) instanceof PlayerEntity playerEntity) {
return playerEntity;
}

return null;
}

@Override
public boolean isClientControlled() {
return getPlayerPassenger() == session.getPlayerEntity() && session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK);
}

@Override
public boolean canWalkOnLava() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,36 @@

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;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.vehicle.CamelVehicleComponent;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
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.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.LongEntityMetadata;

import java.util.UUID;

public class CamelEntity extends AbstractHorseEntity {

public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
public static final float SITTING_HEIGHT_DIFFERENCE = 1.43F;

private final CamelVehicleComponent vehicleComponent = new CamelVehicleComponent(this);

public CamelEntity(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);

Expand Down Expand Up @@ -111,5 +121,58 @@ protected void setDimensions(Pose pose) {
}

public void setDashing(BooleanEntityMetadata entityMetadata) {
// Java sends true to show dash animation and start the dash cooldown,
// false ends the dash animation, not the cooldown.
// Bedrock shows dash animation if HAS_DASH_COOLDOWN is set and the camel is above ground
if (entityMetadata.getPrimitiveValue()) {
setFlag(EntityFlag.HAS_DASH_COOLDOWN, true);
vehicleComponent.startDashCooldown();
} else if (!isClientControlled()) { // Don't remove dash cooldown prematurely if client is controlling
setFlag(EntityFlag.HAS_DASH_COOLDOWN, false);
}
}

public void setLastPoseTick(LongEntityMetadata entityMetadata) {
// Tick is based on world time. If negative, the camel is sitting.
// Must be compared to world time to know if the camel is fully standing/sitting or transitioning.
vehicleComponent.setLastPoseTick(entityMetadata.getPrimitiveValue());
}

@Override
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_JUMP_STRENGTH) {
vehicleComponent.setHorseJumpStrength(attributeData.getValue());
}
return attributeData;
}

@Override
public VehicleComponent<?> getVehicleComponent() {
return vehicleComponent;
}

@Override
public Vector2f getAdjustedInput(Vector2f input) {
return input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f);
}

@Override
public boolean isClientControlled() {
return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity();
}

@Override
public float getVehicleSpeed() {
float moveSpeed = vehicleComponent.getMoveSpeed();
if (!getFlag(EntityFlag.HAS_DASH_COOLDOWN) && session.getPlayerEntity().getFlag(EntityFlag.SPRINTING)) {
return moveSpeed + 0.1f;
}
return moveSpeed;
}

@Override
public boolean canClimb() {
return false;
}
}
Loading