From ff0fe96f4b2a70676aeed7af80a08f24b5b2bc5a Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Wed, 14 Jun 2023 20:42:05 -0400 Subject: [PATCH 01/26] WIP client side vehicles --- .../geyser/entity/EntityDefinitions.java | 4 +- .../geysermc/geyser/entity/type/Entity.java | 16 + .../geyser/entity/type/LivingEntity.java | 9 +- .../entity/type/living/animal/PigEntity.java | 43 +- .../type/living/animal/StriderEntity.java | 48 +- .../type/living/animal/horse/CamelEntity.java | 74 +- .../type/player/SessionPlayerEntity.java | 12 + .../vehicle/BoostableVehicleComponent.java | 63 ++ .../entity/vehicle/CamelVehicleComponent.java | 79 ++ .../geyser/entity/vehicle/ClientVehicle.java | 54 ++ .../entity/vehicle/VehicleComponent.java | 766 ++++++++++++++++++ .../geyser/inventory/PlayerInventory.java | 11 + .../geysermc/geyser/level/JavaDimension.java | 8 +- .../geyser/level/block/BlockStateValues.java | 105 ++- .../geysermc/geyser/level/block/Fluid.java | 32 + .../geyser/level/physics/BoundingBox.java | 20 +- .../level/physics/CollisionManager.java | 52 +- .../geyser/level/physics/Direction.java | 1 + .../populator/BlockRegistryPopulator.java | 34 + .../translator/collision/BlockCollision.java | 11 + .../bedrock/BedrockPlayerInputTranslator.java | 2 + .../player/BedrockRiderJumpTranslator.java | 2 + .../entity/JavaMoveVehicleTranslator.java | 5 + .../entity/JavaRemoveMobEffectTranslator.java | 9 +- .../entity/JavaTeleportEntityTranslator.java | 5 + .../entity/JavaUpdateMobEffectTranslator.java | 9 +- 26 files changed, 1441 insertions(+), 33 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java create mode 100644 core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java create mode 100644 core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java create mode 100644 core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java create mode 100644 core/src/main/java/org/geysermc/geyser/level/block/Fluid.java diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index de58cc26c16..b164be058fe 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -826,7 +826,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) @@ -852,7 +852,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(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 5ca739f6178..2f65727b720 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -50,6 +50,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.EntityUtils; @@ -225,6 +226,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) { + if (clientVehicle.isLogicalSideForUpdatingMovement()) { + return; + } + clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ); + } + position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket(); @@ -443,6 +451,10 @@ public boolean setBoundingBoxHeight(float height) { dirtyMetadata.put(EntityDataTypes.HEIGHT, boundingBoxHeight); updatePassengerOffsets(); + + if (valid && this instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().setHeight(boundingBoxHeight); + } return true; } return false; @@ -452,6 +464,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); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 4f38297c39e..df1624d9ef5 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -51,6 +51,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; @@ -294,9 +295,15 @@ protected void updateAttribute(Attribute javaAttribute, List 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_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 HORSE_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH)); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index 91e94321c71..297b946aa8a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -25,10 +25,16 @@ package org.geysermc.geyser.entity.type.living.animal; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; +import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; 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.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.item.type.Item; @@ -40,7 +46,8 @@ import javax.annotation.Nonnull; import java.util.UUID; -public class PigEntity extends AnimalEntity { +public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle { + private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this); 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); @@ -83,4 +90,38 @@ protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemS } } } + + public void setBoost(IntEntityMetadata entityMetadata) { + vehicleComponent.startBoost(entityMetadata.getPrimitiveValue()); + } + + @Override + public void tick() { + vehicleComponent.tickVehicle(this); + } + + @Nonnull + @Override + public VehicleComponent getVehicleComponent() { + return vehicleComponent; + } + + @Override + public boolean isLogicalSideForUpdatingMovement() { + return getFlag(EntityFlag.SADDLED) + && !passengers.isEmpty() + && passengers.get(0) == session.getPlayerEntity() + && session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK); + } + + @Override + public float getSaddledSpeed() { + return vehicleComponent.getMoveSpeed() * 0.225f * vehicleComponent.getBoostMultiplier(); + } + + @Nonnull + @Override + public Vector2f getAdjustedInput(Vector2f input) { + return Vector2f.from(0, 1.0f); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index e58fa5aca8d..f558714b126 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -26,11 +26,17 @@ package org.geysermc.geyser.entity.type.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; +import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; 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.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.item.type.Item; @@ -42,8 +48,9 @@ import javax.annotation.Nonnull; import java.util.UUID; -public class StriderEntity extends AnimalEntity { +public class StriderEntity extends AnimalEntity implements Tickable, ClientVehicle { + private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this); 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) { @@ -130,4 +137,43 @@ protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemS } } } + + public void setBoost(IntEntityMetadata entityMetadata) { + vehicleComponent.startBoost(entityMetadata.getPrimitiveValue()); + } + + @Override + public void tick() { + vehicleComponent.tickVehicle(this); + } + + @Nonnull + @Override + public VehicleComponent getVehicleComponent() { + return vehicleComponent; + } + + @Nonnull + @Override + public Vector2f getAdjustedInput(Vector2f input) { + return Vector2f.from(0, 1.0f); + } + + @Override + public boolean isLogicalSideForUpdatingMovement() { + // Does not require saddle + return !passengers.isEmpty() + && passengers.get(0) == session.getPlayerEntity() + && session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK); + } + + @Override + public float getSaddledSpeed() { + return vehicleComponent.getMoveSpeed() * (isCold ? 0.35f : 0.55f) * vehicleComponent.getBoostMultiplier(); + } + + @Override + public boolean canWalkOnLava() { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index 4ce6f062b30..b672ff16a1f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -25,26 +25,38 @@ package org.geysermc.geyser.entity.type.living.animal.horse; +import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute; +import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +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; 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.type.Tickable; +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.item.Items; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; +import javax.annotation.Nonnull; import java.util.UUID; -public class CamelEntity extends AbstractHorseEntity { - +public class CamelEntity extends AbstractHorseEntity implements Tickable, ClientVehicle { public static final float SITTING_HEIGHT_DIFFERENCE = 1.43F; + private final CamelVehicleComponent vehicleComponent = new CamelVehicleComponent(this); + private int dashTicks; + 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); @@ -111,6 +123,64 @@ protected void setDimensions(Pose pose) { } public void setDashing(BooleanEntityMetadata entityMetadata) { + if (entityMetadata.getPrimitiveValue()) { + setFlag(EntityFlag.HAS_DASH_COOLDOWN, true); + dashTicks = 55; + } + } + + @Override + protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) { + AttributeData attributeData = super.calculateAttribute(javaAttribute, type); + if (javaAttribute.getType() == AttributeType.Builtin.HORSE_JUMP_STRENGTH) { + vehicleComponent.setHorseJumpStrength(attributeData.getValue()); + } + return attributeData; + } + + @Override + public void tick() { + vehicleComponent.tickVehicle(this); + if (dashTicks > 0 && --dashTicks == 0) { + setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); + updateBedrockMetadata(); + } + } + + @Nonnull + @Override + public VehicleComponent getVehicleComponent() { + return vehicleComponent; + } + @Nonnull + @Override + public Vector2f getAdjustedInput(Vector2f input) { + return input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f); + } + + @Override + public boolean isLogicalSideForUpdatingMovement() { + return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity(); + } + + @Override + public float getSaddledSpeed() { + float moveSpeed = vehicleComponent.getMoveSpeed(); + if (dashTicks == 0 && session.getPlayerEntity().getFlag(EntityFlag.SPRINTING)) { + return moveSpeed + 0.1f; + } + + return moveSpeed; + } + + @Override + public float getStepHeight() { + return 1.5f; + } + + @Override + public boolean canClimb() { + return false; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 27942924295..439ce920bc6 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -32,6 +32,8 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; +import lombok.Setter; +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; @@ -67,6 +69,16 @@ public class SessionPlayerEntity extends PlayerEntity { */ @Getter private boolean isRidingInFront; + /** + * Used when emulating client-side vehicles + */ + @Getter @Setter + private Vector2f vehicleInput = Vector2f.ZERO; + /** + * Used when emulating client-side vehicles + */ + @Getter @Setter + private int vehicleJumpStrength; /** * Used for villager inventory emulation. */ diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java new file mode 100644 index 00000000000..a7c512f8b95 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019-2023 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.TrigMath; +import org.geysermc.geyser.entity.type.LivingEntity; + +public class BoostableVehicleComponent extends VehicleComponent { + private int boostLength; + private int boostTicks = 1; + + public BoostableVehicleComponent(T vehicle) { + super(vehicle); + } + + public void startBoost(int boostLength) { + this.boostLength = boostLength; + this.boostTicks = 0; + } + + public float getBoostMultiplier() { + if (isBoosting()) { + return 1.0f + 1.15f * TrigMath.sin((float)boostTicks / (float)boostLength * TrigMath.PI); + } + return 1.0f; + } + + public boolean isBoosting() { + return boostTicks <= boostLength; + } + + @Override + public void tickVehicle(T vehicle) { + super.tickVehicle(vehicle); + + if (isBoosting()) { + boostTicks++; + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java new file mode 100644 index 00000000000..b32bde404d4 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019-2023 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 com.github.steveice10.mc.protocol.data.game.entity.Effect; +import org.cloudburstmc.math.vector.Vector3f; +import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; + +public class CamelVehicleComponent extends VehicleComponent { + private float horseJumpStrength = 0.42f; // This is the default for Camels. Not sent by vanilla Java server when spawned + private int jumpBoost; + + public CamelVehicleComponent(CamelEntity vehicle) { + super(vehicle); + } + + public void setHorseJumpStrength(float horseJumpStrength) { + this.horseJumpStrength = horseJumpStrength; + } + + @Override + public void setEffect(Effect effect, int effectAmplifier) { + if (effect == Effect.JUMP_BOOST) { + jumpBoost = effectAmplifier + 1; + } else { + super.setEffect(effect, effectAmplifier); + } + } + + @Override + public void removeEffect(Effect effect) { + if (effect == Effect.JUMP_BOOST) { + jumpBoost = 0; + } else { + super.removeEffect(effect); + } + } + + @Override + protected Vector3f getJumpVelocity(CamelEntity vehicle) { + SessionPlayerEntity player = vehicle.getSession().getPlayerEntity(); + float jumpStrength = player.getVehicleJumpStrength(); + + if (jumpStrength > 0) { + player.setVehicleJumpStrength(0); + + jumpStrength = jumpStrength >= 90 ? 1.0f : 0.4f + 0.4f * jumpStrength / 90.0f; + return Vector3f.createDirectionDeg(0, -player.getYaw()) + .mul(22.2222f * jumpStrength * moveSpeed * getVelocityMultiplier(vehicle)) + .up(1.4285f * jumpStrength * (horseJumpStrength * getJumpVelocityMultiplier(vehicle) + (jumpBoost * 0.1f))); + } + + return Vector3f.ZERO; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java new file mode 100644 index 00000000000..45786067141 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2023 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.vector.Vector2f; + +import javax.annotation.Nonnull; + +public interface ClientVehicle { + @Nonnull + VehicleComponent getVehicleComponent(); + + @Nonnull + Vector2f getAdjustedInput(Vector2f input); + + boolean isLogicalSideForUpdatingMovement(); + + float getSaddledSpeed(); + + default boolean canWalkOnLava() { + return false; + } + + default float getStepHeight() { + return 1.0f; + } + + default boolean canClimb() { + return true; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java new file mode 100644 index 00000000000..8ae13feec12 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -0,0 +1,766 @@ +/* + * Copyright (c) 2019-2023 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 com.github.steveice10.mc.protocol.data.game.entity.Effect; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket; +import it.unimi.dsi.fastutil.Pair; +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.data.entity.EntityFlag; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket; +import org.geysermc.erosion.util.BlockPositionIterator; +import org.geysermc.geyser.entity.attribute.GeyserAttributeType; +import org.geysermc.geyser.entity.type.LivingEntity; +import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.block.Fluid; +import org.geysermc.geyser.level.physics.BoundingBox; +import org.geysermc.geyser.level.physics.CollisionManager; +import org.geysermc.geyser.level.physics.Direction; +import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.type.BlockMapping; +import org.geysermc.geyser.translator.collision.BlockCollision; +import org.geysermc.geyser.translator.collision.SolidCollision; +import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.geyser.util.MathUtils; + +import java.util.Optional; + +public class VehicleComponent { + 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; + private static final float MIN_VELOCITY = 0.003f; + private static final float CLIMB_SPEED = 0.15f; + + protected final BoundingBox boundingBox; + + protected float moveSpeed; + protected int levitation; + protected boolean slowFalling; + + public VehicleComponent(T vehicle) { + double width = Double.parseDouble(Float.toString(vehicle.getBoundingBoxWidth())); + double height = Double.parseDouble(Float.toString(vehicle.getBoundingBoxHeight())); + boundingBox = new BoundingBox( + vehicle.getPosition().getX(), + vehicle.getPosition().getY() + height / 2, + vehicle.getPosition().getZ(), + width, height, width + ); + + moveSpeed = GeyserAttributeType.MOVEMENT_SPEED.getDefaultValue(); + } + + public void setWidth(float width) { + double doubleWidth = Double.parseDouble(Float.toString(width)); + boundingBox.setSizeX(doubleWidth); + boundingBox.setSizeZ(doubleWidth); + } + + public void setHeight(float height) { + boundingBox.setSizeY(Double.parseDouble(Float.toString(height))); + } + + public void moveAbsolute(double x, double y, double z) { + boundingBox.setMiddleX(x); + boundingBox.setMiddleY(y + boundingBox.getSizeY() / 2); + boundingBox.setMiddleZ(z); + } + + public void moveRelative(double x, double y, double z) { + boundingBox.translate(x, y, z); + } + + public void setEffect(Effect effect, int effectAmplifier) { + switch (effect) { + case LEVITATION -> levitation = effectAmplifier + 1; + case SLOW_FALLING -> slowFalling = true; + } + } + + public void removeEffect(Effect effect) { + switch (effect) { + case LEVITATION -> levitation = 0; + case SLOW_FALLING -> slowFalling = false; + } + } + + public void setMoveSpeed(float moveSpeed) { + this.moveSpeed = moveSpeed; + } + + public float getMoveSpeed() { + return moveSpeed; + } + + public void tickVehicle(T vehicle) { + if (!vehicle.isLogicalSideForUpdatingMovement()) { + return; + } + + Pair fluidHeight = updateFluidMovement(vehicle); + switch (fluidHeight.left()) { + case WATER -> waterMovement(vehicle); + case LAVA -> { + if (vehicle.canWalkOnLava() && BlockStateValues.getFluid(getBlockAt(vehicle, boundingBox.getBottomCenter().toInt())) == Fluid.LAVA) { + landMovement(vehicle); + } else { + lavaMovement(vehicle, fluidHeight.right()); + } + } + case EMPTY -> landMovement(vehicle); + } + } + + protected Pair updateFluidMovement(T vehicle) { + BoundingBox box = boundingBox.clone(); + box.expand(-0.001); + + Vector3d min = box.getMin(); + Vector3d max = box.getMax(); + + BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ()); + int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); + + double waterHeight = getFluidHeightAndApplyMovement(Fluid.WATER, 0.014, min.getY(), vehicle, iter, blocks); + double lavaHeight = getFluidHeightAndApplyMovement(Fluid.LAVA, vehicle.getSession().getDimensionType().ultraWarm() ? 0.007 : 0.007 / 3, min.getY(), vehicle, iter, blocks); + + if (lavaHeight > 0 && vehicle.getDefinition().entityType() == EntityType.STRIDER) { + Vector3i blockPos = boundingBox.getBottomCenter().toInt(); + if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || BlockStateValues.getFluid(getBlockAt(vehicle, blockPos.up())) == Fluid.LAVA) { + vehicle.setMotion(vehicle.getMotion().mul(0.5f).add(0, 0.05f, 0)); + } else { + vehicle.setOnGround(true); + } + } + + // Water movement has priority over lava movement + if (waterHeight > 0) { + return Pair.of(Fluid.WATER, waterHeight); + } + + if (lavaHeight > 0) { + return Pair.of(Fluid.LAVA, lavaHeight); + } + + return Pair.of(Fluid.EMPTY, 0.0); + } + + protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, double minY, T vehicle, BlockPositionIterator iter, int[] blocks) { + Vector3d totalVelocity = Vector3d.ZERO; + double maxFluidHeight = 0; + int fluidBlocks = 0; + + for (iter.reset(); iter.hasNext(); iter.next()) { + int blockId = blocks[iter.getIteration()]; + if (BlockStateValues.getFluid(blockId) != fluid) { + continue; + } + + Vector3i blockPos = Vector3i.from(iter.getX(), iter.getY(), iter.getZ()); + float worldFluidHeight = getWorldFluidHeight(fluid, blockId); + + double vehicleFluidHeight = blockPos.getY() + worldFluidHeight - minY; + if (vehicleFluidHeight < 0) { + continue; + } + + boolean flowBlocked = worldFluidHeight != 1; // This is only used for determining if a falling fluid should drag the vehicle downwards + Vector3d velocity = Vector3d.ZERO; + for (Direction direction : Direction.HORIZONTAL) { + Vector3i adjacentBlockPos = blockPos.add(direction.getUnitVector()); + int adjacentBlockId = getBlockAt(vehicle, adjacentBlockPos); + Fluid adjacentFluid = BlockStateValues.getFluid(adjacentBlockId); + + float fluidHeightDiff = 0; + if (adjacentFluid == fluid) { + fluidHeightDiff = getLogicalFluidHeight(fluid, blockId) - getLogicalFluidHeight(fluid, adjacentBlockId); + } else if (adjacentFluid == Fluid.EMPTY) { + // If the adjacent block is not a fluid and does not have collision, + // check if there is a fluid under it + BlockCollision adjacentBlockCollision = BlockUtils.getCollision(adjacentBlockId); + if (adjacentBlockCollision == null) { + float adjacentFluidHeight = getLogicalFluidHeight(fluid, getBlockAt(vehicle, adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); + if (adjacentFluidHeight != -1) { // Only care about same type of fluid + fluidHeightDiff = getLogicalFluidHeight(fluid, blockId) - (adjacentFluidHeight - MAX_LOGICAL_FLUID_HEIGHT); + } + } else if (!flowBlocked) { + flowBlocked = isFlowBlocked(fluid, adjacentBlockId); + } + } + + if (fluidHeightDiff != 0) { + velocity = velocity.add(direction.getUnitVector().toDouble().mul(fluidHeightDiff)); + } + } + + if (worldFluidHeight == 1) { // If falling fluid + // If flow is not blocked, check if it is blocked for the fluid above + if (!flowBlocked) { + Vector3i blockPosUp = blockPos.up(); + for (Direction direction : Direction.HORIZONTAL) { + flowBlocked = isFlowBlocked(fluid, getBlockAt(vehicle, blockPosUp.add(direction.getUnitVector()))); + if (flowBlocked) { + break; + } + } + } + + if (flowBlocked) { + velocity = javaNormalize(velocity).add(0.0, -6.0, 0.0); + } + } + + velocity = javaNormalize(velocity); + + maxFluidHeight = Math.max(vehicleFluidHeight, maxFluidHeight); + if (maxFluidHeight < 0.4) { + velocity = velocity.mul(maxFluidHeight); + } + + totalVelocity = totalVelocity.add(velocity); + fluidBlocks++; + } + + if (!totalVelocity.equals(Vector3d.ZERO)) { + Vector3f motion = vehicle.getMotion(); + + totalVelocity = javaNormalize(totalVelocity.mul(1.0 / fluidBlocks)); + totalVelocity = totalVelocity.mul(speed); + + if (totalVelocity.length() < 0.0045 && Math.abs(motion.getX()) < MIN_VELOCITY && Math.abs(motion.getZ()) < MIN_VELOCITY) { + totalVelocity = javaNormalize(totalVelocity).mul(0.0045); + } + + vehicle.setMotion(motion.add(totalVelocity.toFloat())); + } + + return maxFluidHeight; + } + + /** + * Java edition returns the zero vector if the length of the input vector is less than 0.0001 + */ + protected Vector3d javaNormalize(Vector3d vec) { + double len = vec.length(); + return len < 1.0E-4 ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len); + } + + protected int getBlockAt(T vehicle, Vector3i blockPos) { + return vehicle.getSession().getGeyser().getWorldManager().getBlockAt(vehicle.getSession(), blockPos); + } + + private float getWorldFluidHeight(Fluid fluidType, int blockId) { + return (float) switch (fluidType) { + case WATER -> BlockStateValues.getWaterHeight(blockId); + case LAVA -> BlockStateValues.getLavaHeight(blockId); + case EMPTY -> -1; + }; + } + + private float getLogicalFluidHeight(Fluid fluidType, int blockId) { + return Math.min(getWorldFluidHeight(fluidType, blockId), MAX_LOGICAL_FLUID_HEIGHT); + } + + private boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { + if (adjacentBlockId == BlockStateValues.JAVA_ICE_ID) { + return false; + } + + if (BlockStateValues.getFluid(adjacentBlockId) == fluid) { + return false; + } + + // TODO: supposed to check if the opposite face of the block touching the fluid is solid, instead of SolidCollision + return BlockUtils.getCollision(adjacentBlockId) instanceof SolidCollision; + } + + protected void waterMovement(T vehicle) { + float gravity = getGravity(vehicle); + float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier + double originalY = boundingBox.getBottomCenter().getY(); + boolean falling = vehicle.getMotion().getY() <= 0; + + // NOT IMPLEMENTED: depth strider and dolphins grace + + boolean horizontalCollision = travel(vehicle,0.02f); + if (horizontalCollision && isClimbing(vehicle)) { + vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.2f, vehicle.getMotion().getZ())); + } + + vehicle.setMotion(vehicle.getMotion().mul(drag, 0.8f, drag)); + vehicle.setMotion(getFluidGravity(vehicle, gravity, falling)); + + if (horizontalCollision && shouldApplyFluidJumpBoost(vehicle, originalY)) { + vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); + } + } + + protected void lavaMovement(T vehicle, double lavaHeight) { + float gravity = getGravity(vehicle); + double originalY = boundingBox.getBottomCenter().getY(); + boolean falling = vehicle.getMotion().getY() <= 0; + + boolean horizontalCollision = travel(vehicle, 0.02f); + + if (lavaHeight <= (boundingBox.getSizeY() * 0.85 < 0.4 ? 0.0 : 0.4)) { // Swim height + vehicle.setMotion(vehicle.getMotion().mul(0.5f, 0.8f, 0.5f)); + vehicle.setMotion(getFluidGravity(vehicle, gravity, falling)); + } else { + vehicle.setMotion(vehicle.getMotion().mul(0.5f)); + } + + vehicle.setMotion(vehicle.getMotion().down(gravity / 4.0f)); + + if (horizontalCollision && shouldApplyFluidJumpBoost(vehicle, originalY)) { + vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); + } + } + + protected void landMovement(T vehicle) { + float gravity = getGravity(vehicle); + float slipperiness = BlockStateValues.getSlipperiness(getBlockAt(vehicle, getVelocityAffectingPos(vehicle))); + float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; + float speed = vehicle.getSaddledSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); + + boolean horizontalCollision = travel(vehicle, speed); + if (isClimbing(vehicle)) { + vehicle.setMotion(getClimbingSpeed(vehicle, horizontalCollision)); + // NOT IMPLEMENTED: climbing in powdered snow + } + + if (levitation > 0) { + vehicle.setMotion(vehicle.getMotion().up((0.05f * levitation - vehicle.getMotion().getY()) * 0.2f)); + } else { + vehicle.setMotion(vehicle.getMotion().down(gravity)); + // NOT IMPLEMENTED: slow fall when in unloaded chunk + } + + vehicle.setMotion(vehicle.getMotion().mul(drag, 0.98f, drag)); + } + + protected boolean shouldApplyFluidJumpBoost(T vehicle, double originalY) { + BoundingBox box = boundingBox.clone(); + box.translate(vehicle.getMotion().toDouble().up(0.6f - boundingBox.getBottomCenter().getY() + originalY)); + box.expand(-1.0E-7); + + BlockPositionIterator iter = vehicle.getSession().getCollisionManager().collidableBlocksIterator(box); + int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); + + for (iter.reset(); iter.hasNext(); iter.next()) { + int blockId = blocks[iter.getIteration()]; + + // Also check for fluids + BlockCollision blockCollision = BlockUtils.getCollision(blockId); + if (blockCollision == null && BlockStateValues.getFluid(blockId) != Fluid.EMPTY) { + blockCollision = CollisionManager.SOLID_COLLISION; + } + + if (blockCollision != null && blockCollision.checkIntersection(iter.getX(), iter.getY(), iter.getZ(), box)) { + return false; + } + } + + return true; + } + + protected Vector3f getClimbingSpeed(T vehicle, boolean horizontalCollision) { + Vector3f motion = vehicle.getMotion(); + return Vector3f.from( + MathUtils.clamp(motion.getX(), -CLIMB_SPEED, CLIMB_SPEED), + horizontalCollision ? 0.2f : Math.max(motion.getY(), -CLIMB_SPEED), + MathUtils.clamp(motion.getZ(), -CLIMB_SPEED, CLIMB_SPEED) + ); + } + + protected Vector3f getFluidGravity(T vehicle, float gravity, boolean falling) { + Vector3f motion = vehicle.getMotion(); + if (vehicle.getFlag(EntityFlag.HAS_GRAVITY) && !vehicle.getFlag(EntityFlag.SPRINTING)) { + float newY = motion.getY() - gravity / 16; + if (falling && Math.abs(motion.getY() - 0.005f) >= MIN_VELOCITY && Math.abs(newY) < MIN_VELOCITY) { + newY = -MIN_VELOCITY; + } + return Vector3f.from(motion.getX(), newY, motion.getZ()); + } + return motion; + } + + protected Optional getBlockMovementMultiplier(T vehicle) { + BoundingBox box = boundingBox.clone(); + box.expand(-1.0E-7); + + Vector3i min = box.getMin().toInt(); + Vector3i max = box.getMax().toInt(); + + BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); + int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); + + // Iterate backwards + for (int i = blocks.length - 1; i >= 0; i--) { + String cleanIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blocks[i], BlockMapping.AIR).getCleanJavaIdentifier(); + + Vector3f multiplier = switch (cleanIdentifier) { + case "minecraft:cobweb" -> Vector3f.from(0.25f, 0.05f, 0.25f); + case "minecraft:powder_snow" -> Vector3f.from(0.9f, 1.5f, 0.9f); + case "minecraft:sweet_berry_bush" -> Vector3f.from(0.8f, 0.75f, 0.8f); + default -> null; + }; + + if (multiplier != null) { + return Optional.of(multiplier); + } + } + + return Optional.empty(); + } + + protected void applyBlockCollisionEffects(T vehicle) { + BoundingBox box = boundingBox.clone(); + box.expand(-1.0E-7); + + Vector3i min = box.getMin().toInt(); + Vector3i max = box.getMax().toInt(); + + BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); + int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); + + for (iter.reset(); iter.hasNext(); iter.next()) { + int blockId = blocks[iter.getIteration()]; + + if (BlockStateValues.JAVA_HONEY_BLOCK_ID == blockId) { + onHoneyBlockCollision(vehicle); + } else if (BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID == blockId) { + onBubbleColumnCollision(vehicle, true); + } else if (BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID == blockId) { + onBubbleColumnCollision(vehicle, false); + } + } + } + + protected void onHoneyBlockCollision(T vehicle) { + if (vehicle.isOnGround() || vehicle.getMotion().getY() >= -0.08f) { + return; + } + + // NOT IMPLEMENTED: don't slide if inside the honey block + Vector3f motion = vehicle.getMotion(); + float mul = motion.getY() < -0.13f ? -0.05f / motion.getY() : 1; + vehicle.setMotion(Vector3f.from(motion.getX() * mul, -0.05f, motion.getZ() * mul)); + } + + protected void onBubbleColumnCollision(T vehicle, boolean drag) { + Vector3f motion = vehicle.getMotion(); + vehicle.setMotion(Vector3f.from( + motion.getX(), + drag ? Math.max(-0.3f, motion.getY() - 0.03f) : Math.min(0.7f, motion.getY() + 0.06f), + motion.getZ() + )); + } + + protected Vector3f getJumpVelocity(T vehicle) { + return Vector3f.ZERO; + } + + /** + * @return True if there was a horizontal collision + */ + protected boolean travel(T vehicle, float speed) { + Vector3f motion = vehicle.getMotion(); + + // Java only does this client side + motion = motion.mul(0.98f); + + motion = Vector3f.from( + Math.abs(motion.getX()) < MIN_VELOCITY ? 0 : motion.getX(), + Math.abs(motion.getY()) < MIN_VELOCITY ? 0 : motion.getY(), + Math.abs(motion.getZ()) < MIN_VELOCITY ? 0 : motion.getZ() + ); + + // TODO: isImmobile? set input to 0 and jump to false + + motion = motion.add(getInputVelocity(vehicle, speed)); + motion = motion.add(getJumpVelocity(vehicle)); + + Optional movementMultiplier = getBlockMovementMultiplier(vehicle); + if (movementMultiplier.isPresent()) { + motion = motion.mul(movementMultiplier.get()); + } + + Vector3d correctedMovement = vehicle.getSession().getCollisionManager().correctMovement( + motion.toDouble(), boundingBox, vehicle.isOnGround(), vehicle.getStepHeight(), true, vehicle.canWalkOnLava() + ); + + boundingBox.translate(correctedMovement); + Vector3d newPos = boundingBox.getBottomCenter(); + + // Non-zero values indicate a collision on that axis + Vector3d moveDiff = motion.toDouble().sub(correctedMovement); + + boolean onGround = moveDiff.getY() != 0 && motion.getY() < 0; + boolean horizontalCollision = moveDiff.getX() != 0 || moveDiff.getZ() != 0; + + boolean bounced = false; + if (onGround) { + Vector3i landingPos = newPos.sub(0, 0.2f, 0).toInt(); + int landingBlockId = getBlockAt(vehicle, landingPos); + + if (landingBlockId == BlockStateValues.JAVA_SLIME_BLOCK_ID) { + 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 (BlockStateValues.getBedColor(landingBlockId) != -1) { // If bed + 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.isPresent()) { + motion = Vector3f.ZERO; + } else { + motion = motion.mul( + moveDiff.getX() == 0 ? 1 : 0, + moveDiff.getY() == 0 || bounced ? 1 : 0, + moveDiff.getZ() == 0 ? 1 : 0 + ); + } + + // Send the new position to the bedrock client and java server + moveVehicle(vehicle, newPos, onGround); + vehicle.setMotion(motion); + + applyBlockCollisionEffects(vehicle); + + float velocityMultiplier = getVelocityMultiplier(vehicle); + vehicle.setMotion(vehicle.getMotion().mul(velocityMultiplier, 1.0f, velocityMultiplier)); + + return horizontalCollision; + } + + protected boolean isClimbing(T vehicle) { + if (!vehicle.canClimb()) { + return false; + } + + Vector3i blockPos = boundingBox.getBottomCenter().toInt(); + int blockId = getBlockAt(vehicle, blockPos); + + if (BlockStateValues.isClimbable(blockId)) { + return true; + } + + // Check if the vehicle is in an open trapdoor with a ladder of the same direction under it + Optional openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); + if (openTrapdoorDirection.isPresent()) { + Optional ladderDirection = BlockStateValues.getLadderDirection(getBlockAt(vehicle, blockPos.down())); + return ladderDirection.isPresent() && ladderDirection.get() == openTrapdoorDirection.get(); + } + + return false; + } + + protected Vector2f normalizeInput(Vector2f input) { + float lenSquared = input.lengthSquared(); + if (lenSquared < 1.0E-7) { + return Vector2f.ZERO; + } else if (lenSquared > 1.0) { + return input.normalize(); + } + return input; + } + + protected Vector3f getInputVelocity(T vehicle, float speed) { + Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); + input = input.mul(0.98f); + input = vehicle.getAdjustedInput(input); + input = normalizeInput(input); + input = input.mul(speed); + + float yaw = vehicle.getSession().getPlayerEntity().getYaw(); + float sin = TrigMath.sin(yaw * TrigMath.DEG_TO_RAD); + float cos = TrigMath.cos(yaw * TrigMath.DEG_TO_RAD); + return Vector3f.from(input.getX() * cos - input.getY() * sin, 0, input.getY() * cos + input.getX() * sin); + } + + protected void moveVehicle(T vehicle, Vector3d javaPos, boolean isOnGround) { + Vector3f bedrockPos = javaPos.toFloat(); + float yaw = vehicle.getSession().getPlayerEntity().getYaw(); + float pitch = vehicle.getSession().getPlayerEntity().getPitch() * 0.5f; + + MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket(); + moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId()); + + if (isOnGround) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND); + } + vehicle.setOnGround(isOnGround); + + 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()); + } + if (vehicle.getPosition().getZ() != bedrockPos.getZ()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z); + moveEntityDeltaPacket.setZ(bedrockPos.getZ()); + } + vehicle.setPosition(bedrockPos); + + if (vehicle.getYaw() != yaw) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW); + moveEntityDeltaPacket.setYaw(yaw); + vehicle.setYaw(yaw); + } + if (vehicle.getPitch() != pitch) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH); + moveEntityDeltaPacket.setPitch(pitch); + vehicle.setPitch(pitch); + } + if (vehicle.getHeadYaw() != yaw) { // Same as yaw + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW); + moveEntityDeltaPacket.setHeadYaw(yaw); + vehicle.setHeadYaw(yaw); + } + + if (!moveEntityDeltaPacket.getFlags().isEmpty()) { + vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket); + } + + ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), yaw, pitch); + vehicle.getSession().sendDownstreamPacket(moveVehiclePacket); + vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis()); + } + + protected float getGravity(T vehicle) { + if (!vehicle.getFlag(EntityFlag.HAS_GRAVITY)) { + return 0; + } + + if (vehicle.getMotion().getY() <= 0 && slowFalling) { + return 0.01f; + } + + return 0.08f; + } + + protected Optional getSupportingBlockPos(T vehicle) { + Vector3i result = null; + + if (vehicle.isOnGround()) { + Vector3d bottomCenter = boundingBox.getBottomCenter(); + + BoundingBox box = boundingBox.clone(); + box.extend(0, -1.0E-6, 0); // Extend slightly down + + Vector3i min = box.getMin().toInt(); + Vector3i max = box.getMax().toInt(); + + // Use minY as maxY + BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), min.getY(), max.getZ()); + int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); + + double minDistance = Double.MAX_VALUE; + for (iter.reset(); iter.hasNext(); iter.next()) { + Vector3i blockPos = Vector3i.from(iter.getX(), iter.getY(), iter.getZ()); + int blockId = blocks[iter.getIteration()]; + + BlockCollision blockCollision; + if (vehicle.canWalkOnLava()) { + blockCollision = vehicle.getSession().getCollisionManager().getCollisionLavaWalking(blockId, blockPos.getY(), boundingBox); + } else { + blockCollision = BlockUtils.getCollision(blockId); + } + + if (blockCollision != null && blockCollision.checkIntersection(blockPos, box)) { + double distance = bottomCenter.distanceSquared(blockPos.toDouble().add(0.5f, 0.5f, 0.5f)); + if (distance <= minDistance) { + minDistance = distance; + result = blockPos; + } + } + } + } + + return Optional.ofNullable(result); + } + + protected Vector3i getVelocityAffectingPos(T vehicle) { + Optional supportingBlockPos = getSupportingBlockPos(vehicle); + if (supportingBlockPos.isPresent()) { + Vector3i blockPos = supportingBlockPos.get(); + return Vector3i.from(blockPos.getX(), Math.floor(boundingBox.getBottomCenter().getY() - 0.500001f), blockPos.getZ()); + } else { + return vehicle.getPosition().sub(0, 0.500001f, 0).toInt(); + } + } + + protected float getVelocityMultiplier(T vehicle) { + int blockId = getBlockAt(vehicle, boundingBox.getBottomCenter().toInt()); + if (BlockStateValues.getWaterLevel(blockId) != -1 // getWaterLevel does not include waterlogged blocks + || blockId == BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID + || blockId == BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID) { + return 1.0f; + } + + if (blockId == BlockStateValues.JAVA_SOUL_SAND_ID || blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + return 0.4f; + } + + blockId = getBlockAt(vehicle, getVelocityAffectingPos(vehicle)); + if (blockId == BlockStateValues.JAVA_SOUL_SAND_ID || blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + return 0.4f; + } + + return 1.0f; + } + + protected float getJumpVelocityMultiplier(T vehicle) { + int blockId = getBlockAt(vehicle, boundingBox.getBottomCenter().toInt()); + if (blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + return 0.5f; + } + + blockId = getBlockAt(vehicle, getVelocityAffectingPos(vehicle)); + if (blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + return 0.5f; + } + + return 1.0f; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java b/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java index 7b1064c8ff3..55651adb09f 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java @@ -29,6 +29,7 @@ import lombok.Getter; import lombok.Setter; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.jetbrains.annotations.Range; @@ -62,6 +63,16 @@ public void setCursor(@Nonnull GeyserItemStack newCursor, GeyserSession session) cursor = newCursor; } + /** + * Checks if the player is holding the specified item in either hand + * + * @param item The item to look for + * @return If the player is holding the item in either hand + */ + public boolean isHolding(@Nonnull Item item) { + return getItemInHand().asItem() == item || getOffhand().asItem() == item; + } + public GeyserItemStack getItemInHand(@Nonnull Hand hand) { return hand == Hand.OFF_HAND ? getOffhand() : getItemInHand(); } diff --git a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java index a3f6b55e4c2..55cb6b6cbc9 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java +++ b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java @@ -35,8 +35,10 @@ * Represents the information we store from the current Java dimension * @param piglinSafe Whether piglins and hoglins are safe from conversion in this dimension. * This controls if they have the shaking effect applied in the dimension. + * @param ultraWarm If this dimension is ultrawarm. + * Used when calculating movement in lava for client-side vehicles. */ -public record JavaDimension(int minY, int maxY, boolean piglinSafe, double worldCoordinateScale) { +public record JavaDimension(int minY, int maxY, boolean piglinSafe, boolean ultraWarm, double worldCoordinateScale) { public static void load(CompoundTag tag, Map map) { for (CompoundTag dimension : JavaCodecUtil.iterateAsTag(tag.get("minecraft:dimension_type"))) { @@ -47,10 +49,12 @@ public static void load(CompoundTag tag, Map map) { // Set if piglins/hoglins should shake boolean piglinSafe = ((Number) elements.get("piglin_safe").getValue()).byteValue() != (byte) 0; + // Entities in lava move faster in ultrawarm dimensions + boolean ultraWarm = ((Number) elements.get("ultrawarm").getValue()).byteValue() != (byte) 0; // Load world coordinate scale for the world border double coordinateScale = ((Number) elements.get("coordinate_scale").getValue()).doubleValue(); - map.put((String) dimension.get("name").getValue(), new JavaDimension(minY, maxY, piglinSafe, coordinateScale)); + map.put((String) dimension.get("name").getValue(), new JavaDimension(minY, maxY, piglinSafe, ultraWarm, coordinateScale)); } } } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index 27fb539be41..983acd69b8e 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -39,6 +39,7 @@ import org.geysermc.geyser.util.collection.LecternHasBookMap; import java.util.Locale; +import java.util.Optional; /** * Used for block entities if the Java block state contains Bedrock block information. @@ -66,6 +67,10 @@ public final class BlockStateValues { private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap(); private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap(); private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap(); + private static final Int2IntMap LAVA_LEVEL = new Int2IntOpenHashMap(); + private static final IntSet ALL_CLIMBABLE = new IntOpenHashSet(); + private static final Int2ObjectMap LADDER_DIRECTION = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap OPEN_TRAPDOOR_DIRECTION = new Int2ObjectOpenHashMap<>(); public static final int JAVA_AIR_ID = 0; @@ -76,8 +81,12 @@ public final class BlockStateValues { public static int JAVA_SLIME_BLOCK_ID; public static int JAVA_SPAWNER_ID; public static int JAVA_WATER_ID; + public static int JAVA_BUBBLE_COLUMN_DRAG_ID; + public static int JAVA_BUBBLE_COLUMN_UPWARD_ID; + public static int JAVA_SOUL_SAND_ID; + public static int JAVA_ICE_ID; - public static final int NUM_WATER_LEVELS = 9; + public static final int NUM_FLUID_LEVELS = 9; /** * Determines if the block state contains Bedrock block information @@ -193,6 +202,13 @@ public static void storeBlockStateValues(String javaId, int javaBlockState, Json return; } + if (javaId.startsWith("minecraft:lava") && !javaId.contains("cauldron")) { + String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1); + int level = Integer.parseInt(strLevel); + LAVA_LEVEL.put(javaBlockState, level); + return; + } + if (javaId.startsWith("minecraft:jigsaw[orientation=")) { String blockStateData = javaId.substring(javaId.indexOf("orientation=") + "orientation=".length(), javaId.lastIndexOf('_')); Direction direction = Direction.valueOf(blockStateData.toUpperCase(Locale.ROOT)); @@ -208,6 +224,18 @@ public static void storeBlockStateValues(String javaId, int javaBlockState, Json if (javaId.contains("_cauldron") && !javaId.contains("water_")) { NON_WATER_CAULDRONS.add(javaBlockState); } + + if (javaId.contains("vine") || javaId.startsWith("minecraft:ladder") || javaId.startsWith("minecraft:scaffolding")) { + ALL_CLIMBABLE.add(javaBlockState); + } + + if (javaId.startsWith("minecraft:ladder")) { + LADDER_DIRECTION.put(javaBlockState, getBlockDirection(javaId)); + } + + if (javaId.contains("_trapdoor[") && javaId.contains("open=true")) { + OPEN_TRAPDOOR_DIRECTION.put(javaBlockState, getBlockDirection(javaId)); + } } /** @@ -466,6 +494,24 @@ public static byte getShulkerBoxDirection(int state) { return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1); } + /** + * Get the type of fluid from the block state. + * + * @param state BlockState of the block + * @return The type of fluid + */ + public static Fluid getFluid(int state) { + if (WATER_LEVEL.containsKey(state) || BlockRegistries.WATERLOGGED.get().get(state)) { + return Fluid.WATER; + } + + if (LAVA_LEVEL.containsKey(state)) { + return Fluid.LAVA; + } + + return Fluid.EMPTY; + } + /** * Get the level of water from the block state. * @@ -490,7 +536,7 @@ public static double getWaterHeight(int state) { waterLevel = 0; } if (waterLevel >= 0) { - double waterHeight = 1 - (waterLevel + 1) / ((double) NUM_WATER_LEVELS); + double waterHeight = 1 - (waterLevel + 1) / ((double) NUM_FLUID_LEVELS); // Falling water is a full block if (waterLevel >= 8) { waterHeight = 1; @@ -500,6 +546,39 @@ public static double getWaterHeight(int state) { return -1; } + /** + * Get the level of lava from the block state. + * + * @param state BlockState of the block + * @return The lava level or -1 if the block isn't lava + */ + public static int getLavaLevel(int state) { + return LAVA_LEVEL.getOrDefault(state, -1); + } + + /** + * Get the height of lava from the block state + * + * @param state BlockState of the block + * @return The lava height or -1 if the block does not contain lava + */ + public static double getLavaHeight(int state) { + int lavaLevel = BlockStateValues.getLavaLevel(state); + if (lavaLevel >= 0) { + double lavaHeight = 1 - (lavaLevel + 1) / ((double) NUM_FLUID_LEVELS); + // Falling lava is a full block + if (lavaLevel >= 8) { + lavaHeight = 1; + } + return lavaHeight; + } + return -1; + } + + public static boolean isClimbable(int state) { + return ALL_CLIMBABLE.contains(state); + } + /** * Get the slipperiness of a block. * This is used in ItemEntity to calculate the friction on an item as it slides across the ground @@ -517,6 +596,28 @@ public static float getSlipperiness(int state) { }; } + /** + * Get the direction of a ladder. + * Used when determining if an entity is climbing + * + * @param state BlockState of the block + * @return The ladder's direction, or empty if not a ladder + */ + public static Optional getLadderDirection(int state) { + return Optional.ofNullable(LADDER_DIRECTION.get(state)); + } + + /** + * Get the direction of an open trapdoor. + * Used when determining if an entity is climbing + * + * @param state BlockState of the block + * @return The open trapdoor's direction, or empty if not an open trapdoor + */ + public static Optional getOpenTrapdoorDirection(int state) { + return Optional.ofNullable(OPEN_TRAPDOOR_DIRECTION.get(state)); + } + private static Direction getBlockDirection(String javaId) { if (javaId.contains("down")) { return Direction.DOWN; diff --git a/core/src/main/java/org/geysermc/geyser/level/block/Fluid.java b/core/src/main/java/org/geysermc/geyser/level/block/Fluid.java new file mode 100644 index 00000000000..a9693bbf466 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/block/Fluid.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019-2023 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.level.block; + +public enum Fluid { + WATER, + LAVA, + EMPTY +} diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java index 8343babd0ef..650b1929f74 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java @@ -57,10 +57,24 @@ public void extend(double x, double y, double z) { sizeZ += Math.abs(z); } + public void expand(double x, double y, double z) { + sizeX += x; + sizeY += y; + sizeZ += z; + } + + public void translate(Vector3d translate) { + translate(translate.getX(), translate.getY(), translate.getZ()); + } + public void extend(Vector3d extend) { extend(extend.getX(), extend.getY(), extend.getZ()); } + public void expand(double expand) { + expand(expand, expand, expand); + } + public boolean checkIntersection(double offsetX, double offsetY, double offsetZ, BoundingBox otherBox) { return (Math.abs((middleX + offsetX) - otherBox.getMiddleX()) * 2 < (sizeX + otherBox.getSizeX())) && (Math.abs((middleY + offsetY) - otherBox.getMiddleY()) * 2 < (sizeY + otherBox.getSizeY())) && @@ -91,9 +105,9 @@ public Vector3d getBottomCenter() { private boolean checkOverlapInAxis(double xOffset, double yOffset, double zOffset, BoundingBox otherBox, Axis axis) { return switch (axis) { - case X -> Math.abs((middleX + xOffset) - otherBox.getMiddleX()) * 2 < (sizeX + otherBox.getSizeX()); - case Y -> Math.abs((middleY + yOffset) - otherBox.getMiddleY()) * 2 < (sizeY + otherBox.getSizeY()); - case Z -> Math.abs((middleZ + zOffset) - otherBox.getMiddleZ()) * 2 < (sizeZ + otherBox.getSizeZ()); + case X -> (sizeX + otherBox.getSizeX()) - Math.abs((middleX + xOffset) - otherBox.getMiddleX()) * 2 > CollisionManager.COLLISION_TOLERANCE; + case Y -> (sizeY + otherBox.getSizeY()) - Math.abs((middleY + yOffset) - otherBox.getMiddleY()) * 2 > CollisionManager.COLLISION_TOLERANCE; + case Z -> (sizeZ + otherBox.getSizeZ()) - Math.abs((middleZ + zOffset) - otherBox.getMiddleZ()) * 2 > CollisionManager.COLLISION_TOLERANCE; }; } diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index b983da8b4f2..5c21f80585e 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -41,7 +41,9 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.translator.collision.BlockCollision; +import org.geysermc.geyser.translator.collision.OtherCollision; import org.geysermc.geyser.translator.collision.ScaffoldingCollision; +import org.geysermc.geyser.translator.collision.SolidCollision; import org.geysermc.geyser.util.BlockUtils; import java.text.DecimalFormat; @@ -49,6 +51,8 @@ import java.util.Locale; public class CollisionManager { + public static final BlockCollision SOLID_COLLISION = new SolidCollision(""); + public static final BlockCollision FLUID_COLLISION = new OtherCollision(new BoundingBox[]{new BoundingBox(0.5, 0.25, 0.5, 1, 0.5, 1)}); private final GeyserSession session; @@ -264,13 +268,13 @@ public Vector3d correctPlayerMovement(Vector3d movement, boolean checkWorld, boo if (teleported || (!checkWorld && session.getPistonCache().getPistons().isEmpty())) { // There is nothing to check return movement; } - return correctMovement(movement, playerBoundingBox, session.getPlayerEntity().isOnGround(), PLAYER_STEP_UP, checkWorld); + return correctMovement(movement, playerBoundingBox, session.getPlayerEntity().isOnGround(), PLAYER_STEP_UP, checkWorld, false); } - public Vector3d correctMovement(Vector3d movement, BoundingBox boundingBox, boolean onGround, double stepUp, boolean checkWorld) { + public Vector3d correctMovement(Vector3d movement, BoundingBox boundingBox, boolean onGround, double stepUp, boolean checkWorld, boolean walkOnLava) { Vector3d adjustedMovement = movement; if (!movement.equals(Vector3d.ZERO)) { - adjustedMovement = correctMovementForCollisions(movement, boundingBox, checkWorld); + adjustedMovement = correctMovementForCollisions(movement, boundingBox, checkWorld, walkOnLava); } boolean verticalCollision = adjustedMovement.getY() != movement.getY(); @@ -279,14 +283,14 @@ public Vector3d correctMovement(Vector3d movement, BoundingBox boundingBox, bool onGround = onGround || (verticalCollision && falling); if (onGround && horizontalCollision) { Vector3d horizontalMovement = Vector3d.from(movement.getX(), 0, movement.getZ()); - Vector3d stepUpMovement = correctMovementForCollisions(horizontalMovement.up(stepUp), boundingBox, checkWorld); + Vector3d stepUpMovement = correctMovementForCollisions(horizontalMovement.up(stepUp), boundingBox, checkWorld, walkOnLava); BoundingBox stretchedBoundingBox = boundingBox.clone(); stretchedBoundingBox.extend(horizontalMovement); - double maxStepUp = correctMovementForCollisions(Vector3d.from(0, stepUp, 0), stretchedBoundingBox, checkWorld).getY(); + double maxStepUp = correctMovementForCollisions(Vector3d.from(0, stepUp, 0), stretchedBoundingBox, checkWorld, walkOnLava).getY(); if (maxStepUp < stepUp) { // The player collided with a block above them boundingBox.translate(0, maxStepUp, 0); - Vector3d adjustedStepUpMovement = correctMovementForCollisions(horizontalMovement, boundingBox, checkWorld); + Vector3d adjustedStepUpMovement = correctMovementForCollisions(horizontalMovement, boundingBox, checkWorld, walkOnLava); boundingBox.translate(0, -maxStepUp, 0); if (squaredHorizontalLength(adjustedStepUpMovement) > squaredHorizontalLength(stepUpMovement)) { @@ -297,7 +301,7 @@ public Vector3d correctMovement(Vector3d movement, BoundingBox boundingBox, bool if (squaredHorizontalLength(stepUpMovement) > squaredHorizontalLength(adjustedMovement)) { boundingBox.translate(stepUpMovement.getX(), stepUpMovement.getY(), stepUpMovement.getZ()); // Apply the player's remaining vertical movement - double verticalMovement = correctMovementForCollisions(Vector3d.from(0, movement.getY() - stepUpMovement.getY(), 0), boundingBox, checkWorld).getY(); + double verticalMovement = correctMovementForCollisions(Vector3d.from(0, movement.getY() - stepUpMovement.getY(), 0), boundingBox, checkWorld, walkOnLava).getY(); boundingBox.translate(-stepUpMovement.getX(), -stepUpMovement.getY(), -stepUpMovement.getZ()); stepUpMovement = stepUpMovement.up(verticalMovement); @@ -311,7 +315,7 @@ private double squaredHorizontalLength(Vector3d vector) { return vector.getX() * vector.getX() + vector.getZ() * vector.getZ(); } - private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox boundingBox, boolean checkWorld) { + private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox boundingBox, boolean checkWorld, boolean walkOnLava) { double movementX = movement.getX(); double movementY = movement.getY(); double movementZ = movement.getZ(); @@ -320,20 +324,20 @@ private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox bou movementBoundingBox.extend(movement); BlockPositionIterator iter = collidableBlocksIterator(movementBoundingBox); if (Math.abs(movementY) > CollisionManager.COLLISION_TOLERANCE) { - movementY = computeCollisionOffset(boundingBox, Axis.Y, movementY, iter, checkWorld); + movementY = computeCollisionOffset(boundingBox, Axis.Y, movementY, iter, checkWorld, walkOnLava); boundingBox.translate(0, movementY, 0); } boolean checkZFirst = Math.abs(movementZ) > Math.abs(movementX); if (checkZFirst && Math.abs(movementZ) > CollisionManager.COLLISION_TOLERANCE) { - movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, iter, checkWorld); + movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, iter, checkWorld, walkOnLava); boundingBox.translate(0, 0, movementZ); } if (Math.abs(movementX) > CollisionManager.COLLISION_TOLERANCE) { - movementX = computeCollisionOffset(boundingBox, Axis.X, movementX, iter, checkWorld); + movementX = computeCollisionOffset(boundingBox, Axis.X, movementX, iter, checkWorld, walkOnLava); boundingBox.translate(movementX, 0, 0); } if (!checkZFirst && Math.abs(movementZ) > CollisionManager.COLLISION_TOLERANCE) { - movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, iter, checkWorld); + movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, iter, checkWorld, walkOnLava); boundingBox.translate(0, 0, movementZ); } @@ -341,13 +345,21 @@ private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox bou return Vector3d.from(movementX, movementY, movementZ); } - private double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double offset, BlockPositionIterator iter, boolean checkWorld) { + private double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double offset, BlockPositionIterator iter, boolean checkWorld, boolean walkOnLava) { for (iter.reset(); iter.hasNext(); iter.next()) { int x = iter.getX(); int y = iter.getY(); int z = iter.getZ(); if (checkWorld) { - BlockCollision blockCollision = BlockUtils.getCollisionAt(session, x, y, z); + int blockId = session.getGeyser().getWorldManager().getBlockAt(session, x, y, z); + + BlockCollision blockCollision; + if (walkOnLava) { + blockCollision = getCollisionLavaWalking(blockId, y, boundingBox); + } else { + blockCollision = BlockUtils.getCollision(blockId); + } + if (blockCollision != null && !(blockCollision instanceof ScaffoldingCollision)) { offset = blockCollision.computeCollisionOffset(x, y, z, boundingBox, axis, offset); } @@ -360,6 +372,16 @@ private double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double return offset; } + /** + * @return the block collision appropriate for entities that can walk on lava (Strider) + */ + public BlockCollision getCollisionLavaWalking(int blockId, int blockY, BoundingBox boundingBox) { + if (BlockStateValues.getLavaLevel(blockId) == 0 && FLUID_COLLISION.isBelow(blockY, boundingBox)) { + return FLUID_COLLISION; + } + return BlockUtils.getCollision(blockId); + } + /** * @return true if the block located at the player's floor position plus 1 would intersect with the player, * were they not sneaking @@ -412,7 +434,7 @@ public boolean isWaterInEyes() { double eyeY = playerBoundingBox.getMiddleY() - playerBoundingBox.getSizeY() / 2d + session.getEyeHeight(); double eyeZ = playerBoundingBox.getMiddleZ(); - eyeY -= 1 / ((double) BlockStateValues.NUM_WATER_LEVELS); // Subtract the height of one water layer + eyeY -= 1 / ((double) BlockStateValues.NUM_FLUID_LEVELS); // Subtract the height of one water layer int blockID = session.getGeyser().getWorldManager().getBlockAt(session, GenericMath.floor(eyeX), GenericMath.floor(eyeY), GenericMath.floor(eyeZ)); double waterHeight = BlockStateValues.getWaterHeight(blockID); diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java b/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java index fa5201db949..61861ec28dc 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java @@ -39,6 +39,7 @@ public enum Direction { EAST(4, Vector3i.UNIT_X, Axis.X, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.EAST); public static final Direction[] VALUES = values(); + public static final Direction[] HORIZONTAL = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST}; private final int reversedId; @Getter diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index 10e6d808b3e..6333e83b838 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -270,6 +270,10 @@ private static void registerJavaBlocks() { int spawnerRuntimeId = -1; int uniqueJavaId = -1; int waterRuntimeId = -1; + int bubbleColumnUpwardRuntimeId = -1; + int bubbleColumnDragRuntimeId = -1; + int soulSandRuntimeId = -1; + int iceRuntimeId = -1; Iterator> blocksIterator = blocksJson.fields(); while (blocksIterator.hasNext()) { javaRuntimeId++; @@ -357,6 +361,16 @@ private static void registerJavaBlocks() { honeyBlockRuntimeId = javaRuntimeId; } else if (javaId.equals("minecraft:slime_block")) { slimeBlockRuntimeId = javaRuntimeId; + } else if (javaId.startsWith("minecraft:bubble_column")) { + if (javaId.contains("drag=true")) { + bubbleColumnDragRuntimeId = javaRuntimeId; + } else { + bubbleColumnUpwardRuntimeId = javaRuntimeId; + } + } else if (javaId.equals("minecraft:soul_sand")) { + soulSandRuntimeId = javaRuntimeId; + } else if (javaId.equals("minecraft:ice")) { + iceRuntimeId = javaRuntimeId; } } @@ -395,6 +409,26 @@ private static void registerJavaBlocks() { } BlockStateValues.JAVA_WATER_ID = waterRuntimeId; + if (bubbleColumnDragRuntimeId == -1) { + throw new AssertionError("Unable to find drag bubble column in palette"); + } + BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID = bubbleColumnDragRuntimeId; + + if (bubbleColumnUpwardRuntimeId == -1) { + throw new AssertionError("Unable to find upward bubble column in palette"); + } + BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID = bubbleColumnUpwardRuntimeId; + + if (soulSandRuntimeId == -1) { + throw new AssertionError("Unable to find soul sand in palette"); + } + BlockStateValues.JAVA_SOUL_SAND_ID = soulSandRuntimeId; + + if (iceRuntimeId == -1) { + throw new AssertionError("Unable to find ice in palette"); + } + BlockStateValues.JAVA_ICE_ID = iceRuntimeId; + BlockRegistries.CLEAN_JAVA_IDENTIFIERS.set(cleanIdentifiers.toArray(new String[0])); BLOCKS_JSON = blocksJson; diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java index f7e39718b85..f49b480fa68 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java @@ -166,4 +166,15 @@ public double computeCollisionOffset(double x, double y, double z, BoundingBox b } return offset; } + + public boolean isBelow(double y, BoundingBox boundingBox) { + double minY = boundingBox.getMiddleY() - boundingBox.getSizeY() / 2; + for (BoundingBox b : boundingBoxes) { + double offset = y + b.getMiddleY() + b.getSizeY() / 2 - minY; + if (offset > CollisionManager.COLLISION_TOLERANCE) { + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java index 03faca5f22e..e60994ea367 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java @@ -52,6 +52,8 @@ public void translate(GeyserSession session, PlayerInputPacket packet) { session.sendDownstreamPacket(playerInputPacket); + session.getPlayerEntity().setVehicleInput(packet.getInputMotion()); + // Bedrock only sends movement vehicle packets while moving // This allows horses to take damage while standing on magma Entity vehicle = session.getPlayerEntity().getVehicle(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockRiderJumpTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockRiderJumpTranslator.java index 0d0ec47037e..ed5356412d4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockRiderJumpTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockRiderJumpTranslator.java @@ -38,6 +38,8 @@ public class BedrockRiderJumpTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, RiderJumpPacket packet) { + session.getPlayerEntity().setVehicleJumpStrength(packet.getJumpStrength()); + Entity vehicle = session.getPlayerEntity().getVehicle(); if (vehicle instanceof AbstractHorseEntity) { ServerboundPlayerCommandPacket playerCommandPacket = new ServerboundPlayerCommandPacket(vehicle.getEntityId(), PlayerState.START_HORSE_JUMP, packet.getJumpStrength()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaMoveVehicleTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaMoveVehicleTranslator.java index bdb159633a6..7b4a42950aa 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaMoveVehicleTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaMoveVehicleTranslator.java @@ -28,6 +28,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundMoveVehiclePacket; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -40,6 +41,10 @@ public void translate(GeyserSession session, ClientboundMoveVehiclePacket packet Entity entity = session.getPlayerEntity().getVehicle(); if (entity == null) return; + if (entity instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().moveAbsolute(packet.getX(), packet.getY(), packet.getZ()); + } + entity.moveAbsolute(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), false, true); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaRemoveMobEffectTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaRemoveMobEffectTranslator.java index 52b771caa92..b6c991fb562 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaRemoveMobEffectTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaRemoveMobEffectTranslator.java @@ -28,6 +28,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundRemoveMobEffectPacket; import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -39,11 +40,15 @@ public class JavaRemoveMobEffectTranslator extends PacketTranslator Date: Thu, 16 May 2024 01:53:42 -0400 Subject: [PATCH 02/26] Address reviews and remove use of Optional --- .../geysermc/geyser/entity/type/Entity.java | 2 +- .../entity/type/living/animal/PigEntity.java | 6 +- .../type/living/animal/StriderEntity.java | 6 +- .../type/living/animal/horse/CamelEntity.java | 4 +- .../geyser/entity/vehicle/ClientVehicle.java | 4 +- .../entity/vehicle/VehicleComponent.java | 67 ++++++++++--------- .../geyser/level/block/BlockStateValues.java | 14 ++-- 7 files changed, 52 insertions(+), 51 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 81c38833c79..4733adc549c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -221,7 +221,7 @@ 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) { - if (clientVehicle.isLogicalSideForUpdatingMovement()) { + if (clientVehicle.isClientControlled()) { return; } clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index a260098eea0..f32e3315176 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -108,7 +108,7 @@ public VehicleComponent getVehicleComponent() { } @Override - public boolean isLogicalSideForUpdatingMovement() { + public boolean isClientControlled() { return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity() @@ -116,12 +116,12 @@ public boolean isLogicalSideForUpdatingMovement() { } @Override - public float getSaddledSpeed() { + public float getVehicleSpeed() { return vehicleComponent.getMoveSpeed() * 0.225f * vehicleComponent.getBoostMultiplier(); } @Override public Vector2f getAdjustedInput(Vector2f input) { - return Vector2f.from(0, 1.0f); + return Vector2f.UNIT_Y; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index c08b04471b3..207f686d148 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -156,11 +156,11 @@ public void tick() { @Override public @NonNull Vector2f getAdjustedInput(Vector2f input) { - return Vector2f.from(0, 1.0f); + return Vector2f.UNIT_Y; } @Override - public boolean isLogicalSideForUpdatingMovement() { + public boolean isClientControlled() { // Does not require saddle return !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity() @@ -168,7 +168,7 @@ public boolean isLogicalSideForUpdatingMovement() { } @Override - public float getSaddledSpeed() { + public float getVehicleSpeed() { return vehicleComponent.getMoveSpeed() * (isCold ? 0.35f : 0.55f) * vehicleComponent.getBoostMultiplier(); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index 4a1692927b8..5f1925ec649 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -157,12 +157,12 @@ public Vector2f getAdjustedInput(Vector2f input) { } @Override - public boolean isLogicalSideForUpdatingMovement() { + public boolean isClientControlled() { return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity(); } @Override - public float getSaddledSpeed() { + public float getVehicleSpeed() { float moveSpeed = vehicleComponent.getMoveSpeed(); if (dashTicks == 0 && session.getPlayerEntity().getFlag(EntityFlag.SPRINTING)) { return moveSpeed + 0.1f; diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index 42b85e5ed53..b0229a9a319 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -32,9 +32,9 @@ public interface ClientVehicle { Vector2f getAdjustedInput(Vector2f input); - boolean isLogicalSideForUpdatingMovement(); + boolean isClientControlled(); - float getSaddledSpeed(); + float getVehicleSpeed(); default boolean canWalkOnLava() { return false; diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index d5eeffd7f2c..48317b8ca62 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -25,7 +25,8 @@ package org.geysermc.geyser.entity.vehicle; -import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.objects.ObjectDoublePair; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.TrigMath; import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3d; @@ -51,14 +52,21 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket; -import java.util.Optional; +import java.util.Map; public class VehicleComponent { + private static final ObjectDoublePair 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; private static final float MIN_VELOCITY = 0.003f; private static final float CLIMB_SPEED = 0.15f; + private static final Map MOVEMENT_MULTIPLIERS = Map.of( + "minecraft:cobweb", Vector3f.from(0.25f, 0.05f, 0.25f), + "minecraft:powder_snow", Vector3f.from(0.9f, 1.5f, 0.9f), + "minecraft:sweet_berry_bush", Vector3f.from(0.8f, 0.75f, 0.8f) + ); + protected final BoundingBox boundingBox; protected float moveSpeed; @@ -121,25 +129,25 @@ public float getMoveSpeed() { } public void tickVehicle(T vehicle) { - if (!vehicle.isLogicalSideForUpdatingMovement()) { + if (!vehicle.isClientControlled()) { return; } - Pair fluidHeight = updateFluidMovement(vehicle); + ObjectDoublePair fluidHeight = updateFluidMovement(vehicle); switch (fluidHeight.left()) { case WATER -> waterMovement(vehicle); case LAVA -> { if (vehicle.canWalkOnLava() && BlockStateValues.getFluid(getBlockAt(vehicle, boundingBox.getBottomCenter().toInt())) == Fluid.LAVA) { landMovement(vehicle); } else { - lavaMovement(vehicle, fluidHeight.right()); + lavaMovement(vehicle, fluidHeight.rightDouble()); } } case EMPTY -> landMovement(vehicle); } } - protected Pair updateFluidMovement(T vehicle) { + protected ObjectDoublePair updateFluidMovement(T vehicle) { BoundingBox box = boundingBox.clone(); box.expand(-0.001); @@ -163,14 +171,14 @@ protected Pair updateFluidMovement(T vehicle) { // Water movement has priority over lava movement if (waterHeight > 0) { - return Pair.of(Fluid.WATER, waterHeight); + return ObjectDoublePair.of(Fluid.WATER, waterHeight); } if (lavaHeight > 0) { - return Pair.of(Fluid.LAVA, lavaHeight); + return ObjectDoublePair.of(Fluid.LAVA, lavaHeight); } - return Pair.of(Fluid.EMPTY, 0.0); + return EMPTY_FLUID_PAIR; } protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, double minY, T vehicle, BlockPositionIterator iter, int[] blocks) { @@ -348,7 +356,7 @@ protected void landMovement(T vehicle) { float gravity = getGravity(vehicle); float slipperiness = BlockStateValues.getSlipperiness(getBlockAt(vehicle, getVelocityAffectingPos(vehicle))); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; - float speed = vehicle.getSaddledSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); + float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); boolean horizontalCollision = travel(vehicle, speed); if (isClimbing(vehicle)) { @@ -412,7 +420,7 @@ protected Vector3f getFluidGravity(T vehicle, float gravity, boolean falling) { return motion; } - protected Optional getBlockMovementMultiplier(T vehicle) { + protected @Nullable Vector3f getBlockMovementMultiplier(T vehicle) { BoundingBox box = boundingBox.clone(); box.expand(-1.0E-7); @@ -425,20 +433,14 @@ protected Optional getBlockMovementMultiplier(T vehicle) { // Iterate backwards for (int i = blocks.length - 1; i >= 0; i--) { String cleanIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blocks[i], BlockMapping.DEFAULT).getCleanJavaIdentifier(); - - Vector3f multiplier = switch (cleanIdentifier) { - case "minecraft:cobweb" -> Vector3f.from(0.25f, 0.05f, 0.25f); - case "minecraft:powder_snow" -> Vector3f.from(0.9f, 1.5f, 0.9f); - case "minecraft:sweet_berry_bush" -> Vector3f.from(0.8f, 0.75f, 0.8f); - default -> null; - }; + Vector3f multiplier = MOVEMENT_MULTIPLIERS.get(cleanIdentifier); if (multiplier != null) { - return Optional.of(multiplier); + return multiplier; } } - return Optional.empty(); + return null; } protected void applyBlockCollisionEffects(T vehicle) { @@ -508,9 +510,9 @@ protected boolean travel(T vehicle, float speed) { motion = motion.add(getInputVelocity(vehicle, speed)); motion = motion.add(getJumpVelocity(vehicle)); - Optional movementMultiplier = getBlockMovementMultiplier(vehicle); - if (movementMultiplier.isPresent()) { - motion = motion.mul(movementMultiplier.get()); + Vector3f movementMultiplier = getBlockMovementMultiplier(vehicle); + if (movementMultiplier != null) { + motion = motion.mul(movementMultiplier); } Vector3d correctedMovement = vehicle.getSession().getCollisionManager().correctMovement( @@ -548,7 +550,7 @@ protected boolean travel(T vehicle, float speed) { } // Set motion to 0 if a movement multiplier was used, else set to 0 on each axis with a collision - if (movementMultiplier.isPresent()) { + if (movementMultiplier != null) { motion = Vector3f.ZERO; } else { motion = motion.mul( @@ -583,10 +585,10 @@ protected boolean isClimbing(T vehicle) { } // Check if the vehicle is in an open trapdoor with a ladder of the same direction under it - Optional openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); - if (openTrapdoorDirection.isPresent()) { - Optional ladderDirection = BlockStateValues.getLadderDirection(getBlockAt(vehicle, blockPos.down())); - return ladderDirection.isPresent() && ladderDirection.get() == openTrapdoorDirection.get(); + Direction openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); + if (openTrapdoorDirection != null) { + Direction ladderDirection = BlockStateValues.getLadderDirection(getBlockAt(vehicle, blockPos.down())); + return ladderDirection != null && ladderDirection == openTrapdoorDirection; } return false; @@ -679,7 +681,7 @@ protected float getGravity(T vehicle) { return 0.08f; } - protected Optional getSupportingBlockPos(T vehicle) { + protected @Nullable Vector3i getSupportingBlockPos(T vehicle) { Vector3i result = null; if (vehicle.isOnGround()) { @@ -717,13 +719,12 @@ protected Optional getSupportingBlockPos(T vehicle) { } } - return Optional.ofNullable(result); + return result; } protected Vector3i getVelocityAffectingPos(T vehicle) { - Optional supportingBlockPos = getSupportingBlockPos(vehicle); - if (supportingBlockPos.isPresent()) { - Vector3i blockPos = supportingBlockPos.get(); + Vector3i blockPos = getSupportingBlockPos(vehicle); + if (blockPos != null) { return Vector3i.from(blockPos.getX(), Math.floor(boundingBox.getBottomCenter().getY() - 0.500001f), blockPos.getZ()); } else { return vehicle.getPosition().sub(0, 0.500001f, 0).toInt(); diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index 5f240c61246..20338313fb8 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.level.physics.PistonBehavior; import org.geysermc.geyser.registry.BlockRegistries; @@ -39,7 +40,6 @@ import org.geysermc.geyser.util.collection.LecternHasBookMap; import java.util.Locale; -import java.util.Optional; /** * Used for block entities if the Java block state contains Bedrock block information. @@ -638,10 +638,10 @@ public static float getSlipperiness(int state) { * Used when determining if an entity is climbing * * @param state BlockState of the block - * @return The ladder's direction, or empty if not a ladder + * @return The ladder's direction, or null if not a ladder */ - public static Optional getLadderDirection(int state) { - return Optional.ofNullable(LADDER_DIRECTION.get(state)); + public static @Nullable Direction getLadderDirection(int state) { + return LADDER_DIRECTION.get(state); } /** @@ -649,10 +649,10 @@ public static Optional getLadderDirection(int state) { * Used when determining if an entity is climbing * * @param state BlockState of the block - * @return The open trapdoor's direction, or empty if not an open trapdoor + * @return The open trapdoor's direction, or null if not an open trapdoor */ - public static Optional getOpenTrapdoorDirection(int state) { - return Optional.ofNullable(OPEN_TRAPDOOR_DIRECTION.get(state)); + public static @Nullable Direction getOpenTrapdoorDirection(int state) { + return OPEN_TRAPDOOR_DIRECTION.get(state); } private static Direction getBlockDirection(String javaId) { From 4417331c5155e8edff34b78d7e16e7c171c9e77d Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Thu, 16 May 2024 02:57:13 -0400 Subject: [PATCH 03/26] Only tick active vehicle --- .../entity/type/living/animal/PigEntity.java | 8 +------- .../type/living/animal/StriderEntity.java | 8 +------- .../type/living/animal/horse/CamelEntity.java | 17 +++-------------- .../vehicle/BoostableVehicleComponent.java | 4 ++-- .../entity/vehicle/CamelVehicleComponent.java | 18 ++++++++++++++++++ .../entity/vehicle/VehicleComponent.java | 9 ++++++--- .../geysermc/geyser/session/GeyserSession.java | 12 ++++++++++++ 7 files changed, 43 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index f32e3315176..f4813015d61 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -31,7 +31,6 @@ import org.cloudburstmc.math.vector.Vector3f; 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.vehicle.BoostableVehicleComponent; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.VehicleComponent; @@ -47,7 +46,7 @@ import java.util.UUID; -public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle { +public class PigEntity extends AnimalEntity implements ClientVehicle { private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this); public PigEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { @@ -97,11 +96,6 @@ public void setBoost(IntEntityMetadata entityMetadata) { vehicleComponent.startBoost(entityMetadata.getPrimitiveValue()); } - @Override - public void tick() { - vehicleComponent.tickVehicle(this); - } - @Override public VehicleComponent getVehicleComponent() { return vehicleComponent; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index 207f686d148..3392c3f1aaf 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -32,7 +32,6 @@ 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.vehicle.BoostableVehicleComponent; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.VehicleComponent; @@ -49,7 +48,7 @@ import java.util.UUID; -public class StriderEntity extends AnimalEntity implements Tickable, ClientVehicle { +public class StriderEntity extends AnimalEntity implements ClientVehicle { private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this); private boolean isCold = false; @@ -144,11 +143,6 @@ public void setBoost(IntEntityMetadata entityMetadata) { vehicleComponent.startBoost(entityMetadata.getPrimitiveValue()); } - @Override - public void tick() { - vehicleComponent.tickVehicle(this); - } - @Override public @NonNull VehicleComponent getVehicleComponent() { return vehicleComponent; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index 5f1925ec649..b48569381b0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -36,7 +36,6 @@ 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.type.Tickable; import org.geysermc.geyser.entity.vehicle.CamelVehicleComponent; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.VehicleComponent; @@ -50,11 +49,10 @@ import java.util.UUID; -public class CamelEntity extends AbstractHorseEntity implements Tickable, ClientVehicle { +public class CamelEntity extends AbstractHorseEntity implements ClientVehicle { public static final float SITTING_HEIGHT_DIFFERENCE = 1.43F; private final CamelVehicleComponent vehicleComponent = new CamelVehicleComponent(this); - private int dashTicks; 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); @@ -124,7 +122,7 @@ protected void setDimensions(Pose pose) { public void setDashing(BooleanEntityMetadata entityMetadata) { if (entityMetadata.getPrimitiveValue()) { setFlag(EntityFlag.HAS_DASH_COOLDOWN, true); - dashTicks = 55; + vehicleComponent.startDashCooldown(); } } @@ -137,15 +135,6 @@ protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttrib return attributeData; } - @Override - public void tick() { - vehicleComponent.tickVehicle(this); - if (dashTicks > 0 && --dashTicks == 0) { - setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); - updateBedrockMetadata(); - } - } - @Override public VehicleComponent getVehicleComponent() { return vehicleComponent; @@ -164,7 +153,7 @@ public boolean isClientControlled() { @Override public float getVehicleSpeed() { float moveSpeed = vehicleComponent.getMoveSpeed(); - if (dashTicks == 0 && session.getPlayerEntity().getFlag(EntityFlag.SPRINTING)) { + if (!getFlag(EntityFlag.HAS_DASH_COOLDOWN) && session.getPlayerEntity().getFlag(EntityFlag.SPRINTING)) { return moveSpeed + 0.1f; } return moveSpeed; diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java index b218b97f313..5ed9b55376c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java @@ -53,8 +53,8 @@ public boolean isBoosting() { } @Override - public void tickVehicle(T vehicle) { - super.tickVehicle(vehicle); + public void tickVehicle() { + super.tickVehicle(); if (isBoosting()) { boostTicks++; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index a1686432ba2..c5b91fcc34e 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -26,12 +26,14 @@ package org.geysermc.geyser.entity.vehicle; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; public class CamelVehicleComponent extends VehicleComponent { private float horseJumpStrength = 0.42f; // This is the default for Camels. Not sent by vanilla Java server when spawned + private int dashTick; private int jumpBoost; public CamelVehicleComponent(CamelEntity vehicle) { @@ -42,6 +44,22 @@ public void setHorseJumpStrength(float horseJumpStrength) { this.horseJumpStrength = horseJumpStrength; } + public void startDashCooldown() { + this.dashTick = vehicle.getSession().getTicks() + 55; + } + + @Override + public void tickVehicle() { + if (vehicle.getFlag(EntityFlag.HAS_DASH_COOLDOWN)) { + if (vehicle.getSession().getTicks() > dashTick) { + vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); + vehicle.updateBedrockMetadata(); + } + } + + super.tickVehicle(); + } + @Override public void setEffect(Effect effect, int effectAmplifier) { if (effect == Effect.JUMP_BOOST) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 48317b8ca62..26f99867448 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -67,6 +67,7 @@ public class VehicleComponent { "minecraft:sweet_berry_bush", Vector3f.from(0.8f, 0.75f, 0.8f) ); + protected final T vehicle; protected final BoundingBox boundingBox; protected float moveSpeed; @@ -74,16 +75,18 @@ public class VehicleComponent { protected boolean slowFalling; public VehicleComponent(T vehicle) { + this.vehicle = vehicle; + double width = Double.parseDouble(Float.toString(vehicle.getBoundingBoxWidth())); double height = Double.parseDouble(Float.toString(vehicle.getBoundingBoxHeight())); - boundingBox = new BoundingBox( + this.boundingBox = new BoundingBox( vehicle.getPosition().getX(), vehicle.getPosition().getY() + height / 2, vehicle.getPosition().getZ(), width, height, width ); - moveSpeed = GeyserAttributeType.MOVEMENT_SPEED.getDefaultValue(); + this.moveSpeed = GeyserAttributeType.MOVEMENT_SPEED.getDefaultValue(); } public void setWidth(float width) { @@ -128,7 +131,7 @@ public float getMoveSpeed() { return moveSpeed; } - public void tickVehicle(T vehicle) { + public void tickVehicle() { if (!vehicle.isClientControlled()) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index d10a20b3da7..1e354805842 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -117,6 +117,7 @@ import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.Tickable; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler; import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler; import org.geysermc.geyser.impl.camera.CameraDefinitions; @@ -545,6 +546,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { */ private ScheduledFuture tickThread = null; + /** + * The number of ticks that have elapsed since the start of this session + */ + private int ticks; + /** * Used to return the player to their original rotation after using an item in BedrockInventoryTransactionTranslator */ @@ -1198,6 +1204,10 @@ protected void tick() { isInWorldBorderWarningArea = false; } + Entity vehicle = playerEntity.getVehicle(); + if (vehicle instanceof ClientVehicle clientVehicle && vehicle.isValid()) { + clientVehicle.getVehicleComponent().tickVehicle(); + } for (Tickable entity : entityCache.getTickableEntities()) { entity.tick(); @@ -1233,6 +1243,8 @@ protected void tick() { } catch (Throwable throwable) { throwable.printStackTrace(); } + + ticks++; } public void setAuthenticationData(AuthData authData) { From b25087448ccd357499dac7f739561640625dab4c Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 17 May 2024 02:58:31 -0400 Subject: [PATCH 04/26] Track world ticks --- .../java/org/geysermc/geyser/session/GeyserSession.java | 9 +++++++++ .../translator/protocol/java/JavaRespawnTranslator.java | 1 + .../protocol/java/level/JavaSetTimeTranslator.java | 2 ++ 3 files changed, 12 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index b54d8553587..f37862a7539 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -551,6 +551,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { */ private int ticks; + /** + * The world time in ticks according to the server + *

+ * Note: The TickingStatePacket is currently ignored. + */ + @Setter + private long worldTicks; + /** * Used to return the player to their original rotation after using an item in BedrockInventoryTransactionTranslator */ @@ -1247,6 +1255,7 @@ protected void tick() { } ticks++; + worldTicks++; } public void setAuthenticationData(AuthData authData) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java index fe0868253af..966d147637a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java @@ -100,6 +100,7 @@ public void translate(GeyserSession session, ClientboundRespawnPacket packet) { DimensionUtils.switchDimension(session, fakeDim); } session.setWorldName(spawnInfo.getWorldName()); + session.setWorldTicks(0); DimensionUtils.switchDimension(session, newDimension); ChunkUtils.loadDimension(session); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java index 1e398ad9bbf..43ef0870a05 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java @@ -36,6 +36,8 @@ public class JavaSetTimeTranslator extends PacketTranslator Date: Fri, 17 May 2024 03:12:52 -0400 Subject: [PATCH 05/26] Fixes for Camel dash and pose transition --- .../geyser/entity/EntityDefinitions.java | 2 +- .../type/living/animal/horse/CamelEntity.java | 12 +++ .../entity/vehicle/CamelVehicleComponent.java | 96 ++++++++++++++----- .../entity/vehicle/VehicleComponent.java | 13 +-- .../entity/JavaSetPassengersTranslator.java | 5 + 5 files changed, 95 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 19b8c9cdbfe..44d3c00d67a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -926,7 +926,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) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index b48569381b0..4bd6e848141 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -46,6 +46,7 @@ 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; @@ -120,12 +121,23 @@ 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); diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index c5b91fcc34e..bf06d130c69 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.entity.vehicle; +import lombok.Setter; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; @@ -32,7 +33,15 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; public class CamelVehicleComponent extends VehicleComponent { - private float horseJumpStrength = 0.42f; // This is the default for Camels. Not sent by vanilla Java server when spawned + private static final int STANDING_TICKS = 52; + private static final int DASH_TICKS = 55; + + @Setter + private float horseJumpStrength = 0.42f; // Not sent by vanilla Java server when spawned + + @Setter + private long lastPoseTick; + private int dashTick; private int jumpBoost; @@ -40,58 +49,93 @@ public CamelVehicleComponent(CamelEntity vehicle) { super(vehicle); } - public void setHorseJumpStrength(float horseJumpStrength) { - this.horseJumpStrength = horseJumpStrength; - } - public void startDashCooldown() { - this.dashTick = vehicle.getSession().getTicks() + 55; + this.dashTick = vehicle.getSession().getTicks() + DASH_TICKS; } @Override public void tickVehicle() { - if (vehicle.getFlag(EntityFlag.HAS_DASH_COOLDOWN)) { - if (vehicle.getSession().getTicks() > dashTick) { + if (this.dashTick != 0) { + if (vehicle.getSession().getTicks() > this.dashTick) { vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); - vehicle.updateBedrockMetadata(); + this.dashTick = 0; + } else { + vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, true); } } + vehicle.setFlag(EntityFlag.CAN_DASH, vehicle.getFlag(EntityFlag.SADDLED) && !isStationary()); + vehicle.updateBedrockMetadata(); + super.tickVehicle(); } @Override - public void setEffect(Effect effect, int effectAmplifier) { - if (effect == Effect.JUMP_BOOST) { - jumpBoost = effectAmplifier + 1; - } else { - super.setEffect(effect, effectAmplifier); - } + public void onDismount() { + vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); + vehicle.updateBedrockMetadata(); } @Override - public void removeEffect(Effect effect) { - if (effect == Effect.JUMP_BOOST) { - jumpBoost = 0; - } else { - super.removeEffect(effect); + protected boolean travel(CamelEntity vehicle, float speed) { + if (vehicle.isOnGround() && isStationary()) { + vehicle.setMotion(vehicle.getMotion().mul(0, 1, 0)); } + + return super.travel(vehicle, speed); } @Override - protected Vector3f getJumpVelocity(CamelEntity vehicle) { + protected Vector3f getInputVelocity(CamelEntity vehicle, float speed) { + if (isStationary()) { + return Vector3f.ZERO; + } + SessionPlayerEntity player = vehicle.getSession().getPlayerEntity(); + Vector3f inputVelocity = super.getInputVelocity(vehicle, speed); float jumpStrength = player.getVehicleJumpStrength(); if (jumpStrength > 0) { player.setVehicleJumpStrength(0); - jumpStrength = jumpStrength >= 90 ? 1.0f : 0.4f + 0.4f * jumpStrength / 90.0f; - return Vector3f.createDirectionDeg(0, -player.getYaw()) - .mul(22.2222f * jumpStrength * moveSpeed * getVelocityMultiplier(vehicle)) - .up(1.4285f * jumpStrength * (horseJumpStrength * getJumpVelocityMultiplier(vehicle) + (jumpBoost * 0.1f))); + if (jumpStrength >= 90) { + jumpStrength = 1.0f; + } else { + jumpStrength = 0.4f + 0.4f * jumpStrength / 90.0f; + } + + return inputVelocity.add(Vector3f.createDirectionDeg(0, -player.getYaw()) + .mul(22.2222f * jumpStrength * this.moveSpeed * getVelocityMultiplier(vehicle)) + .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier(vehicle) + (this.jumpBoost * 0.1f)))); } - return Vector3f.ZERO; + return inputVelocity; + } + + /** + * Checks if the camel is sitting + * or transitioning to standing pose. + */ + private boolean isStationary() { + // Java checks if sitting using lastPoseTick + return this.lastPoseTick < 0 || vehicle.getSession().getWorldTicks() < this.lastPoseTick + STANDING_TICKS; + } + + @Override + public void setEffect(Effect effect, int effectAmplifier) { + if (effect == Effect.JUMP_BOOST) { + jumpBoost = effectAmplifier + 1; + } else { + super.setEffect(effect, effectAmplifier); + } + } + + @Override + public void removeEffect(Effect effect) { + if (effect == Effect.JUMP_BOOST) { + jumpBoost = 0; + } else { + super.removeEffect(effect); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 26f99867448..70be16f3e35 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -96,7 +96,9 @@ public void setWidth(float width) { } public void setHeight(float height) { - boundingBox.setSizeY(Double.parseDouble(Float.toString(height))); + double doubleHeight = Double.parseDouble(Float.toString(height)); + boundingBox.translate(0, (doubleHeight - boundingBox.getSizeY()) / 2, 0); + boundingBox.setSizeY(doubleHeight); } public void moveAbsolute(double x, double y, double z) { @@ -131,6 +133,10 @@ public float getMoveSpeed() { return moveSpeed; } + public void onDismount() { + // + } + public void tickVehicle() { if (!vehicle.isClientControlled()) { return; @@ -489,10 +495,6 @@ protected void onBubbleColumnCollision(T vehicle, boolean drag) { )); } - protected Vector3f getJumpVelocity(T vehicle) { - return Vector3f.ZERO; - } - /** * @return True if there was a horizontal collision */ @@ -511,7 +513,6 @@ protected boolean travel(T vehicle, float speed) { // TODO: isImmobile? set input to 0 and jump to false motion = motion.add(getInputVelocity(vehicle, speed)); - motion = motion.add(getJumpVelocity(vehicle)); Vector3f movementMultiplier = getBlockMovementMultiplier(vehicle); if (movementMultiplier != null) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java index 9895a248c29..c35c2b7567a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java @@ -30,6 +30,7 @@ import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -100,6 +101,10 @@ public void translate(GeyserSession session, ClientboundSetPassengersPacket pack // as of Java 1.19.3, but the scheduled future checks for the vehicle being null anyway. session.getMountVehicleScheduledFuture().cancel(false); } + + if (entity instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().onDismount(); + } } } } From 021e3bf64f21fc1e4c8f4d45181646e40e981620 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 17 May 2024 03:19:21 -0400 Subject: [PATCH 06/26] Remove vehicle parameter --- .../entity/vehicle/CamelVehicleComponent.java | 12 +- .../entity/vehicle/VehicleComponent.java | 128 +++++++++--------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index bf06d130c69..97f7ed6d701 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -77,22 +77,22 @@ public void onDismount() { } @Override - protected boolean travel(CamelEntity vehicle, float speed) { + protected boolean travel(float speed) { if (vehicle.isOnGround() && isStationary()) { vehicle.setMotion(vehicle.getMotion().mul(0, 1, 0)); } - return super.travel(vehicle, speed); + return super.travel(speed); } @Override - protected Vector3f getInputVelocity(CamelEntity vehicle, float speed) { + protected Vector3f getInputVelocity(float speed) { if (isStationary()) { return Vector3f.ZERO; } SessionPlayerEntity player = vehicle.getSession().getPlayerEntity(); - Vector3f inputVelocity = super.getInputVelocity(vehicle, speed); + Vector3f inputVelocity = super.getInputVelocity(speed); float jumpStrength = player.getVehicleJumpStrength(); if (jumpStrength > 0) { @@ -105,8 +105,8 @@ protected Vector3f getInputVelocity(CamelEntity vehicle, float speed) { } return inputVelocity.add(Vector3f.createDirectionDeg(0, -player.getYaw()) - .mul(22.2222f * jumpStrength * this.moveSpeed * getVelocityMultiplier(vehicle)) - .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier(vehicle) + (this.jumpBoost * 0.1f)))); + .mul(22.2222f * jumpStrength * this.moveSpeed * getVelocityMultiplier()) + .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier() + (this.jumpBoost * 0.1f)))); } return inputVelocity; diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 70be16f3e35..c0ec857d61f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -142,21 +142,21 @@ public void tickVehicle() { return; } - ObjectDoublePair fluidHeight = updateFluidMovement(vehicle); + ObjectDoublePair fluidHeight = updateFluidMovement(); switch (fluidHeight.left()) { - case WATER -> waterMovement(vehicle); + case WATER -> waterMovement(); case LAVA -> { - if (vehicle.canWalkOnLava() && BlockStateValues.getFluid(getBlockAt(vehicle, boundingBox.getBottomCenter().toInt())) == Fluid.LAVA) { - landMovement(vehicle); + if (vehicle.canWalkOnLava() && BlockStateValues.getFluid(getBlockAt(boundingBox.getBottomCenter().toInt())) == Fluid.LAVA) { + landMovement(); } else { - lavaMovement(vehicle, fluidHeight.rightDouble()); + lavaMovement(fluidHeight.rightDouble()); } } - case EMPTY -> landMovement(vehicle); + case EMPTY -> landMovement(); } } - protected ObjectDoublePair updateFluidMovement(T vehicle) { + protected ObjectDoublePair updateFluidMovement() { BoundingBox box = boundingBox.clone(); box.expand(-0.001); @@ -166,12 +166,12 @@ protected ObjectDoublePair updateFluidMovement(T vehicle) { BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ()); int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); - double waterHeight = getFluidHeightAndApplyMovement(Fluid.WATER, 0.014, min.getY(), vehicle, iter, blocks); - double lavaHeight = getFluidHeightAndApplyMovement(Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY(), vehicle, iter, blocks); + double waterHeight = getFluidHeightAndApplyMovement(Fluid.WATER, 0.014, min.getY(), iter, blocks); + double lavaHeight = getFluidHeightAndApplyMovement(Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY(), iter, blocks); if (lavaHeight > 0 && vehicle.getDefinition().entityType() == EntityType.STRIDER) { Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || BlockStateValues.getFluid(getBlockAt(vehicle, blockPos.up())) == Fluid.LAVA) { + if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || BlockStateValues.getFluid(getBlockAt(blockPos.up())) == Fluid.LAVA) { vehicle.setMotion(vehicle.getMotion().mul(0.5f).add(0, 0.05f, 0)); } else { vehicle.setOnGround(true); @@ -190,7 +190,7 @@ protected ObjectDoublePair updateFluidMovement(T vehicle) { return EMPTY_FLUID_PAIR; } - protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, double minY, T vehicle, BlockPositionIterator iter, int[] blocks) { + protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, double minY, BlockPositionIterator iter, int[] blocks) { Vector3d totalVelocity = Vector3d.ZERO; double maxFluidHeight = 0; int fluidBlocks = 0; @@ -213,7 +213,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl Vector3d velocity = Vector3d.ZERO; for (Direction direction : Direction.HORIZONTAL) { Vector3i adjacentBlockPos = blockPos.add(direction.getUnitVector()); - int adjacentBlockId = getBlockAt(vehicle, adjacentBlockPos); + int adjacentBlockId = getBlockAt(adjacentBlockPos); Fluid adjacentFluid = BlockStateValues.getFluid(adjacentBlockId); float fluidHeightDiff = 0; @@ -224,7 +224,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl // check if there is a fluid under it BlockCollision adjacentBlockCollision = BlockUtils.getCollision(adjacentBlockId); if (adjacentBlockCollision == null) { - float adjacentFluidHeight = getLogicalFluidHeight(fluid, getBlockAt(vehicle, adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); + float adjacentFluidHeight = getLogicalFluidHeight(fluid, getBlockAt(adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); if (adjacentFluidHeight != -1) { // Only care about same type of fluid fluidHeightDiff = getLogicalFluidHeight(fluid, blockId) - (adjacentFluidHeight - MAX_LOGICAL_FLUID_HEIGHT); } @@ -243,7 +243,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl if (!flowBlocked) { Vector3i blockPosUp = blockPos.up(); for (Direction direction : Direction.HORIZONTAL) { - flowBlocked = isFlowBlocked(fluid, getBlockAt(vehicle, blockPosUp.add(direction.getUnitVector()))); + flowBlocked = isFlowBlocked(fluid, getBlockAt(blockPosUp.add(direction.getUnitVector()))); if (flowBlocked) { break; } @@ -290,7 +290,7 @@ protected Vector3d javaNormalize(Vector3d vec) { return len < 1.0E-4 ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len); } - protected int getBlockAt(T vehicle, Vector3i blockPos) { + protected int getBlockAt(Vector3i blockPos) { return vehicle.getSession().getGeyser().getWorldManager().getBlockAt(vehicle.getSession(), blockPos); } @@ -319,57 +319,57 @@ private boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { return BlockUtils.getCollision(adjacentBlockId) instanceof SolidCollision; } - protected void waterMovement(T vehicle) { - float gravity = getGravity(vehicle); + protected void waterMovement() { + float gravity = getGravity(); float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier double originalY = boundingBox.getBottomCenter().getY(); boolean falling = vehicle.getMotion().getY() <= 0; // NOT IMPLEMENTED: depth strider and dolphins grace - boolean horizontalCollision = travel(vehicle,0.02f); - if (horizontalCollision && isClimbing(vehicle)) { + boolean horizontalCollision = travel(0.02f); + if (horizontalCollision && isClimbing()) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.2f, vehicle.getMotion().getZ())); } vehicle.setMotion(vehicle.getMotion().mul(drag, 0.8f, drag)); - vehicle.setMotion(getFluidGravity(vehicle, gravity, falling)); + vehicle.setMotion(getFluidGravity(gravity, falling)); - if (horizontalCollision && shouldApplyFluidJumpBoost(vehicle, originalY)) { + if (horizontalCollision && shouldApplyFluidJumpBoost(originalY)) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); } } - protected void lavaMovement(T vehicle, double lavaHeight) { - float gravity = getGravity(vehicle); + protected void lavaMovement(double lavaHeight) { + float gravity = getGravity(); double originalY = boundingBox.getBottomCenter().getY(); boolean falling = vehicle.getMotion().getY() <= 0; - boolean horizontalCollision = travel(vehicle, 0.02f); + boolean horizontalCollision = travel(0.02f); if (lavaHeight <= (boundingBox.getSizeY() * 0.85 < 0.4 ? 0.0 : 0.4)) { // Swim height vehicle.setMotion(vehicle.getMotion().mul(0.5f, 0.8f, 0.5f)); - vehicle.setMotion(getFluidGravity(vehicle, gravity, falling)); + vehicle.setMotion(getFluidGravity(gravity, falling)); } else { vehicle.setMotion(vehicle.getMotion().mul(0.5f)); } vehicle.setMotion(vehicle.getMotion().down(gravity / 4.0f)); - if (horizontalCollision && shouldApplyFluidJumpBoost(vehicle, originalY)) { + if (horizontalCollision && shouldApplyFluidJumpBoost(originalY)) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); } } - protected void landMovement(T vehicle) { - float gravity = getGravity(vehicle); - float slipperiness = BlockStateValues.getSlipperiness(getBlockAt(vehicle, getVelocityAffectingPos(vehicle))); + protected void landMovement() { + float gravity = getGravity(); + float slipperiness = BlockStateValues.getSlipperiness(getBlockAt(getVelocityAffectingPos())); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); - boolean horizontalCollision = travel(vehicle, speed); - if (isClimbing(vehicle)) { - vehicle.setMotion(getClimbingSpeed(vehicle, horizontalCollision)); + boolean horizontalCollision = travel(speed); + if (isClimbing()) { + vehicle.setMotion(getClimbingSpeed(horizontalCollision)); // NOT IMPLEMENTED: climbing in powdered snow } @@ -383,7 +383,7 @@ protected void landMovement(T vehicle) { vehicle.setMotion(vehicle.getMotion().mul(drag, 0.98f, drag)); } - protected boolean shouldApplyFluidJumpBoost(T vehicle, double originalY) { + protected boolean shouldApplyFluidJumpBoost(double originalY) { BoundingBox box = boundingBox.clone(); box.translate(vehicle.getMotion().toDouble().up(0.6f - boundingBox.getBottomCenter().getY() + originalY)); box.expand(-1.0E-7); @@ -408,7 +408,7 @@ protected boolean shouldApplyFluidJumpBoost(T vehicle, double originalY) { return true; } - protected Vector3f getClimbingSpeed(T vehicle, boolean horizontalCollision) { + protected Vector3f getClimbingSpeed(boolean horizontalCollision) { Vector3f motion = vehicle.getMotion(); return Vector3f.from( MathUtils.clamp(motion.getX(), -CLIMB_SPEED, CLIMB_SPEED), @@ -417,7 +417,7 @@ protected Vector3f getClimbingSpeed(T vehicle, boolean horizontalCollision) { ); } - protected Vector3f getFluidGravity(T vehicle, float gravity, boolean falling) { + protected Vector3f getFluidGravity(float gravity, boolean falling) { Vector3f motion = vehicle.getMotion(); if (vehicle.getFlag(EntityFlag.HAS_GRAVITY) && !vehicle.getFlag(EntityFlag.SPRINTING)) { float newY = motion.getY() - gravity / 16; @@ -429,7 +429,7 @@ protected Vector3f getFluidGravity(T vehicle, float gravity, boolean falling) { return motion; } - protected @Nullable Vector3f getBlockMovementMultiplier(T vehicle) { + protected @Nullable Vector3f getBlockMovementMultiplier() { BoundingBox box = boundingBox.clone(); box.expand(-1.0E-7); @@ -452,7 +452,7 @@ protected Vector3f getFluidGravity(T vehicle, float gravity, boolean falling) { return null; } - protected void applyBlockCollisionEffects(T vehicle) { + protected void applyBlockCollisionEffects() { BoundingBox box = boundingBox.clone(); box.expand(-1.0E-7); @@ -466,16 +466,16 @@ protected void applyBlockCollisionEffects(T vehicle) { int blockId = blocks[iter.getIteration()]; if (BlockStateValues.JAVA_HONEY_BLOCK_ID == blockId) { - onHoneyBlockCollision(vehicle); + onHoneyBlockCollision(); } else if (BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID == blockId) { - onBubbleColumnCollision(vehicle, true); + onBubbleColumnCollision(true); } else if (BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID == blockId) { - onBubbleColumnCollision(vehicle, false); + onBubbleColumnCollision(false); } } } - protected void onHoneyBlockCollision(T vehicle) { + protected void onHoneyBlockCollision() { if (vehicle.isOnGround() || vehicle.getMotion().getY() >= -0.08f) { return; } @@ -486,7 +486,7 @@ protected void onHoneyBlockCollision(T vehicle) { vehicle.setMotion(Vector3f.from(motion.getX() * mul, -0.05f, motion.getZ() * mul)); } - protected void onBubbleColumnCollision(T vehicle, boolean drag) { + protected void onBubbleColumnCollision(boolean drag) { Vector3f motion = vehicle.getMotion(); vehicle.setMotion(Vector3f.from( motion.getX(), @@ -498,7 +498,7 @@ protected void onBubbleColumnCollision(T vehicle, boolean drag) { /** * @return True if there was a horizontal collision */ - protected boolean travel(T vehicle, float speed) { + protected boolean travel(float speed) { Vector3f motion = vehicle.getMotion(); // Java only does this client side @@ -512,9 +512,9 @@ protected boolean travel(T vehicle, float speed) { // TODO: isImmobile? set input to 0 and jump to false - motion = motion.add(getInputVelocity(vehicle, speed)); + motion = motion.add(getInputVelocity(speed)); - Vector3f movementMultiplier = getBlockMovementMultiplier(vehicle); + Vector3f movementMultiplier = getBlockMovementMultiplier(); if (movementMultiplier != null) { motion = motion.mul(movementMultiplier); } @@ -535,7 +535,7 @@ protected boolean travel(T vehicle, float speed) { boolean bounced = false; if (onGround) { Vector3i landingPos = newPos.sub(0, 0.2f, 0).toInt(); - int landingBlockId = getBlockAt(vehicle, landingPos); + int landingBlockId = getBlockAt(landingPos); if (landingBlockId == BlockStateValues.JAVA_SLIME_BLOCK_ID) { motion = Vector3f.from(motion.getX(), -motion.getY(), motion.getZ()); @@ -565,24 +565,24 @@ protected boolean travel(T vehicle, float speed) { } // Send the new position to the bedrock client and java server - moveVehicle(vehicle, newPos, onGround); + moveVehicle(newPos, onGround); vehicle.setMotion(motion); - applyBlockCollisionEffects(vehicle); + applyBlockCollisionEffects(); - float velocityMultiplier = getVelocityMultiplier(vehicle); + float velocityMultiplier = getVelocityMultiplier(); vehicle.setMotion(vehicle.getMotion().mul(velocityMultiplier, 1.0f, velocityMultiplier)); return horizontalCollision; } - protected boolean isClimbing(T vehicle) { + protected boolean isClimbing() { if (!vehicle.canClimb()) { return false; } Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - int blockId = getBlockAt(vehicle, blockPos); + int blockId = getBlockAt(blockPos); if (BlockStateValues.isClimbable(blockId)) { return true; @@ -591,7 +591,7 @@ protected boolean isClimbing(T vehicle) { // Check if the vehicle is in an open trapdoor with a ladder of the same direction under it Direction openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); if (openTrapdoorDirection != null) { - Direction ladderDirection = BlockStateValues.getLadderDirection(getBlockAt(vehicle, blockPos.down())); + Direction ladderDirection = BlockStateValues.getLadderDirection(getBlockAt(blockPos.down())); return ladderDirection != null && ladderDirection == openTrapdoorDirection; } @@ -608,7 +608,7 @@ protected Vector2f normalizeInput(Vector2f input) { return input; } - protected Vector3f getInputVelocity(T vehicle, float speed) { + protected Vector3f getInputVelocity(float speed) { Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); input = input.mul(0.98f); input = vehicle.getAdjustedInput(input); @@ -621,7 +621,7 @@ protected Vector3f getInputVelocity(T vehicle, float speed) { return Vector3f.from(input.getX() * cos - input.getY() * sin, 0, input.getY() * cos + input.getX() * sin); } - protected void moveVehicle(T vehicle, Vector3d javaPos, boolean isOnGround) { + protected void moveVehicle(Vector3d javaPos, boolean isOnGround) { Vector3f bedrockPos = javaPos.toFloat(); float yaw = vehicle.getSession().getPlayerEntity().getYaw(); float pitch = vehicle.getSession().getPlayerEntity().getPitch() * 0.5f; @@ -673,7 +673,7 @@ protected void moveVehicle(T vehicle, Vector3d javaPos, boolean isOnGround) { vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis()); } - protected float getGravity(T vehicle) { + protected float getGravity() { if (!vehicle.getFlag(EntityFlag.HAS_GRAVITY)) { return 0; } @@ -685,7 +685,7 @@ protected float getGravity(T vehicle) { return 0.08f; } - protected @Nullable Vector3i getSupportingBlockPos(T vehicle) { + protected @Nullable Vector3i getSupportingBlockPos() { Vector3i result = null; if (vehicle.isOnGround()) { @@ -726,8 +726,8 @@ protected float getGravity(T vehicle) { return result; } - protected Vector3i getVelocityAffectingPos(T vehicle) { - Vector3i blockPos = getSupportingBlockPos(vehicle); + protected Vector3i getVelocityAffectingPos() { + Vector3i blockPos = getSupportingBlockPos(); if (blockPos != null) { return Vector3i.from(blockPos.getX(), Math.floor(boundingBox.getBottomCenter().getY() - 0.500001f), blockPos.getZ()); } else { @@ -735,8 +735,8 @@ protected Vector3i getVelocityAffectingPos(T vehicle) { } } - protected float getVelocityMultiplier(T vehicle) { - int blockId = getBlockAt(vehicle, boundingBox.getBottomCenter().toInt()); + protected float getVelocityMultiplier() { + int blockId = getBlockAt(boundingBox.getBottomCenter().toInt()); if (BlockStateValues.getWaterLevel(blockId) != -1 // getWaterLevel does not include waterlogged blocks || blockId == BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID || blockId == BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID) { @@ -747,7 +747,7 @@ protected float getVelocityMultiplier(T vehicle) { return 0.4f; } - blockId = getBlockAt(vehicle, getVelocityAffectingPos(vehicle)); + blockId = getBlockAt(getVelocityAffectingPos()); if (blockId == BlockStateValues.JAVA_SOUL_SAND_ID || blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { return 0.4f; } @@ -755,13 +755,13 @@ protected float getVelocityMultiplier(T vehicle) { return 1.0f; } - protected float getJumpVelocityMultiplier(T vehicle) { - int blockId = getBlockAt(vehicle, boundingBox.getBottomCenter().toInt()); + protected float getJumpVelocityMultiplier() { + int blockId = getBlockAt(boundingBox.getBottomCenter().toInt()); if (blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { return 0.5f; } - blockId = getBlockAt(vehicle, getVelocityAffectingPos(vehicle)); + blockId = getBlockAt(getVelocityAffectingPos()); if (blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { return 0.5f; } From b79a12ea8e28cc74ff11d343d4e5f811ee8569a8 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 17 May 2024 19:09:42 -0400 Subject: [PATCH 07/26] Start using blocks refactor --- .../entity/vehicle/VehicleComponent.java | 87 ++++++++++--------- .../geyser/level/block/BlockStateValues.java | 16 ---- .../level/physics/CollisionManager.java | 2 +- 3 files changed, 47 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index c0ec857d61f..16dec67594e 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -38,12 +38,14 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.LivingEntity; 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.property.Properties; +import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.CollisionManager; import org.geysermc.geyser.level.physics.Direction; -import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.SolidCollision; import org.geysermc.geyser.util.BlockUtils; @@ -61,10 +63,10 @@ public class VehicleComponent { private static final float MIN_VELOCITY = 0.003f; private static final float CLIMB_SPEED = 0.15f; - private static final Map MOVEMENT_MULTIPLIERS = Map.of( - "minecraft:cobweb", Vector3f.from(0.25f, 0.05f, 0.25f), - "minecraft:powder_snow", Vector3f.from(0.9f, 1.5f, 0.9f), - "minecraft:sweet_berry_bush", Vector3f.from(0.8f, 0.75f, 0.8f) + private static final Map MOVEMENT_MULTIPLIERS = Map.of( + Blocks.COBWEB, Vector3f.from(0.25f, 0.05f, 0.25f), + Blocks.POWDER_SNOW, Vector3f.from(0.9f, 1.5f, 0.9f), + Blocks.SWEET_BERRY_BUSH, Vector3f.from(0.8f, 0.75f, 0.8f) ); protected final T vehicle; @@ -146,7 +148,7 @@ public void tickVehicle() { switch (fluidHeight.left()) { case WATER -> waterMovement(); case LAVA -> { - if (vehicle.canWalkOnLava() && BlockStateValues.getFluid(getBlockAt(boundingBox.getBottomCenter().toInt())) == Fluid.LAVA) { + if (vehicle.canWalkOnLava() && getBlock(boundingBox.getBottomCenter().toInt()) == Blocks.LAVA) { landMovement(); } else { lavaMovement(fluidHeight.rightDouble()); @@ -171,7 +173,7 @@ protected ObjectDoublePair updateFluidMovement() { if (lavaHeight > 0 && vehicle.getDefinition().entityType() == EntityType.STRIDER) { Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || BlockStateValues.getFluid(getBlockAt(blockPos.up())) == Fluid.LAVA) { + if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || getBlock(boundingBox.getBottomCenter().toInt()) == Blocks.LAVA) { vehicle.setMotion(vehicle.getMotion().mul(0.5f).add(0, 0.05f, 0)); } else { vehicle.setOnGround(true); @@ -213,7 +215,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl Vector3d velocity = Vector3d.ZERO; for (Direction direction : Direction.HORIZONTAL) { Vector3i adjacentBlockPos = blockPos.add(direction.getUnitVector()); - int adjacentBlockId = getBlockAt(adjacentBlockPos); + int adjacentBlockId = getBlockId(adjacentBlockPos); Fluid adjacentFluid = BlockStateValues.getFluid(adjacentBlockId); float fluidHeightDiff = 0; @@ -224,7 +226,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl // check if there is a fluid under it BlockCollision adjacentBlockCollision = BlockUtils.getCollision(adjacentBlockId); if (adjacentBlockCollision == null) { - float adjacentFluidHeight = getLogicalFluidHeight(fluid, getBlockAt(adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); + float adjacentFluidHeight = getLogicalFluidHeight(fluid, getBlockId(adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); if (adjacentFluidHeight != -1) { // Only care about same type of fluid fluidHeightDiff = getLogicalFluidHeight(fluid, blockId) - (adjacentFluidHeight - MAX_LOGICAL_FLUID_HEIGHT); } @@ -243,7 +245,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl if (!flowBlocked) { Vector3i blockPosUp = blockPos.up(); for (Direction direction : Direction.HORIZONTAL) { - flowBlocked = isFlowBlocked(fluid, getBlockAt(blockPosUp.add(direction.getUnitVector()))); + flowBlocked = isFlowBlocked(fluid, getBlockId(blockPosUp.add(direction.getUnitVector()))); if (flowBlocked) { break; } @@ -290,11 +292,19 @@ protected Vector3d javaNormalize(Vector3d vec) { return len < 1.0E-4 ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len); } - protected int getBlockAt(Vector3i blockPos) { + protected int getBlockId(Vector3i blockPos) { return vehicle.getSession().getGeyser().getWorldManager().getBlockAt(vehicle.getSession(), blockPos); } - private float getWorldFluidHeight(Fluid fluidType, int blockId) { + protected BlockState getBlockState(Vector3i blockPos) { + return BlockState.of(getBlockId(blockPos)); + } + + protected Block getBlock(Vector3i blockPos) { + return getBlockState(blockPos).block(); + } + + protected float getWorldFluidHeight(Fluid fluidType, int blockId) { return (float) switch (fluidType) { case WATER -> BlockStateValues.getWaterHeight(blockId); case LAVA -> BlockStateValues.getLavaHeight(blockId); @@ -302,12 +312,12 @@ private float getWorldFluidHeight(Fluid fluidType, int blockId) { }; } - private float getLogicalFluidHeight(Fluid fluidType, int blockId) { + protected float getLogicalFluidHeight(Fluid fluidType, int blockId) { return Math.min(getWorldFluidHeight(fluidType, blockId), MAX_LOGICAL_FLUID_HEIGHT); } - private boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { - if (adjacentBlockId == BlockStateValues.JAVA_ICE_ID) { + protected boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { + if (BlockState.of(adjacentBlockId).block() == Blocks.ICE) { return false; } @@ -363,7 +373,7 @@ protected void lavaMovement(double lavaHeight) { protected void landMovement() { float gravity = getGravity(); - float slipperiness = BlockStateValues.getSlipperiness(getBlockAt(getVelocityAffectingPos())); + float slipperiness = BlockStateValues.getSlipperiness(getBlockId(getVelocityAffectingPos())); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); @@ -441,8 +451,7 @@ protected Vector3f getFluidGravity(float gravity, boolean falling) { // Iterate backwards for (int i = blocks.length - 1; i >= 0; i--) { - String cleanIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blocks[i], BlockMapping.DEFAULT).getCleanJavaIdentifier(); - Vector3f multiplier = MOVEMENT_MULTIPLIERS.get(cleanIdentifier); + Vector3f multiplier = MOVEMENT_MULTIPLIERS.get(BlockState.of(blocks[i]).block()); if (multiplier != null) { return multiplier; @@ -463,14 +472,12 @@ protected void applyBlockCollisionEffects() { int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); for (iter.reset(); iter.hasNext(); iter.next()) { - int blockId = blocks[iter.getIteration()]; + BlockState blockState = BlockState.of(blocks[iter.getIteration()]); - if (BlockStateValues.JAVA_HONEY_BLOCK_ID == blockId) { + if (blockState.block() == Blocks.HONEY_BLOCK) { onHoneyBlockCollision(); - } else if (BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID == blockId) { - onBubbleColumnCollision(true); - } else if (BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID == blockId) { - onBubbleColumnCollision(false); + } else if (blockState.block() == Blocks.BUBBLE_COLUMN) { + onBubbleColumnCollision(blockState.getValue(Properties.DRAG)); } } } @@ -535,9 +542,9 @@ protected boolean travel(float speed) { boolean bounced = false; if (onGround) { Vector3i landingPos = newPos.sub(0, 0.2f, 0).toInt(); - int landingBlockId = getBlockAt(landingPos); + int landingBlockId = getBlockId(landingPos); - if (landingBlockId == BlockStateValues.JAVA_SLIME_BLOCK_ID) { + if (BlockState.of(landingBlockId).block() == Blocks.SLIME_BLOCK) { motion = Vector3f.from(motion.getX(), -motion.getY(), motion.getZ()); bounced = true; @@ -582,7 +589,7 @@ protected boolean isClimbing() { } Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - int blockId = getBlockAt(blockPos); + int blockId = getBlockId(blockPos); if (BlockStateValues.isClimbable(blockId)) { return true; @@ -591,8 +598,8 @@ protected boolean isClimbing() { // Check if the vehicle is in an open trapdoor with a ladder of the same direction under it Direction openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); if (openTrapdoorDirection != null) { - Direction ladderDirection = BlockStateValues.getLadderDirection(getBlockAt(blockPos.down())); - return ladderDirection != null && ladderDirection == openTrapdoorDirection; + BlockState ladder = getBlockState(blockPos.down()); + return ladder.block() == Blocks.LADDER && ladder.getValue(Properties.HORIZONTAL_FACING) == openTrapdoorDirection; } return false; @@ -736,19 +743,17 @@ protected Vector3i getVelocityAffectingPos() { } protected float getVelocityMultiplier() { - int blockId = getBlockAt(boundingBox.getBottomCenter().toInt()); - if (BlockStateValues.getWaterLevel(blockId) != -1 // getWaterLevel does not include waterlogged blocks - || blockId == BlockStateValues.JAVA_BUBBLE_COLUMN_DRAG_ID - || blockId == BlockStateValues.JAVA_BUBBLE_COLUMN_UPWARD_ID) { + Block block = getBlock(boundingBox.getBottomCenter().toInt()); + if (block == Blocks.WATER || block == Blocks.BUBBLE_COLUMN) { return 1.0f; } - if (blockId == BlockStateValues.JAVA_SOUL_SAND_ID || blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + if (block == Blocks.SOUL_SAND || block == Blocks.HONEY_BLOCK) { return 0.4f; } - blockId = getBlockAt(getVelocityAffectingPos()); - if (blockId == BlockStateValues.JAVA_SOUL_SAND_ID || blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + block = getBlock(getVelocityAffectingPos()); + if (block == Blocks.SOUL_SAND || block == Blocks.HONEY_BLOCK) { return 0.4f; } @@ -756,13 +761,13 @@ protected float getVelocityMultiplier() { } protected float getJumpVelocityMultiplier() { - int blockId = getBlockAt(boundingBox.getBottomCenter().toInt()); - if (blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + Block block = getBlock(boundingBox.getBottomCenter().toInt()); + if (block == Blocks.HONEY_BLOCK) { return 0.5f; } - blockId = getBlockAt(getVelocityAffectingPos()); - if (blockId == BlockStateValues.JAVA_HONEY_BLOCK_ID) { + block = getBlock(getVelocityAffectingPos()); + if (block == Blocks.HONEY_BLOCK) { return 0.5f; } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index 791134a776c..9e3a58faa18 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -71,7 +71,6 @@ public final class BlockStateValues { private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap(); private static final Int2IntMap LAVA_LEVEL = new Int2IntOpenHashMap(); private static final IntSet ALL_CLIMBABLE = new IntOpenHashSet(); - private static final Int2ObjectMap LADDER_DIRECTION = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap OPEN_TRAPDOOR_DIRECTION = new Int2ObjectOpenHashMap<>(); public static final int JAVA_AIR_ID = 0; @@ -232,10 +231,6 @@ public static void storeBlockStateValues(String javaId, int javaBlockState, Json ALL_CLIMBABLE.add(javaBlockState); } - if (javaId.startsWith("minecraft:ladder")) { - LADDER_DIRECTION.put(javaBlockState, getBlockDirection(javaId)); - } - if (javaId.contains("_trapdoor[") && javaId.contains("open=true")) { OPEN_TRAPDOOR_DIRECTION.put(javaBlockState, getBlockDirection(javaId)); } @@ -614,17 +609,6 @@ public static float getSlipperiness(int state) { }; } - /** - * Get the direction of a ladder. - * Used when determining if an entity is climbing - * - * @param state BlockState of the block - * @return The ladder's direction, or null if not a ladder - */ - public static @Nullable Direction getLadderDirection(int state) { - return LADDER_DIRECTION.get(state); - } - /** * Get the direction of an open trapdoor. * Used when determining if an entity is climbing diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index cafaeb2d7cd..79c8d4d092a 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -52,7 +52,7 @@ import java.util.Locale; public class CollisionManager { - public static final BlockCollision SOLID_COLLISION = new SolidCollision(""); + public static final BlockCollision SOLID_COLLISION = new SolidCollision(null); public static final BlockCollision FLUID_COLLISION = new OtherCollision(new BoundingBox[]{new BoundingBox(0.5, 0.25, 0.5, 1, 0.5, 1)}); private final GeyserSession session; From a21ca9922b43b07b01ac49d44f1ec708e215b5e3 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 17 May 2024 19:16:14 -0400 Subject: [PATCH 08/26] Update BlockRegistryPopulator --- .../populator/BlockRegistryPopulator.java | 47 +++++-------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index 9b7a1b30e3a..64b466f12e0 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -138,7 +138,7 @@ private static void registerBedrockBlocks() { List vanillaBlockStates; List blockStates; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(String.format("bedrock/block_palette.%s.nbt", palette.key())); - NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)), true, true)) { + NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)), true, true)) { NbtMap blockPalette = (NbtMap) nbtInputStream.readTag(); vanillaBlockStates = new ArrayList<>(blockPalette.getList("blocks", NbtType.COMPOUND)); @@ -260,7 +260,7 @@ private static void registerBedrockBlocks() { bedrockDefinition = customBlockStateDefinitions.get(blockStateOverride); if (bedrockDefinition == null) { throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID! Custom block override: \n" + - blockStateOverride); + blockStateOverride); } } @@ -409,7 +409,6 @@ private static void registerJavaBlocks() { Deque cleanIdentifiers = new ArrayDeque<>(); int javaRuntimeId = -1; - int cobwebBlockId = -1; int furnaceRuntimeId = -1; int furnaceLitRuntimeId = -1; int honeyBlockRuntimeId = -1; @@ -417,10 +416,6 @@ private static void registerJavaBlocks() { int spawnerRuntimeId = -1; int uniqueJavaId = -1; int waterRuntimeId = -1; - int bubbleColumnUpwardRuntimeId = -1; - int bubbleColumnDragRuntimeId = -1; - int soulSandRuntimeId = -1; - int iceRuntimeId = -1; Iterator> blocksIterator = blocksJson.fields(); while (blocksIterator.hasNext()) { javaRuntimeId++; @@ -489,10 +484,7 @@ private static void registerJavaBlocks() { // It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern()); - if (javaId.contains("cobweb")) { - cobwebBlockId = uniqueJavaId; - - } else if (javaId.startsWith("minecraft:furnace[facing=north")) { + if (javaId.startsWith("minecraft:furnace[facing=north")) { if (javaId.contains("lit=true")) { furnaceLitRuntimeId = javaRuntimeId; } else { @@ -508,24 +500,9 @@ private static void registerJavaBlocks() { honeyBlockRuntimeId = javaRuntimeId; } else if (javaId.equals("minecraft:slime_block")) { slimeBlockRuntimeId = javaRuntimeId; - } else if (javaId.startsWith("minecraft:bubble_column")) { - if (javaId.contains("drag=true")) { - bubbleColumnDragRuntimeId = javaRuntimeId; - } else { - bubbleColumnUpwardRuntimeId = javaRuntimeId; - } - } else if (javaId.equals("minecraft:soul_sand")) { - soulSandRuntimeId = javaRuntimeId; - } else if (javaId.equals("minecraft:ice")) { - iceRuntimeId = javaRuntimeId; } } - if (cobwebBlockId == -1) { - throw new AssertionError("Unable to find cobwebs in palette"); - } - BlockStateValues.JAVA_COBWEB_ID = cobwebBlockId; - if (furnaceRuntimeId == -1) { throw new AssertionError("Unable to find furnace in palette"); } @@ -570,15 +547,15 @@ private static void registerJavaBlocks() { int stateRuntimeId = javaBlockState.javaId(); String pistonBehavior = javaBlockState.pistonBehavior(); BlockMapping blockMapping = BlockMapping.builder() - .canBreakWithHand(javaBlockState.canBreakWithHand()) - .pickItem(javaBlockState.pickItem()) - .isNonVanilla(true) - .javaIdentifier(javaId) - .javaBlockId(javaBlockState.stateGroupId()) - .hardness(javaBlockState.blockHardness()) - .pistonBehavior(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior)) - .isBlockEntity(javaBlockState.hasBlockEntity()) - .build(); + .canBreakWithHand(javaBlockState.canBreakWithHand()) + .pickItem(javaBlockState.pickItem()) + .isNonVanilla(true) + .javaIdentifier(javaId) + .javaBlockId(javaBlockState.stateGroupId()) + .hardness(javaBlockState.blockHardness()) + .pistonBehavior(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior)) + .isBlockEntity(javaBlockState.hasBlockEntity()) + .build(); Block.Builder builder = Block.builder() .destroyTime(javaBlockState.blockHardness()); From fa7b9fe06a6a70307fac1d8ba718833b980ed7b4 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 17 May 2024 21:39:16 -0400 Subject: [PATCH 09/26] Update blocks --- .../geyser/entity/vehicle/VehicleComponent.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 16dec67594e..50ccf656aa0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -41,6 +41,7 @@ import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.Fluid; import org.geysermc.geyser.level.block.property.Properties; +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.BoundingBox; @@ -317,7 +318,7 @@ protected float getLogicalFluidHeight(Fluid fluidType, int blockId) { } protected boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { - if (BlockState.of(adjacentBlockId).block() == Blocks.ICE) { + if (BlockState.of(adjacentBlockId).is(Blocks.ICE)) { return false; } @@ -474,9 +475,9 @@ protected void applyBlockCollisionEffects() { for (iter.reset(); iter.hasNext(); iter.next()) { BlockState blockState = BlockState.of(blocks[iter.getIteration()]); - if (blockState.block() == Blocks.HONEY_BLOCK) { + if (blockState.is(Blocks.HONEY_BLOCK)) { onHoneyBlockCollision(); - } else if (blockState.block() == Blocks.BUBBLE_COLUMN) { + } else if (blockState.is(Blocks.BUBBLE_COLUMN)) { onBubbleColumnCollision(blockState.getValue(Properties.DRAG)); } } @@ -542,9 +543,9 @@ protected boolean travel(float speed) { boolean bounced = false; if (onGround) { Vector3i landingPos = newPos.sub(0, 0.2f, 0).toInt(); - int landingBlockId = getBlockId(landingPos); + Block landingBlock = getBlock(landingPos); - if (BlockState.of(landingBlockId).block() == Blocks.SLIME_BLOCK) { + if (landingBlock == Blocks.SLIME_BLOCK) { motion = Vector3f.from(motion.getX(), -motion.getY(), motion.getZ()); bounced = true; @@ -554,7 +555,7 @@ protected boolean travel(float speed) { float mul = 0.4f + absY * 0.2f; motion = motion.mul(mul, 1.0f, mul); } - } else if (BlockStateValues.getBedColor(landingBlockId) != -1) { // If bed + } else if (landingBlock instanceof BedBlock) { motion = Vector3f.from(motion.getX(), -motion.getY() * 0.66f, motion.getZ()); bounced = true; } @@ -599,7 +600,7 @@ protected boolean isClimbing() { Direction openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); if (openTrapdoorDirection != null) { BlockState ladder = getBlockState(blockPos.down()); - return ladder.block() == Blocks.LADDER && ladder.getValue(Properties.HORIZONTAL_FACING) == openTrapdoorDirection; + return ladder.is(Blocks.LADDER) && ladder.getValue(Properties.HORIZONTAL_FACING) == openTrapdoorDirection; } return false; From f490af06de86b81bc8d9946975d6d93d7bb72644 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sat, 18 May 2024 19:02:42 -0400 Subject: [PATCH 10/26] Support step height attribute --- .../geysermc/geyser/entity/type/LivingEntity.java | 5 +++++ .../entity/type/living/animal/PigEntity.java | 2 +- .../entity/type/living/animal/StriderEntity.java | 2 +- .../type/living/animal/horse/CamelEntity.java | 5 ----- .../entity/vehicle/BoostableVehicleComponent.java | 4 ++-- .../entity/vehicle/CamelVehicleComponent.java | 2 +- .../geyser/entity/vehicle/ClientVehicle.java | 4 ---- .../geyser/entity/vehicle/VehicleComponent.java | 15 ++++++++++----- .../geyser/level/physics/BoundingBox.java | 8 +++++--- 9 files changed, 25 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 294320d15e3..71ce0c5aec4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -408,6 +408,11 @@ protected void updateAttribute(Attribute javaAttribute, List newA clientVehicle.getVehicleComponent().setMoveSpeed(attributeData.getValue()); } } + case GENERIC_STEP_HEIGHT -> { + if (this instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().setStepHeight((float) 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_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index f4813015d61..f7cf9603006 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -47,7 +47,7 @@ import java.util.UUID; public class PigEntity extends AnimalEntity implements ClientVehicle { - private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this); + private final BoostableVehicleComponent 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); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index 3392c3f1aaf..ba6fa3633ce 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -50,7 +50,7 @@ public class StriderEntity extends AnimalEntity implements ClientVehicle { - private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this); + private final BoostableVehicleComponent 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) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index 4bd6e848141..3c0bf1a70e0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -171,11 +171,6 @@ public float getVehicleSpeed() { return moveSpeed; } - @Override - public float getStepHeight() { - return 1.5f; - } - @Override public boolean canClimb() { return false; diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java index 5ed9b55376c..039f8ce32df 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java @@ -32,8 +32,8 @@ public class BoostableVehicleComponent e private int boostLength; private int boostTicks = 1; - public BoostableVehicleComponent(T vehicle) { - super(vehicle); + public BoostableVehicleComponent(T vehicle, float stepHeight) { + super(vehicle, stepHeight); } public void startBoost(int boostLength) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index 97f7ed6d701..5a475f211a7 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -46,7 +46,7 @@ public class CamelVehicleComponent extends VehicleComponent { private int jumpBoost; public CamelVehicleComponent(CamelEntity vehicle) { - super(vehicle); + super(vehicle, 1.5f); } public void startDashCooldown() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index b0229a9a319..5a936bc6136 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -40,10 +40,6 @@ default boolean canWalkOnLava() { return false; } - default float getStepHeight() { - return 1.0f; - } - default boolean canClimb() { return true; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 50ccf656aa0..631d3ec12e7 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -73,12 +73,15 @@ public class VehicleComponent { protected final T vehicle; protected final BoundingBox boundingBox; + protected float stepHeight; protected float moveSpeed; protected int levitation; protected boolean slowFalling; - public VehicleComponent(T vehicle) { + public VehicleComponent(T vehicle, float stepHeight) { this.vehicle = vehicle; + this.stepHeight = stepHeight; + this.moveSpeed = GeyserAttributeType.MOVEMENT_SPEED.getDefaultValue(); double width = Double.parseDouble(Float.toString(vehicle.getBoundingBoxWidth())); double height = Double.parseDouble(Float.toString(vehicle.getBoundingBoxHeight())); @@ -88,8 +91,6 @@ public VehicleComponent(T vehicle) { vehicle.getPosition().getZ(), width, height, width ); - - this.moveSpeed = GeyserAttributeType.MOVEMENT_SPEED.getDefaultValue(); } public void setWidth(float width) { @@ -136,6 +137,10 @@ public float getMoveSpeed() { return moveSpeed; } + public void setStepHeight(float stepHeight) { + this.stepHeight = MathUtils.clamp(stepHeight, 1.0f, 10.0f); + } + public void onDismount() { // } @@ -174,7 +179,7 @@ protected ObjectDoublePair updateFluidMovement() { if (lavaHeight > 0 && vehicle.getDefinition().entityType() == EntityType.STRIDER) { Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || getBlock(boundingBox.getBottomCenter().toInt()) == Blocks.LAVA) { + if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || getBlock(blockPos.up()) == Blocks.LAVA) { vehicle.setMotion(vehicle.getMotion().mul(0.5f).add(0, 0.05f, 0)); } else { vehicle.setOnGround(true); @@ -528,7 +533,7 @@ protected boolean travel(float speed) { } Vector3d correctedMovement = vehicle.getSession().getCollisionManager().correctMovement( - motion.toDouble(), boundingBox, vehicle.isOnGround(), vehicle.getStepHeight(), true, vehicle.canWalkOnLava() + motion.toDouble(), boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() ); boundingBox.translate(correctedMovement); diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java index 74d2f5e872f..c4245964b9d 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java @@ -33,6 +33,8 @@ @Data @AllArgsConstructor public class BoundingBox implements Cloneable { + private static final double EPSILON = 1.0E-7; + private double middleX; private double middleY; private double middleZ; @@ -105,9 +107,9 @@ public Vector3d getBottomCenter() { private boolean checkOverlapInAxis(double xOffset, double yOffset, double zOffset, BoundingBox otherBox, Axis axis) { return switch (axis) { - case X -> (sizeX + otherBox.getSizeX()) - Math.abs((middleX + xOffset) - otherBox.getMiddleX()) * 2 > CollisionManager.COLLISION_TOLERANCE; - case Y -> (sizeY + otherBox.getSizeY()) - Math.abs((middleY + yOffset) - otherBox.getMiddleY()) * 2 > CollisionManager.COLLISION_TOLERANCE; - case Z -> (sizeZ + otherBox.getSizeZ()) - Math.abs((middleZ + zOffset) - otherBox.getMiddleZ()) * 2 > CollisionManager.COLLISION_TOLERANCE; + case X -> (sizeX + otherBox.getSizeX()) - Math.abs((middleX + xOffset) - otherBox.getMiddleX()) * 2 > EPSILON; + case Y -> (sizeY + otherBox.getSizeY()) - Math.abs((middleY + yOffset) - otherBox.getMiddleY()) * 2 > EPSILON; + case Z -> (sizeZ + otherBox.getSizeZ()) - Math.abs((middleZ + zOffset) - otherBox.getMiddleZ()) * 2 > EPSILON; }; } From 30a37fa8a53ce9a016b67ead4fefa03c2dc22b05 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 20 May 2024 01:05:46 -0400 Subject: [PATCH 11/26] Use climbable block tag and TrapDoorBlock --- .../entity/vehicle/VehicleComponent.java | 16 +++++++----- .../geyser/level/block/BlockStateValues.java | 26 ------------------- .../geyser/session/cache/tags/BlockTag.java | 3 ++- 3 files changed, 11 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 631d3ec12e7..bfa35f64c69 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -44,9 +44,11 @@ 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.block.type.TrapDoorBlock; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.CollisionManager; import org.geysermc.geyser.level.physics.Direction; +import org.geysermc.geyser.session.cache.tags.BlockTag; import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.SolidCollision; import org.geysermc.geyser.util.BlockUtils; @@ -379,7 +381,7 @@ protected void lavaMovement(double lavaHeight) { protected void landMovement() { float gravity = getGravity(); - float slipperiness = BlockStateValues.getSlipperiness(getBlockId(getVelocityAffectingPos())); + float slipperiness = BlockStateValues.getSlipperiness(getBlockState(getVelocityAffectingPos())); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); @@ -595,17 +597,17 @@ protected boolean isClimbing() { } Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - int blockId = getBlockId(blockPos); + BlockState blockState = getBlockState(blockPos); - if (BlockStateValues.isClimbable(blockId)) { + if (vehicle.getSession().getTagCache().is(BlockTag.CLIMBABLE, blockState.block())) { return true; } // Check if the vehicle is in an open trapdoor with a ladder of the same direction under it - Direction openTrapdoorDirection = BlockStateValues.getOpenTrapdoorDirection(blockId); - if (openTrapdoorDirection != null) { - BlockState ladder = getBlockState(blockPos.down()); - return ladder.is(Blocks.LADDER) && ladder.getValue(Properties.HORIZONTAL_FACING) == openTrapdoorDirection; + if (blockState.block() instanceof TrapDoorBlock && blockState.getValue(Properties.OPEN)) { + BlockState ladderState = getBlockState(blockPos.down()); + return ladderState.is(Blocks.LADDER) && + ladderState.getValue(Properties.HORIZONTAL_FACING) == blockState.getValue(Properties.HORIZONTAL_FACING); } return false; diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index 627a89a247d..15cc23a6a83 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -28,7 +28,6 @@ import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.level.block.type.BlockState; @@ -50,8 +49,6 @@ public final class BlockStateValues { private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet(); private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap(); private static final Int2IntMap LAVA_LEVEL = new Int2IntOpenHashMap(); - private static final IntSet ALL_CLIMBABLE = new IntOpenHashSet(); - private static final Int2ObjectMap OPEN_TRAPDOOR_DIRECTION = new Int2ObjectOpenHashMap<>(); public static int JAVA_WATER_ID; @@ -99,14 +96,6 @@ public static void storeBlockStateValues(String javaId, int javaBlockState) { HORIZONTAL_FACING_JIGSAWS.add(javaBlockState); } } - - if (javaId.contains("vine") || javaId.startsWith("minecraft:ladder") || javaId.startsWith("minecraft:scaffolding")) { - ALL_CLIMBABLE.add(javaBlockState); - } - - if (javaId.contains("_trapdoor[") && javaId.contains("open=true")) { - OPEN_TRAPDOOR_DIRECTION.put(javaBlockState, getBlockDirection(javaId)); - } } /** @@ -288,10 +277,6 @@ public static double getLavaHeight(int state) { return -1; } - public static boolean isClimbable(int state) { - return ALL_CLIMBABLE.contains(state); - } - /** * Get the slipperiness of a block. * This is used in ItemEntity to calculate the friction on an item as it slides across the ground @@ -313,17 +298,6 @@ public static float getSlipperiness(BlockState state) { return 0.6f; } - /** - * Get the direction of an open trapdoor. - * Used when determining if an entity is climbing - * - * @param state BlockState of the block - * @return The open trapdoor's direction, or null if not an open trapdoor - */ - public static @Nullable Direction getOpenTrapdoorDirection(int state) { - return OPEN_TRAPDOOR_DIRECTION.get(state); - } - private static Direction getBlockDirection(String javaId) { if (javaId.contains("down")) { return Direction.DOWN; diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java index 7017ad55c84..483f8e5efa8 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java @@ -36,7 +36,8 @@ public enum BlockTag { SHOVEL_EFFECTIVE("mineable/shovel"), NEEDS_STONE_TOOL("needs_stone_tool"), NEEDS_IRON_TOOL("needs_iron_tool"), - NEEDS_DIAMOND_TOOL("needs_diamond_tool"); + NEEDS_DIAMOND_TOOL("needs_diamond_tool"), + CLIMBABLE("climbable"); BlockTag(String identifier) { register(identifier, this); From 16d766dfd124d35ff73b28affbd80509d83245b4 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 20 May 2024 04:25:25 -0400 Subject: [PATCH 12/26] Lock camel rotation if stationary --- .../entity/vehicle/CamelVehicleComponent.java | 9 ++++++ .../entity/vehicle/VehicleComponent.java | 28 +++++++++++-------- core/src/main/resources/mappings | 2 +- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index 5a475f211a7..eb100aa0b6b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.entity.vehicle; import lombok.Setter; +import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; @@ -112,6 +113,14 @@ protected Vector3f getInputVelocity(float speed) { return inputVelocity; } + @Override + protected Vector2f getVehicleRotation() { + if (isStationary()) { + return Vector2f.from(vehicle.getYaw(), vehicle.getPitch()); + } + return super.getVehicleRotation(); + } + /** * Checks if the camel is sitting * or transitioning to standing pose. diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index bfa35f64c69..dbfccc137fd 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -636,10 +636,14 @@ protected Vector3f getInputVelocity(float speed) { return Vector3f.from(input.getX() * cos - input.getY() * sin, 0, input.getY() * cos + input.getX() * sin); } + protected Vector2f getVehicleRotation() { + LivingEntity player = vehicle.getSession().getPlayerEntity(); + return Vector2f.from(player.getYaw(), player.getPitch() * 0.5f); + } + protected void moveVehicle(Vector3d javaPos, boolean isOnGround) { Vector3f bedrockPos = javaPos.toFloat(); - float yaw = vehicle.getSession().getPlayerEntity().getYaw(); - float pitch = vehicle.getSession().getPlayerEntity().getPitch() * 0.5f; + Vector2f rotation = getVehicleRotation(); MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket(); moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId()); @@ -663,27 +667,27 @@ protected void moveVehicle(Vector3d javaPos, boolean isOnGround) { } vehicle.setPosition(bedrockPos); - if (vehicle.getYaw() != yaw) { + if (vehicle.getYaw() != rotation.getX()) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW); - moveEntityDeltaPacket.setYaw(yaw); - vehicle.setYaw(yaw); + moveEntityDeltaPacket.setYaw(rotation.getX()); + vehicle.setYaw(rotation.getX()); } - if (vehicle.getPitch() != pitch) { + if (vehicle.getPitch() != rotation.getY()) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH); - moveEntityDeltaPacket.setPitch(pitch); - vehicle.setPitch(pitch); + moveEntityDeltaPacket.setPitch(rotation.getY()); + vehicle.setPitch(rotation.getY()); } - if (vehicle.getHeadYaw() != yaw) { // Same as yaw + if (vehicle.getHeadYaw() != rotation.getX()) { // Same as yaw moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW); - moveEntityDeltaPacket.setHeadYaw(yaw); - vehicle.setHeadYaw(yaw); + moveEntityDeltaPacket.setHeadYaw(rotation.getX()); + vehicle.setHeadYaw(rotation.getX()); } if (!moveEntityDeltaPacket.getFlags().isEmpty()) { vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket); } - ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), yaw, pitch); + ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), rotation.getX(), rotation.getY()); vehicle.getSession().sendDownstreamPacket(moveVehiclePacket); vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis()); } diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 6b661f0d517..968a22bbab0 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 6b661f0d517d895aebc1f55a25d2c86f033beb1d +Subproject commit 968a22bbab02d7d003c5b451a40d8bb2439b0d97 From de5b1a2c315ddf1e1f15a2b13fee1fabe81b757d Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 20 May 2024 05:39:02 -0400 Subject: [PATCH 13/26] Fix boost ticking --- .../entity/type/living/animal/PigEntity.java | 14 +++++++------- .../type/living/animal/StriderEntity.java | 18 +++++++++--------- .../vehicle/BoostableVehicleComponent.java | 12 +++++++----- .../entity/vehicle/CamelVehicleComponent.java | 4 ++-- .../geyser/entity/vehicle/ClientVehicle.java | 4 ++-- .../entity/vehicle/VehicleComponent.java | 6 ++++-- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index f7cf9603006..d6f0ff95e92 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -102,11 +102,8 @@ public VehicleComponent getVehicleComponent() { } @Override - public boolean isClientControlled() { - return getFlag(EntityFlag.SADDLED) - && !passengers.isEmpty() - && passengers.get(0) == session.getPlayerEntity() - && session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK); + public Vector2f getAdjustedInput(Vector2f input) { + return Vector2f.UNIT_Y; } @Override @@ -115,7 +112,10 @@ public float getVehicleSpeed() { } @Override - public Vector2f getAdjustedInput(Vector2f input) { - return Vector2f.UNIT_Y; + public boolean isClientControlled() { + return getFlag(EntityFlag.SADDLED) + && !passengers.isEmpty() + && passengers.get(0) == session.getPlayerEntity() + && session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index ba6fa3633ce..bee133c53fd 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -144,26 +144,26 @@ public void setBoost(IntEntityMetadata entityMetadata) { } @Override - public @NonNull VehicleComponent getVehicleComponent() { + public VehicleComponent getVehicleComponent() { return vehicleComponent; } @Override - public @NonNull Vector2f getAdjustedInput(Vector2f input) { + public Vector2f getAdjustedInput(Vector2f input) { return Vector2f.UNIT_Y; } @Override - public boolean isClientControlled() { - // Does not require saddle - return !passengers.isEmpty() - && passengers.get(0) == session.getPlayerEntity() - && session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK); + public float getVehicleSpeed() { + return vehicleComponent.getMoveSpeed() * (isCold ? 0.35f : 0.55f) * vehicleComponent.getBoostMultiplier(); } @Override - public float getVehicleSpeed() { - return vehicleComponent.getMoveSpeed() * (isCold ? 0.35f : 0.55f) * vehicleComponent.getBoostMultiplier(); + public boolean isClientControlled() { + return getFlag(EntityFlag.SADDLED) + && !passengers.isEmpty() + && passengers.get(0) == session.getPlayerEntity() + && session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java index 039f8ce32df..20d4e7fbf2a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java @@ -38,12 +38,12 @@ public BoostableVehicleComponent(T vehicle, float stepHeight) { public void startBoost(int boostLength) { this.boostLength = boostLength; - this.boostTicks = 0; + this.boostTicks = 1; } public float getBoostMultiplier() { if (isBoosting()) { - return 1.0f + 1.15f * TrigMath.sin((float)boostTicks / (float)boostLength * TrigMath.PI); + return 1.0f + 1.15f * TrigMath.sin((float) boostTicks / (float) boostLength * TrigMath.PI); } return 1.0f; } @@ -53,10 +53,12 @@ public boolean isBoosting() { } @Override - public void tickVehicle() { - super.tickVehicle(); - if (isBoosting()) { + public boolean tickVehicle() { + boolean clientControlled = super.tickVehicle(); + if (clientControlled && isBoosting()) { boostTicks++; } + + return clientControlled; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index eb100aa0b6b..9c866804b04 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -55,7 +55,7 @@ public void startDashCooldown() { } @Override - public void tickVehicle() { + public boolean tickVehicle() { if (this.dashTick != 0) { if (vehicle.getSession().getTicks() > this.dashTick) { vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); @@ -68,7 +68,7 @@ public void tickVehicle() { vehicle.setFlag(EntityFlag.CAN_DASH, vehicle.getFlag(EntityFlag.SADDLED) && !isStationary()); vehicle.updateBedrockMetadata(); - super.tickVehicle(); + return super.tickVehicle(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index 5a936bc6136..e6aaf1daac0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -32,10 +32,10 @@ public interface ClientVehicle { Vector2f getAdjustedInput(Vector2f input); - boolean isClientControlled(); - float getVehicleSpeed(); + boolean isClientControlled(); + default boolean canWalkOnLava() { return false; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index dbfccc137fd..d27719ad26f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -147,9 +147,9 @@ public void onDismount() { // } - public void tickVehicle() { + public boolean tickVehicle() { if (!vehicle.isClientControlled()) { - return; + return false; } ObjectDoublePair fluidHeight = updateFluidMovement(); @@ -164,6 +164,8 @@ public void tickVehicle() { } case EMPTY -> landMovement(); } + + return true; } protected ObjectDoublePair updateFluidMovement() { From 62822e0a2d183ff200380fc1ca95d7b2060089b8 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Tue, 21 May 2024 21:28:24 -0400 Subject: [PATCH 14/26] Keep cache of surrounding blocks --- .../vehicle/BoostableVehicleComponent.java | 1 + .../entity/vehicle/CamelVehicleComponent.java | 12 +- .../entity/vehicle/VehicleComponent.java | 236 +++++++++++------- gradle/libs.versions.toml | 2 +- 4 files changed, 153 insertions(+), 98 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java index 20d4e7fbf2a..febe78ab448 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java @@ -56,6 +56,7 @@ public boolean isBoosting() { public boolean tickVehicle() { boolean clientControlled = super.tickVehicle(); if (clientControlled && isBoosting()) { + // TODO: the client ticks boost if any player is controlling boostTicks++; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index 9c866804b04..de9b83a380b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -78,22 +78,22 @@ public void onDismount() { } @Override - protected boolean travel(float speed) { + protected boolean travel(VehicleContext ctx, float speed) { if (vehicle.isOnGround() && isStationary()) { vehicle.setMotion(vehicle.getMotion().mul(0, 1, 0)); } - return super.travel(speed); + return super.travel(ctx, speed); } @Override - protected Vector3f getInputVelocity(float speed) { + protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { if (isStationary()) { return Vector3f.ZERO; } SessionPlayerEntity player = vehicle.getSession().getPlayerEntity(); - Vector3f inputVelocity = super.getInputVelocity(speed); + Vector3f inputVelocity = super.getInputVelocity(ctx, speed); float jumpStrength = player.getVehicleJumpStrength(); if (jumpStrength > 0) { @@ -106,8 +106,8 @@ protected Vector3f getInputVelocity(float speed) { } return inputVelocity.add(Vector3f.createDirectionDeg(0, -player.getYaw()) - .mul(22.2222f * jumpStrength * this.moveSpeed * getVelocityMultiplier()) - .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier() + (this.jumpBoost * 0.1f)))); + .mul(22.2222f * jumpStrength * this.moveSpeed * getVelocityMultiplier(ctx)) + .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier(ctx) + (this.jumpBoost * 0.1f)))); } return inputVelocity; diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index d27719ad26f..60da6e3ca1f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -152,23 +152,26 @@ public boolean tickVehicle() { return false; } - ObjectDoublePair fluidHeight = updateFluidMovement(); + VehicleContext ctx = new VehicleContext(); + ctx.loadSurroundingBlocks(); + + ObjectDoublePair fluidHeight = updateFluidMovement(ctx); switch (fluidHeight.left()) { - case WATER -> waterMovement(); + case WATER -> waterMovement(ctx); case LAVA -> { - if (vehicle.canWalkOnLava() && getBlock(boundingBox.getBottomCenter().toInt()) == Blocks.LAVA) { - landMovement(); + if (vehicle.canWalkOnLava() && ctx.centerBlock().is(Blocks.LAVA)) { + landMovement(ctx); } else { - lavaMovement(fluidHeight.rightDouble()); + lavaMovement(ctx, fluidHeight.rightDouble()); } } - case EMPTY -> landMovement(); + case EMPTY -> landMovement(ctx); } return true; } - protected ObjectDoublePair updateFluidMovement() { + protected ObjectDoublePair updateFluidMovement(VehicleContext ctx) { BoundingBox box = boundingBox.clone(); box.expand(-0.001); @@ -176,14 +179,14 @@ protected ObjectDoublePair updateFluidMovement() { Vector3d max = box.getMax(); BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ()); - int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); - double waterHeight = getFluidHeightAndApplyMovement(Fluid.WATER, 0.014, min.getY(), iter, blocks); - double lavaHeight = getFluidHeightAndApplyMovement(Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY(), iter, blocks); + double waterHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.WATER, 0.014, min.getY()); + double lavaHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY()); if (lavaHeight > 0 && vehicle.getDefinition().entityType() == EntityType.STRIDER) { - Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || getBlock(blockPos.up()) == Blocks.LAVA) { + Vector3i blockPos = ctx.centerPos().toInt(); + if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) + || ctx.getBlock(blockPos.up()).is(Blocks.LAVA)) { vehicle.setMotion(vehicle.getMotion().mul(0.5f).add(0, 0.05f, 0)); } else { vehicle.setOnGround(true); @@ -202,13 +205,13 @@ protected ObjectDoublePair updateFluidMovement() { return EMPTY_FLUID_PAIR; } - protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, double minY, BlockPositionIterator iter, int[] blocks) { + protected double getFluidHeightAndApplyMovement(VehicleContext ctx, BlockPositionIterator iter, Fluid fluid, double speed, double minY) { Vector3d totalVelocity = Vector3d.ZERO; double maxFluidHeight = 0; int fluidBlocks = 0; for (iter.reset(); iter.hasNext(); iter.next()) { - int blockId = blocks[iter.getIteration()]; + int blockId = ctx.getBlockId(iter); if (BlockStateValues.getFluid(blockId) != fluid) { continue; } @@ -225,7 +228,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl Vector3d velocity = Vector3d.ZERO; for (Direction direction : Direction.HORIZONTAL) { Vector3i adjacentBlockPos = blockPos.add(direction.getUnitVector()); - int adjacentBlockId = getBlockId(adjacentBlockPos); + int adjacentBlockId = ctx.getBlockId(adjacentBlockPos); Fluid adjacentFluid = BlockStateValues.getFluid(adjacentBlockId); float fluidHeightDiff = 0; @@ -236,7 +239,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl // check if there is a fluid under it BlockCollision adjacentBlockCollision = BlockUtils.getCollision(adjacentBlockId); if (adjacentBlockCollision == null) { - float adjacentFluidHeight = getLogicalFluidHeight(fluid, getBlockId(adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); + float adjacentFluidHeight = getLogicalFluidHeight(fluid, ctx.getBlockId(adjacentBlockPos.add(Direction.DOWN.getUnitVector()))); if (adjacentFluidHeight != -1) { // Only care about same type of fluid fluidHeightDiff = getLogicalFluidHeight(fluid, blockId) - (adjacentFluidHeight - MAX_LOGICAL_FLUID_HEIGHT); } @@ -255,7 +258,7 @@ protected double getFluidHeightAndApplyMovement(Fluid fluid, double speed, doubl if (!flowBlocked) { Vector3i blockPosUp = blockPos.up(); for (Direction direction : Direction.HORIZONTAL) { - flowBlocked = isFlowBlocked(fluid, getBlockId(blockPosUp.add(direction.getUnitVector()))); + flowBlocked = isFlowBlocked(fluid, ctx.getBlockId(blockPosUp.add(direction.getUnitVector()))); if (flowBlocked) { break; } @@ -302,18 +305,6 @@ protected Vector3d javaNormalize(Vector3d vec) { return len < 1.0E-4 ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len); } - protected int getBlockId(Vector3i blockPos) { - return vehicle.getSession().getGeyser().getWorldManager().getBlockAt(vehicle.getSession(), blockPos); - } - - protected BlockState getBlockState(Vector3i blockPos) { - return BlockState.of(getBlockId(blockPos)); - } - - protected Block getBlock(Vector3i blockPos) { - return getBlockState(blockPos).block(); - } - protected float getWorldFluidHeight(Fluid fluidType, int blockId) { return (float) switch (fluidType) { case WATER -> BlockStateValues.getWaterHeight(blockId); @@ -339,33 +330,34 @@ protected boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { return BlockUtils.getCollision(adjacentBlockId) instanceof SolidCollision; } - protected void waterMovement() { + protected void waterMovement(VehicleContext ctx) { float gravity = getGravity(); float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier - double originalY = boundingBox.getBottomCenter().getY(); + double originalY = ctx.centerPos().getY(); boolean falling = vehicle.getMotion().getY() <= 0; // NOT IMPLEMENTED: depth strider and dolphins grace - boolean horizontalCollision = travel(0.02f); - if (horizontalCollision && isClimbing()) { + boolean horizontalCollision = travel(ctx, 0.02f); + + if (horizontalCollision && isClimbing(ctx)) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.2f, vehicle.getMotion().getZ())); } vehicle.setMotion(vehicle.getMotion().mul(drag, 0.8f, drag)); vehicle.setMotion(getFluidGravity(gravity, falling)); - if (horizontalCollision && shouldApplyFluidJumpBoost(originalY)) { + if (horizontalCollision && shouldApplyFluidJumpBoost(ctx, originalY)) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); } } - protected void lavaMovement(double lavaHeight) { + protected void lavaMovement(VehicleContext ctx, double lavaHeight) { float gravity = getGravity(); - double originalY = boundingBox.getBottomCenter().getY(); + double originalY = ctx.centerPos().getY(); boolean falling = vehicle.getMotion().getY() <= 0; - boolean horizontalCollision = travel(0.02f); + boolean horizontalCollision = travel(ctx, 0.02f); if (lavaHeight <= (boundingBox.getSizeY() * 0.85 < 0.4 ? 0.0 : 0.4)) { // Swim height vehicle.setMotion(vehicle.getMotion().mul(0.5f, 0.8f, 0.5f)); @@ -376,19 +368,20 @@ protected void lavaMovement(double lavaHeight) { vehicle.setMotion(vehicle.getMotion().down(gravity / 4.0f)); - if (horizontalCollision && shouldApplyFluidJumpBoost(originalY)) { + if (horizontalCollision && shouldApplyFluidJumpBoost(ctx, originalY)) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); } } - protected void landMovement() { + protected void landMovement(VehicleContext ctx) { float gravity = getGravity(); - float slipperiness = BlockStateValues.getSlipperiness(getBlockState(getVelocityAffectingPos())); + float slipperiness = BlockStateValues.getSlipperiness(ctx.velocityAffectingBlock()); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); - boolean horizontalCollision = travel(speed); - if (isClimbing()) { + boolean horizontalCollision = travel(ctx, speed); + + if (isClimbing(ctx)) { vehicle.setMotion(getClimbingSpeed(horizontalCollision)); // NOT IMPLEMENTED: climbing in powdered snow } @@ -403,16 +396,14 @@ protected void landMovement() { vehicle.setMotion(vehicle.getMotion().mul(drag, 0.98f, drag)); } - protected boolean shouldApplyFluidJumpBoost(double originalY) { + protected boolean shouldApplyFluidJumpBoost(VehicleContext ctx, double originalY) { BoundingBox box = boundingBox.clone(); - box.translate(vehicle.getMotion().toDouble().up(0.6f - boundingBox.getBottomCenter().getY() + originalY)); + box.translate(vehicle.getMotion().toDouble().up(0.6f - ctx.centerPos().getY() + originalY)); box.expand(-1.0E-7); BlockPositionIterator iter = vehicle.getSession().getCollisionManager().collidableBlocksIterator(box); - int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); - for (iter.reset(); iter.hasNext(); iter.next()) { - int blockId = blocks[iter.getIteration()]; + int blockId = ctx.getBlockId(iter); // Also check for fluids BlockCollision blockCollision = BlockUtils.getCollision(blockId); @@ -449,29 +440,31 @@ protected Vector3f getFluidGravity(float gravity, boolean falling) { return motion; } - protected @Nullable Vector3f getBlockMovementMultiplier() { + protected @Nullable Vector3f getBlockMovementMultiplier(VehicleContext ctx) { BoundingBox box = boundingBox.clone(); box.expand(-1.0E-7); Vector3i min = box.getMin().toInt(); Vector3i max = box.getMax().toInt(); - BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); - int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); - - // Iterate backwards - for (int i = blocks.length - 1; i >= 0; i--) { - Vector3f multiplier = MOVEMENT_MULTIPLIERS.get(BlockState.of(blocks[i]).block()); + // Iterate xyz backwards + for (int x = max.getX(); x >= min.getX(); x--) { + for (int y = max.getY(); y >= min.getY(); y--) { + for (int z = max.getZ(); z >= min.getZ(); z--) { + Block block = ctx.getBlock(x, y, z).block(); + Vector3f multiplier = MOVEMENT_MULTIPLIERS.get(block); - if (multiplier != null) { - return multiplier; + if (multiplier != null) { + return multiplier; + } + } } } return null; } - protected void applyBlockCollisionEffects() { + protected void applyBlockCollisionEffects(VehicleContext ctx) { BoundingBox box = boundingBox.clone(); box.expand(-1.0E-7); @@ -479,10 +472,8 @@ protected void applyBlockCollisionEffects() { Vector3i max = box.getMax().toInt(); BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); - int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); - for (iter.reset(); iter.hasNext(); iter.next()) { - BlockState blockState = BlockState.of(blocks[iter.getIteration()]); + BlockState blockState = ctx.getBlock(iter); if (blockState.is(Blocks.HONEY_BLOCK)) { onHoneyBlockCollision(); @@ -515,7 +506,7 @@ protected void onBubbleColumnCollision(boolean drag) { /** * @return True if there was a horizontal collision */ - protected boolean travel(float speed) { + protected boolean travel(VehicleContext ctx, float speed) { Vector3f motion = vehicle.getMotion(); // Java only does this client side @@ -529,9 +520,9 @@ protected boolean travel(float speed) { // TODO: isImmobile? set input to 0 and jump to false - motion = motion.add(getInputVelocity(speed)); + motion = motion.add(getInputVelocity(ctx, speed)); - Vector3f movementMultiplier = getBlockMovementMultiplier(); + Vector3f movementMultiplier = getBlockMovementMultiplier(ctx); if (movementMultiplier != null) { motion = motion.mul(movementMultiplier); } @@ -541,7 +532,7 @@ protected boolean travel(float speed) { ); boundingBox.translate(correctedMovement); - Vector3d newPos = boundingBox.getBottomCenter(); + ctx.loadSurroundingBlocks(); // Non-zero values indicate a collision on that axis Vector3d moveDiff = motion.toDouble().sub(correctedMovement); @@ -551,8 +542,8 @@ protected boolean travel(float speed) { boolean bounced = false; if (onGround) { - Vector3i landingPos = newPos.sub(0, 0.2f, 0).toInt(); - Block landingBlock = getBlock(landingPos); + Vector3i landingPos = ctx.centerPos().sub(0, 0.2f, 0).toInt(); + Block landingBlock = ctx.getBlock(landingPos).block(); if (landingBlock == Blocks.SLIME_BLOCK) { motion = Vector3f.from(motion.getX(), -motion.getY(), motion.getZ()); @@ -582,32 +573,30 @@ protected boolean travel(float speed) { } // Send the new position to the bedrock client and java server - moveVehicle(newPos, onGround); + moveVehicle(ctx.centerPos(), onGround); vehicle.setMotion(motion); - applyBlockCollisionEffects(); + applyBlockCollisionEffects(ctx); - float velocityMultiplier = getVelocityMultiplier(); + float velocityMultiplier = getVelocityMultiplier(ctx); vehicle.setMotion(vehicle.getMotion().mul(velocityMultiplier, 1.0f, velocityMultiplier)); return horizontalCollision; } - protected boolean isClimbing() { + protected boolean isClimbing(VehicleContext ctx) { if (!vehicle.canClimb()) { return false; } - Vector3i blockPos = boundingBox.getBottomCenter().toInt(); - BlockState blockState = getBlockState(blockPos); - + BlockState blockState = ctx.centerBlock(); if (vehicle.getSession().getTagCache().is(BlockTag.CLIMBABLE, blockState.block())) { return true; } // Check if the vehicle is in an open trapdoor with a ladder of the same direction under it if (blockState.block() instanceof TrapDoorBlock && blockState.getValue(Properties.OPEN)) { - BlockState ladderState = getBlockState(blockPos.down()); + BlockState ladderState = ctx.getBlock(ctx.centerPos().toInt().down()); return ladderState.is(Blocks.LADDER) && ladderState.getValue(Properties.HORIZONTAL_FACING) == blockState.getValue(Properties.HORIZONTAL_FACING); } @@ -625,7 +614,7 @@ protected Vector2f normalizeInput(Vector2f input) { return input; } - protected Vector3f getInputVelocity(float speed) { + protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); input = input.mul(0.98f); input = vehicle.getAdjustedInput(input); @@ -706,12 +695,10 @@ protected float getGravity() { return 0.08f; } - protected @Nullable Vector3i getSupportingBlockPos() { + protected @Nullable Vector3i getSupportingBlockPos(VehicleContext ctx) { Vector3i result = null; if (vehicle.isOnGround()) { - Vector3d bottomCenter = boundingBox.getBottomCenter(); - BoundingBox box = boundingBox.clone(); box.extend(0, -1.0E-6, 0); // Extend slightly down @@ -720,12 +707,11 @@ protected float getGravity() { // Use minY as maxY BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), min.getY(), max.getZ()); - int[] blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), iter); double minDistance = Double.MAX_VALUE; for (iter.reset(); iter.hasNext(); iter.next()) { Vector3i blockPos = Vector3i.from(iter.getX(), iter.getY(), iter.getZ()); - int blockId = blocks[iter.getIteration()]; + int blockId = ctx.getBlockId(iter); BlockCollision blockCollision; if (vehicle.canWalkOnLava()) { @@ -735,7 +721,7 @@ protected float getGravity() { } if (blockCollision != null && blockCollision.checkIntersection(blockPos, box)) { - double distance = bottomCenter.distanceSquared(blockPos.toDouble().add(0.5f, 0.5f, 0.5f)); + double distance = ctx.centerPos().distanceSquared(blockPos.toDouble().add(0.5f, 0.5f, 0.5f)); if (distance <= minDistance) { minDistance = distance; result = blockPos; @@ -747,17 +733,17 @@ protected float getGravity() { return result; } - protected Vector3i getVelocityAffectingPos() { - Vector3i blockPos = getSupportingBlockPos(); + protected Vector3i getVelocityAffectingPos(VehicleContext ctx) { + Vector3i blockPos = getSupportingBlockPos(ctx); if (blockPos != null) { - return Vector3i.from(blockPos.getX(), Math.floor(boundingBox.getBottomCenter().getY() - 0.500001f), blockPos.getZ()); + return Vector3i.from(blockPos.getX(), Math.floor(ctx.centerPos().getY() - 0.500001f), blockPos.getZ()); } else { - return vehicle.getPosition().sub(0, 0.500001f, 0).toInt(); + return ctx.centerPos().sub(0, 0.500001f, 0).toInt(); } } - protected float getVelocityMultiplier() { - Block block = getBlock(boundingBox.getBottomCenter().toInt()); + protected float getVelocityMultiplier(VehicleContext ctx) { + Block block = ctx.centerBlock().block(); if (block == Blocks.WATER || block == Blocks.BUBBLE_COLUMN) { return 1.0f; } @@ -766,7 +752,7 @@ protected float getVelocityMultiplier() { return 0.4f; } - block = getBlock(getVelocityAffectingPos()); + block = ctx.velocityAffectingBlock().block(); if (block == Blocks.SOUL_SAND || block == Blocks.HONEY_BLOCK) { return 0.4f; } @@ -774,17 +760,85 @@ protected float getVelocityMultiplier() { return 1.0f; } - protected float getJumpVelocityMultiplier() { - Block block = getBlock(boundingBox.getBottomCenter().toInt()); + protected float getJumpVelocityMultiplier(VehicleContext ctx) { + Block block = ctx.centerBlock().block(); if (block == Blocks.HONEY_BLOCK) { return 0.5f; } - block = getBlock(getVelocityAffectingPos()); + block = ctx.velocityAffectingBlock().block(); if (block == Blocks.HONEY_BLOCK) { return 0.5f; } return 1.0f; } + + protected class VehicleContext { + private Vector3d centerPos; + private BlockState centerBlock; + private BlockState velocityAffectingBlock; + private BlockPositionIterator blockIter; + private int[] blocks; + + protected void loadSurroundingBlocks() { + BoundingBox box = boundingBox.clone(); + box.expand(2); + + Vector3i min = box.getMin().toInt(); + Vector3i max = box.getMax().toInt(); + this.blockIter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); + this.blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), this.blockIter); + + this.centerPos = boundingBox.getBottomCenter(); + this.centerBlock = getBlock(this.centerPos.toInt()); + this.velocityAffectingBlock = null; + } + + protected Vector3d centerPos() { + return this.centerPos; + } + + protected BlockState centerBlock() { + return this.centerBlock; + } + + protected BlockState velocityAffectingBlock() { + if (this.velocityAffectingBlock == null) { + this.velocityAffectingBlock = getBlock(getVelocityAffectingPos(this)); + } + + return this.velocityAffectingBlock; + } + + protected int getBlockId(int x, int y, int z) { + int index = this.blockIter.getIndex(x, y, z); + if (index == -1) { + vehicle.getSession().getGeyser().getLogger().debug("[client-vehicle] Block cache miss"); + return vehicle.getSession().getGeyser().getWorldManager().getBlockAt(vehicle.getSession(), x, y, z); + } + + return blocks[index]; + } + + protected int getBlockId(Vector3i pos) { + return getBlockId(pos.getX(), pos.getY(), pos.getZ()); + } + + protected int getBlockId(BlockPositionIterator iter) { + return getBlockId(iter.getX(), iter.getY(), iter.getZ()); + } + + protected BlockState getBlock(int x, int y, int z) { + return BlockState.of(getBlockId(x, y, z)); + } + + protected BlockState getBlock(Vector3i pos) { + return BlockState.of(getBlockId(pos.getX(), pos.getY(), pos.getZ())); + } + + protected BlockState getBlock(BlockPositionIterator iter) { + return BlockState.of(getBlockId(iter.getX(), iter.getY(), iter.getZ())); + } + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a3a2d20c2d..b0378d23581 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] base-api = "1.0.0-SNAPSHOT" cumulus = "1.1.2" -erosion = "1.1-20240515.191456-1" +erosion = "1.1-20240521.000109-3" events = "1.1-SNAPSHOT" jackson = "2.17.0" fastutil = "8.5.2" From 376b7ea988cd77585b110658131508195f323c32 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Tue, 21 May 2024 21:51:26 -0400 Subject: [PATCH 15/26] Fix bug causing BoundingBox position to change in CollisionManager --- .../level/physics/CollisionManager.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index 79c8d4d092a..cc36f45c036 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -290,20 +290,21 @@ public Vector3d correctMovement(Vector3d movement, BoundingBox boundingBox, bool stretchedBoundingBox.extend(horizontalMovement); double maxStepUp = correctMovementForCollisions(Vector3d.from(0, stepUp, 0), stretchedBoundingBox, checkWorld, walkOnLava).getY(); if (maxStepUp < stepUp) { // The player collided with a block above them - boundingBox.translate(0, maxStepUp, 0); - Vector3d adjustedStepUpMovement = correctMovementForCollisions(horizontalMovement, boundingBox, checkWorld, walkOnLava); - boundingBox.translate(0, -maxStepUp, 0); + BoundingBox stepUpBoundingBox = boundingBox.clone(); + stepUpBoundingBox.translate(0, maxStepUp, 0); + Vector3d adjustedStepUpMovement = correctMovementForCollisions(horizontalMovement, stepUpBoundingBox, checkWorld, walkOnLava); if (squaredHorizontalLength(adjustedStepUpMovement) > squaredHorizontalLength(stepUpMovement)) { stepUpMovement = adjustedStepUpMovement.up(maxStepUp); } } if (squaredHorizontalLength(stepUpMovement) > squaredHorizontalLength(adjustedMovement)) { - boundingBox.translate(stepUpMovement.getX(), stepUpMovement.getY(), stepUpMovement.getZ()); + BoundingBox stepUpBoundingBox = boundingBox.clone(); + stepUpBoundingBox.translate(stepUpMovement.getX(), stepUpMovement.getY(), stepUpMovement.getZ()); + // Apply the player's remaining vertical movement - double verticalMovement = correctMovementForCollisions(Vector3d.from(0, movement.getY() - stepUpMovement.getY(), 0), boundingBox, checkWorld, walkOnLava).getY(); - boundingBox.translate(-stepUpMovement.getX(), -stepUpMovement.getY(), -stepUpMovement.getZ()); + double verticalMovement = correctMovementForCollisions(Vector3d.from(0, movement.getY() - stepUpMovement.getY(), 0), stepUpBoundingBox, checkWorld, walkOnLava).getY(); stepUpMovement = stepUpMovement.up(verticalMovement); adjustedMovement = stepUpMovement; @@ -321,6 +322,11 @@ private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox bou double movementY = movement.getY(); double movementZ = movement.getZ(); + // Position might change slightly due to floating point error + double originalX = boundingBox.getMiddleX(); + double originalY = boundingBox.getMiddleY(); + double originalZ = boundingBox.getMiddleZ(); + BoundingBox movementBoundingBox = boundingBox.clone(); movementBoundingBox.extend(movement); BlockPositionIterator iter = collidableBlocksIterator(movementBoundingBox); @@ -342,7 +348,10 @@ private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox bou boundingBox.translate(0, 0, movementZ); } - boundingBox.translate(-movementX, -movementY, -movementZ); + boundingBox.setMiddleX(originalX); + boundingBox.setMiddleY(originalY); + boundingBox.setMiddleZ(originalZ); + return Vector3d.from(movementX, movementY, movementZ); } From 818bef0bdfeddbc7381ec2f4ded054bb1b329487 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Thu, 23 May 2024 05:25:29 -0400 Subject: [PATCH 16/26] Clamp user input --- .../entity/type/player/SessionPlayerEntity.java | 15 ++++++++++++--- .../entity/vehicle/CamelVehicleComponent.java | 1 + .../geyser/entity/vehicle/VehicleComponent.java | 5 +++++ .../java/entity/JavaSetPassengersTranslator.java | 4 ++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 0b031112691..4f9316f3b97 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -28,7 +28,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import org.checkerframework.checker.nullness.qual.Nullable; -import lombok.Setter; import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.AttributeData; @@ -39,6 +38,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.DimensionUtils; +import org.geysermc.geyser.util.MathUtils; 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.GlobalPos; @@ -70,13 +70,11 @@ public class SessionPlayerEntity extends PlayerEntity { * Used when emulating client-side vehicles */ @Getter - @Setter private Vector2f vehicleInput = Vector2f.ZERO; /** * Used when emulating client-side vehicles */ @Getter - @Setter private int vehicleJumpStrength; public SessionPlayerEntity(GeyserSession session) { @@ -280,4 +278,15 @@ public void resetAttributes() { public void resetAir() { this.setAirSupply(getMaxAir()); } + + public void setVehicleInput(Vector2f vehicleInput) { + this.vehicleInput = Vector2f.from( + MathUtils.clamp(vehicleInput.getX(), -1.0f, 1.0f), + MathUtils.clamp(vehicleInput.getY(), -1.0f, 1.0f) + ); + } + + public void setVehicleJumpStrength(int vehicleJumpStrength) { + this.vehicleJumpStrength = MathUtils.constrain(vehicleJumpStrength, 0, 100); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index de9b83a380b..13e9de34fed 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -75,6 +75,7 @@ public boolean tickVehicle() { public void onDismount() { vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); vehicle.updateBedrockMetadata(); + super.onDismount(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 60da6e3ca1f..375bbf7293d 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -143,6 +143,11 @@ public void setStepHeight(float stepHeight) { this.stepHeight = MathUtils.clamp(stepHeight, 1.0f, 10.0f); } + public void onMount() { + vehicle.getSession().getPlayerEntity().setVehicleInput(Vector2f.ZERO); + vehicle.getSession().getPlayerEntity().setVehicleJumpStrength(0); + } + public void onDismount() { // } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java index c35c2b7567a..865ca046413 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java @@ -56,6 +56,10 @@ public void translate(GeyserSession session, ClientboundSetPassengersPacket pack session.getPlayerEntity().setVehicle(entity); // We need to confirm teleports before entering a vehicle, or else we will likely exit right out session.confirmTeleport(passenger.getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0).toDouble()); + + if (entity instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().onMount(); + } } if (passenger == null) { // Can occur if the passenger is outside the client's tracking range From b33a0231e29ed5fde2d2d5a0a4861b777b46255b Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Thu, 23 May 2024 19:53:12 -0400 Subject: [PATCH 17/26] Support weaving status effect --- .../entity/vehicle/CamelVehicleComponent.java | 8 ++-- .../entity/vehicle/VehicleComponent.java | 43 +++++++++++-------- .../geyser/level/block/BlockStateValues.java | 2 +- .../level/physics/CollisionManager.java | 8 +--- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index 13e9de34fed..55dc7b0be01 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -44,7 +44,7 @@ public class CamelVehicleComponent extends VehicleComponent { private long lastPoseTick; private int dashTick; - private int jumpBoost; + private int effectJumpBoost; public CamelVehicleComponent(CamelEntity vehicle) { super(vehicle, 1.5f); @@ -108,7 +108,7 @@ protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { return inputVelocity.add(Vector3f.createDirectionDeg(0, -player.getYaw()) .mul(22.2222f * jumpStrength * this.moveSpeed * getVelocityMultiplier(ctx)) - .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier(ctx) + (this.jumpBoost * 0.1f)))); + .up(1.4285f * jumpStrength * (this.horseJumpStrength * getJumpVelocityMultiplier(ctx) + (this.effectJumpBoost * 0.1f)))); } return inputVelocity; @@ -134,7 +134,7 @@ private boolean isStationary() { @Override public void setEffect(Effect effect, int effectAmplifier) { if (effect == Effect.JUMP_BOOST) { - jumpBoost = effectAmplifier + 1; + effectJumpBoost = effectAmplifier + 1; } else { super.setEffect(effect, effectAmplifier); } @@ -143,7 +143,7 @@ public void setEffect(Effect effect, int effectAmplifier) { @Override public void removeEffect(Effect effect) { if (effect == Effect.JUMP_BOOST) { - jumpBoost = 0; + effectJumpBoost = 0; } else { super.removeEffect(effect); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 375bbf7293d..5cc868535a1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -57,8 +57,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket; -import java.util.Map; - public class VehicleComponent { private static final ObjectDoublePair EMPTY_FLUID_PAIR = ObjectDoublePair.of(Fluid.EMPTY, 0.0); private static final float MAX_LOGICAL_FLUID_HEIGHT = 8.0f / BlockStateValues.NUM_FLUID_LEVELS; @@ -66,19 +64,14 @@ public class VehicleComponent { private static final float MIN_VELOCITY = 0.003f; private static final float CLIMB_SPEED = 0.15f; - private static final Map MOVEMENT_MULTIPLIERS = Map.of( - Blocks.COBWEB, Vector3f.from(0.25f, 0.05f, 0.25f), - Blocks.POWDER_SNOW, Vector3f.from(0.9f, 1.5f, 0.9f), - Blocks.SWEET_BERRY_BUSH, Vector3f.from(0.8f, 0.75f, 0.8f) - ); - protected final T vehicle; protected final BoundingBox boundingBox; protected float stepHeight; protected float moveSpeed; - protected int levitation; - protected boolean slowFalling; + protected int effectLevitation; + protected boolean effectSlowFalling; + protected boolean effectWeaving; public VehicleComponent(T vehicle, float stepHeight) { this.vehicle = vehicle; @@ -119,15 +112,17 @@ public void moveRelative(double x, double y, double z) { public void setEffect(Effect effect, int effectAmplifier) { switch (effect) { - case LEVITATION -> levitation = effectAmplifier + 1; - case SLOW_FALLING -> slowFalling = true; + case LEVITATION -> effectLevitation = effectAmplifier + 1; + case SLOW_FALLING -> effectSlowFalling = true; + case WEAVING -> effectWeaving = true; } } public void removeEffect(Effect effect) { switch (effect) { - case LEVITATION -> levitation = 0; - case SLOW_FALLING -> slowFalling = false; + case LEVITATION -> effectLevitation = 0; + case SLOW_FALLING -> effectSlowFalling = false; + case WEAVING -> effectWeaving = false; } } @@ -391,8 +386,8 @@ protected void landMovement(VehicleContext ctx) { // NOT IMPLEMENTED: climbing in powdered snow } - if (levitation > 0) { - vehicle.setMotion(vehicle.getMotion().up((0.05f * levitation - vehicle.getMotion().getY()) * 0.2f)); + if (effectLevitation > 0) { + vehicle.setMotion(vehicle.getMotion().up((0.05f * effectLevitation - vehicle.getMotion().getY()) * 0.2f)); } else { vehicle.setMotion(vehicle.getMotion().down(gravity)); // NOT IMPLEMENTED: slow fall when in unloaded chunk @@ -457,7 +452,19 @@ protected Vector3f getFluidGravity(float gravity, boolean falling) { for (int y = max.getY(); y >= min.getY(); y--) { for (int z = max.getZ(); z >= min.getZ(); z--) { Block block = ctx.getBlock(x, y, z).block(); - Vector3f multiplier = MOVEMENT_MULTIPLIERS.get(block); + Vector3f multiplier = null; + + if (block == Blocks.COBWEB) { + if (effectWeaving) { + multiplier = Vector3f.from(0.5, 0.25, 0.5); + } else { + multiplier = Vector3f.from(0.25, 0.05f, 0.25); + } + } else if (block == Blocks.POWDER_SNOW) { + multiplier = Vector3f.from(0.9f, 1.5, 0.9f); + } else if (block == Blocks.SWEET_BERRY_BUSH) { + multiplier = Vector3f.from(0.8f, 0.75, 0.8f); + } if (multiplier != null) { return multiplier; @@ -693,7 +700,7 @@ protected float getGravity() { return 0; } - if (vehicle.getMotion().getY() <= 0 && slowFalling) { + if (vehicle.getMotion().getY() <= 0 && effectSlowFalling) { return 0.01f; } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index de327989b30..36e437026a1 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -100,7 +100,7 @@ public static boolean canPistonMoveBlock(BlockState state, boolean isPushing) { } /** - * Get the type of fluid from the block state. + * Get the type of fluid from the block state, including waterlogged blocks. * * @param state BlockState of the block * @return The type of fluid diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index 07c43a71cb2..a4fdb2e7c31 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -366,13 +366,7 @@ private double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double if (checkWorld) { int blockId = session.getGeyser().getWorldManager().getBlockAt(session, x, y, z); - BlockCollision blockCollision; - if (walkOnLava) { - blockCollision = getCollisionLavaWalking(blockId, y, boundingBox); - } else { - blockCollision = BlockUtils.getCollision(blockId); - } - + BlockCollision blockCollision = walkOnLava ? getCollisionLavaWalking(blockId, y, boundingBox) : BlockUtils.getCollision(blockId); if (blockCollision != null && !(blockCollision instanceof ScaffoldingCollision)) { offset = blockCollision.computeCollisionOffset(x, y, z, boundingBox, axis, offset); } From 1f84b6267f1cf8b1f796fa06f546e262cdf05570 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sat, 25 May 2024 03:59:20 -0400 Subject: [PATCH 18/26] Support gravity attribute --- .../geyser/entity/type/LivingEntity.java | 5 +++ .../entity/vehicle/VehicleComponent.java | 31 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 71ce0c5aec4..064a6b4e58c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -413,6 +413,11 @@ protected void updateAttribute(Attribute javaAttribute, List newA 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_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 5cc868535a1..b49301faa9f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -54,6 +54,7 @@ import org.geysermc.geyser.util.BlockUtils; import org.geysermc.geyser.util.MathUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket; @@ -69,6 +70,7 @@ public class VehicleComponent { protected float stepHeight; protected float moveSpeed; + protected double gravity; protected int effectLevitation; protected boolean effectSlowFalling; protected boolean effectWeaving; @@ -76,7 +78,8 @@ public class VehicleComponent { public VehicleComponent(T vehicle, float stepHeight) { this.vehicle = vehicle; this.stepHeight = stepHeight; - this.moveSpeed = GeyserAttributeType.MOVEMENT_SPEED.getDefaultValue(); + this.moveSpeed = (float) AttributeType.Builtin.GENERIC_MOVEMENT_SPEED.getDef(); + this.gravity = AttributeType.Builtin.GENERIC_GRAVITY.getDef(); double width = Double.parseDouble(Float.toString(vehicle.getBoundingBoxWidth())); double height = Double.parseDouble(Float.toString(vehicle.getBoundingBoxHeight())); @@ -138,6 +141,10 @@ public void setStepHeight(float stepHeight) { this.stepHeight = MathUtils.clamp(stepHeight, 1.0f, 10.0f); } + public void setGravity(double gravity) { + this.gravity = MathUtils.constrain(gravity, -1.0, 1.0); + } + public void onMount() { vehicle.getSession().getPlayerEntity().setVehicleInput(Vector2f.ZERO); vehicle.getSession().getPlayerEntity().setVehicleJumpStrength(0); @@ -331,7 +338,7 @@ protected boolean isFlowBlocked(Fluid fluid, int adjacentBlockId) { } protected void waterMovement(VehicleContext ctx) { - float gravity = getGravity(); + double gravity = getGravity(); float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier double originalY = ctx.centerPos().getY(); boolean falling = vehicle.getMotion().getY() <= 0; @@ -353,7 +360,7 @@ protected void waterMovement(VehicleContext ctx) { } protected void lavaMovement(VehicleContext ctx, double lavaHeight) { - float gravity = getGravity(); + double gravity = getGravity(); double originalY = ctx.centerPos().getY(); boolean falling = vehicle.getMotion().getY() <= 0; @@ -366,7 +373,7 @@ protected void lavaMovement(VehicleContext ctx, double lavaHeight) { vehicle.setMotion(vehicle.getMotion().mul(0.5f)); } - vehicle.setMotion(vehicle.getMotion().down(gravity / 4.0f)); + vehicle.setMotion(vehicle.getMotion().down((float) (gravity / 4.0))); if (horizontalCollision && shouldApplyFluidJumpBoost(ctx, originalY)) { vehicle.setMotion(Vector3f.from(vehicle.getMotion().getX(), 0.3f, vehicle.getMotion().getZ())); @@ -374,7 +381,7 @@ protected void lavaMovement(VehicleContext ctx, double lavaHeight) { } protected void landMovement(VehicleContext ctx) { - float gravity = getGravity(); + double gravity = getGravity(); float slipperiness = BlockStateValues.getSlipperiness(ctx.velocityAffectingBlock()); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); @@ -389,7 +396,7 @@ protected void landMovement(VehicleContext ctx) { if (effectLevitation > 0) { vehicle.setMotion(vehicle.getMotion().up((0.05f * effectLevitation - vehicle.getMotion().getY()) * 0.2f)); } else { - vehicle.setMotion(vehicle.getMotion().down(gravity)); + vehicle.setMotion(vehicle.getMotion().down((float) gravity)); // NOT IMPLEMENTED: slow fall when in unloaded chunk } @@ -428,10 +435,10 @@ protected Vector3f getClimbingSpeed(boolean horizontalCollision) { ); } - protected Vector3f getFluidGravity(float gravity, boolean falling) { + protected Vector3f getFluidGravity(double gravity, boolean falling) { Vector3f motion = vehicle.getMotion(); - if (vehicle.getFlag(EntityFlag.HAS_GRAVITY) && !vehicle.getFlag(EntityFlag.SPRINTING)) { - float newY = motion.getY() - gravity / 16; + if (gravity != 0 && !vehicle.getFlag(EntityFlag.SPRINTING)) { + float newY = (float) (motion.getY() - gravity / 16); if (falling && Math.abs(motion.getY() - 0.005f) >= MIN_VELOCITY && Math.abs(newY) < MIN_VELOCITY) { newY = -MIN_VELOCITY; } @@ -695,16 +702,16 @@ protected void moveVehicle(Vector3d javaPos, boolean isOnGround) { vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis()); } - protected float getGravity() { + protected double getGravity() { if (!vehicle.getFlag(EntityFlag.HAS_GRAVITY)) { return 0; } if (vehicle.getMotion().getY() <= 0 && effectSlowFalling) { - return 0.01f; + return Math.min(0.01, this.gravity); } - return 0.08f; + return this.gravity; } protected @Nullable Vector3i getSupportingBlockPos(VehicleContext ctx) { From 1a6c70feeddf3b833879d186633d0aedd7ea149f Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 14 Jun 2024 21:19:37 -0400 Subject: [PATCH 19/26] Piston support --- .../entity/vehicle/VehicleComponent.java | 94 ++++++++++++------- .../level/physics/CollisionManager.java | 3 +- .../geyser/session/cache/PistonCache.java | 26 ++++- .../level/block/entity/PistonBlockEntity.java | 73 +++++++++----- 4 files changed, 139 insertions(+), 57 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index b49301faa9f..d3839e47f53 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -35,7 +35,6 @@ 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.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.Blocks; @@ -81,8 +80,8 @@ public VehicleComponent(T vehicle, float stepHeight) { this.moveSpeed = (float) AttributeType.Builtin.GENERIC_MOVEMENT_SPEED.getDef(); this.gravity = AttributeType.Builtin.GENERIC_GRAVITY.getDef(); - double width = Double.parseDouble(Float.toString(vehicle.getBoundingBoxWidth())); - double height = Double.parseDouble(Float.toString(vehicle.getBoundingBoxHeight())); + double width = vehicle.getBoundingBoxWidth(); + double height = vehicle.getBoundingBoxHeight(); this.boundingBox = new BoundingBox( vehicle.getPosition().getX(), vehicle.getPosition().getY() + height / 2, @@ -92,15 +91,13 @@ public VehicleComponent(T vehicle, float stepHeight) { } public void setWidth(float width) { - double doubleWidth = Double.parseDouble(Float.toString(width)); - boundingBox.setSizeX(doubleWidth); - boundingBox.setSizeZ(doubleWidth); + boundingBox.setSizeX(width); + boundingBox.setSizeZ(width); } public void setHeight(float height) { - double doubleHeight = Double.parseDouble(Float.toString(height)); - boundingBox.translate(0, (doubleHeight - boundingBox.getSizeY()) / 2, 0); - boundingBox.setSizeY(doubleHeight); + boundingBox.translate(0, (height - boundingBox.getSizeY()) / 2, 0); + boundingBox.setSizeY(height); } public void moveAbsolute(double x, double y, double z) { @@ -113,6 +110,14 @@ public void moveRelative(double x, double y, double z) { boundingBox.translate(x, y, z); } + public void moveRelative(Vector3d vec) { + boundingBox.translate(vec); + } + + public BoundingBox getBoundingBox() { + return this.boundingBox; + } + public void setEffect(Effect effect, int effectAmplifier) { switch (effect) { case LEVITATION -> effectLevitation = effectAmplifier + 1; @@ -129,6 +134,12 @@ public void removeEffect(Effect effect) { } } + public Vector3d correctMovement(Vector3d movement) { + return vehicle.getSession().getCollisionManager().correctMovement( + movement, boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() + ); + } + public void setMoveSpeed(float moveSpeed) { this.moveSpeed = moveSpeed; } @@ -382,7 +393,7 @@ protected void lavaMovement(VehicleContext ctx, double lavaHeight) { protected void landMovement(VehicleContext ctx) { double gravity = getGravity(); - float slipperiness = BlockStateValues.getSlipperiness(ctx.velocityAffectingBlock()); + float slipperiness = BlockStateValues.getSlipperiness(getVelocityBlock(ctx)); float drag = vehicle.isOnGround() ? 0.91f * slipperiness : 0.91f; float speed = vehicle.getVehicleSpeed() * (vehicle.isOnGround() ? BASE_SLIPPERINESS_CUBED / (slipperiness * slipperiness * slipperiness) : 0.1f); @@ -556,13 +567,12 @@ protected boolean travel(VehicleContext ctx, float speed) { // Non-zero values indicate a collision on that axis Vector3d moveDiff = motion.toDouble().sub(correctedMovement); - boolean onGround = moveDiff.getY() != 0 && motion.getY() < 0; + vehicle.setOnGround(moveDiff.getY() != 0 && motion.getY() < 0); boolean horizontalCollision = moveDiff.getX() != 0 || moveDiff.getZ() != 0; boolean bounced = false; - if (onGround) { - Vector3i landingPos = ctx.centerPos().sub(0, 0.2f, 0).toInt(); - Block landingBlock = ctx.getBlock(landingPos).block(); + if (vehicle.isOnGround()) { + Block landingBlock = getLandingBlock(ctx).block(); if (landingBlock == Blocks.SLIME_BLOCK) { motion = Vector3f.from(motion.getX(), -motion.getY(), motion.getZ()); @@ -592,7 +602,7 @@ protected boolean travel(VehicleContext ctx, float speed) { } // Send the new position to the bedrock client and java server - moveVehicle(ctx.centerPos(), onGround); + moveVehicle(ctx.centerPos()); vehicle.setMotion(motion); applyBlockCollisionEffects(ctx); @@ -651,17 +661,16 @@ protected Vector2f getVehicleRotation() { return Vector2f.from(player.getYaw(), player.getPitch() * 0.5f); } - protected void moveVehicle(Vector3d javaPos, boolean isOnGround) { + protected void moveVehicle(Vector3d javaPos) { Vector3f bedrockPos = javaPos.toFloat(); Vector2f rotation = getVehicleRotation(); MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket(); moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId()); - if (isOnGround) { + if (vehicle.isOnGround()) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND); } - vehicle.setOnGround(isOnGround); if (vehicle.getPosition().getX() != bedrockPos.getX()) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X); @@ -714,7 +723,16 @@ protected double getGravity() { return this.gravity; } - protected @Nullable Vector3i getSupportingBlockPos(VehicleContext ctx) { + /** + * Finds the position of the main block supporting the vehicle. + * Used when determining slipperiness, speed, etc. + *

+ * Should use {@link VehicleContext#supportingBlockPos()}, instead of calling this directly. + * + * @param ctx context + * @return position of the main block supporting this entity + */ + private @Nullable Vector3i getSupportingBlockPos(VehicleContext ctx) { Vector3i result = null; if (vehicle.isOnGround()) { @@ -752,13 +770,25 @@ protected double getGravity() { return result; } - protected Vector3i getVelocityAffectingPos(VehicleContext ctx) { - Vector3i blockPos = getSupportingBlockPos(ctx); - if (blockPos != null) { - return Vector3i.from(blockPos.getX(), Math.floor(ctx.centerPos().getY() - 0.500001f), blockPos.getZ()); + protected BlockState getBlockUnder(VehicleContext ctx, float dist) { + Vector3i supportingBlockPos = ctx.supportingBlockPos(); + + Vector3i blockPos; + if (supportingBlockPos != null) { + blockPos = Vector3i.from(supportingBlockPos.getX(), Math.floor(ctx.centerPos().getY() - dist), supportingBlockPos.getZ()); } else { - return ctx.centerPos().sub(0, 0.500001f, 0).toInt(); + blockPos = ctx.centerPos().sub(0, dist, 0).toInt(); } + + return ctx.getBlock(blockPos); + } + + protected BlockState getLandingBlock(VehicleContext ctx) { + return getBlockUnder(ctx, 0.2f); + } + + protected BlockState getVelocityBlock(VehicleContext ctx) { + return getBlockUnder(ctx, 0.500001f); } protected float getVelocityMultiplier(VehicleContext ctx) { @@ -771,7 +801,7 @@ protected float getVelocityMultiplier(VehicleContext ctx) { return 0.4f; } - block = ctx.velocityAffectingBlock().block(); + block = getVelocityBlock(ctx).block(); if (block == Blocks.SOUL_SAND || block == Blocks.HONEY_BLOCK) { return 0.4f; } @@ -785,7 +815,7 @@ protected float getJumpVelocityMultiplier(VehicleContext ctx) { return 0.5f; } - block = ctx.velocityAffectingBlock().block(); + block = getVelocityBlock(ctx).block(); if (block == Blocks.HONEY_BLOCK) { return 0.5f; } @@ -796,7 +826,7 @@ protected float getJumpVelocityMultiplier(VehicleContext ctx) { protected class VehicleContext { private Vector3d centerPos; private BlockState centerBlock; - private BlockState velocityAffectingBlock; + private Vector3i supportingBlockPos; private BlockPositionIterator blockIter; private int[] blocks; @@ -811,7 +841,7 @@ protected void loadSurroundingBlocks() { this.centerPos = boundingBox.getBottomCenter(); this.centerBlock = getBlock(this.centerPos.toInt()); - this.velocityAffectingBlock = null; + this.supportingBlockPos = null; } protected Vector3d centerPos() { @@ -822,12 +852,12 @@ protected BlockState centerBlock() { return this.centerBlock; } - protected BlockState velocityAffectingBlock() { - if (this.velocityAffectingBlock == null) { - this.velocityAffectingBlock = getBlock(getVelocityAffectingPos(this)); + protected Vector3i supportingBlockPos() { + if (this.supportingBlockPos == null) { + this.supportingBlockPos = getSupportingBlockPos(this); } - return this.velocityAffectingBlock; + return this.supportingBlockPos; } protected int getBlockId(int x, int y, int z) { diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index a4fdb2e7c31..ec2ddd2698b 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -177,7 +177,8 @@ public void updatePlayerBoundingBox() { // Send corrected position to Bedrock if they differ by too much to prevent de-syncs if (onGround != newOnGround || movement.distanceSquared(adjustedMovement) > INCORRECT_MOVEMENT_THRESHOLD) { PlayerEntity playerEntity = session.getPlayerEntity(); - if (pistonCache.getPlayerMotion().equals(Vector3f.ZERO) && !pistonCache.isPlayerSlimeCollision()) { + // Client will dismount if on a vehicle + if (playerEntity.getVehicle() == null && pistonCache.getPlayerMotion().equals(Vector3f.ZERO) && !pistonCache.isPlayerSlimeCollision()) { playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), newOnGround, true); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java index d0a5bc094c7..dee4aa7cf50 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java @@ -33,7 +33,9 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.level.physics.Axis; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.session.GeyserSession; @@ -119,6 +121,12 @@ private void resetPlayerMovement() { private void sendPlayerMovement() { if (!playerDisplacement.equals(Vector3d.ZERO) && playerMotion.equals(Vector3f.ZERO)) { SessionPlayerEntity playerEntity = session.getPlayerEntity(); + + Entity vehicle = playerEntity.getVehicle(); + if (vehicle instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + return; + } + boolean isOnGround = playerDisplacement.getY() > 0 || playerEntity.isOnGround(); Vector3d position = session.getCollisionManager().getPlayerBoundingBox().getBottomCenter(); playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), isOnGround, true); @@ -128,6 +136,13 @@ private void sendPlayerMovement() { private void sendPlayerMotion() { if (!playerMotion.equals(Vector3f.ZERO)) { SessionPlayerEntity playerEntity = session.getPlayerEntity(); + + Entity vehicle = playerEntity.getVehicle(); + if (vehicle instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + vehicle.setMotion(playerMotion); + return; + } + playerEntity.setMotion(playerMotion); SetEntityMotionPacket setEntityMotionPacket = new SetEntityMotionPacket(); @@ -149,10 +164,15 @@ public void displacePlayer(Vector3d displacement) { totalDisplacement = totalDisplacement.max(-0.51d, -0.51d, -0.51d).min(0.51d, 0.51d, 0.51d); Vector3d delta = totalDisplacement.sub(playerDisplacement); - // Check if the piston is pushing a player into collision - delta = session.getCollisionManager().correctPlayerMovement(delta, true, false); - session.getCollisionManager().getPlayerBoundingBox().translate(delta.getX(), delta.getY(), delta.getZ()); + // Check if the piston is pushing a player into collision + if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + delta = clientVehicle.getVehicleComponent().correctMovement(delta); + clientVehicle.getVehicleComponent().moveRelative(delta); + } else { + delta = session.getCollisionManager().correctPlayerMovement(delta, true, false); + session.getCollisionManager().getPlayerBoundingBox().translate(delta.getX(), delta.getY(), delta.getZ()); + } playerDisplacement = totalDisplacement; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java index 350ce8c3e33..2cde9855305 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java @@ -38,6 +38,7 @@ import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.api.util.PlatformType; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.property.Properties; @@ -348,18 +349,31 @@ public void pushPlayer() { blockMovement = 1f - lastProgress; } - BoundingBox playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox(); + boolean onGround; + BoundingBox playerBoundingBox; + if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + onGround = session.getPlayerEntity().getVehicle().isOnGround(); + playerBoundingBox = clientVehicle.getVehicleComponent().getBoundingBox(); + } else { + onGround = session.getPlayerEntity().isOnGround(); + playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox(); + } + // Shrink the collision in the other axes slightly, to avoid false positives when pressed up against the side of blocks Vector3d shrink = Vector3i.ONE.sub(direction.abs()).toDouble().mul(CollisionManager.COLLISION_TOLERANCE * 2); - playerBoundingBox.setSizeX(playerBoundingBox.getSizeX() - shrink.getX()); - playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() - shrink.getY()); - playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() - shrink.getZ()); + double sizeX = playerBoundingBox.getSizeX(); + double sizeY = playerBoundingBox.getSizeY(); + double sizeZ = playerBoundingBox.getSizeZ(); + + playerBoundingBox.setSizeX(sizeX - shrink.getX()); + playerBoundingBox.setSizeY(sizeY - shrink.getY()); + playerBoundingBox.setSizeZ(sizeZ - shrink.getZ()); // Resolve collision with the piston head BlockState pistonHeadId = Blocks.PISTON_HEAD.defaultBlockState() .withValue(Properties.SHORT, false) .withValue(Properties.FACING, orientation); - pushPlayerBlock(pistonHeadId, getPistonHeadPos().toDouble(), blockMovement, playerBoundingBox); + pushPlayerBlock(pistonHeadId, getPistonHeadPos().toDouble(), blockMovement, playerBoundingBox, onGround); // Resolve collision with any attached moving blocks, but skip slime blocks // This prevents players from being launched by slime blocks covered by other blocks @@ -367,7 +381,7 @@ public void pushPlayer() { BlockState state = entry.getValue(); if (!state.is(Blocks.SLIME_BLOCK)) { Vector3d blockPos = entry.getKey().toDouble(); - pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox); + pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox, onGround); } } // Resolve collision with slime blocks @@ -375,14 +389,14 @@ public void pushPlayer() { BlockState state = entry.getValue(); if (state.is(Blocks.SLIME_BLOCK)) { Vector3d blockPos = entry.getKey().toDouble(); - pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox); + pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox, onGround); } } // Undo shrink - playerBoundingBox.setSizeX(playerBoundingBox.getSizeX() + shrink.getX()); - playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() + shrink.getY()); - playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() + shrink.getZ()); + playerBoundingBox.setSizeX(sizeX); + playerBoundingBox.setSizeY(sizeY); + playerBoundingBox.setSizeZ(sizeZ); } /** @@ -392,20 +406,22 @@ public void pushPlayer() { * @param playerBoundingBox The player's bounding box * @return True if the player attached, otherwise false */ - private boolean isPlayerAttached(Vector3d blockPos, BoundingBox playerBoundingBox) { + private boolean isPlayerAttached(Vector3d blockPos, BoundingBox playerBoundingBox, boolean onGround) { if (orientation.isVertical()) { return false; } - return session.getPlayerEntity().isOnGround() && HONEY_BOUNDING_BOX.checkIntersection(blockPos, playerBoundingBox); + return onGround && HONEY_BOUNDING_BOX.checkIntersection(blockPos, playerBoundingBox); } /** * Launches a player if the player is on the pushing side of the slime block * * @param blockPos The position of the slime block - * @param playerPos The player's position + * @param playerBoundingBox The player's bounding box */ - private void applySlimeBlockMotion(Vector3d blockPos, Vector3d playerPos) { + private void applySlimeBlockMotion(Vector3d blockPos, BoundingBox playerBoundingBox) { + Vector3d playerPos = Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()); + Direction movementDirection = orientation; // Invert direction when pulling if (action == PistonValueType.PULLING) { @@ -471,7 +487,7 @@ private double getBlockIntersection(BlockCollision blockCollision, Vector3d bloc return maxIntersection; } - private void pushPlayerBlock(BlockState state, Vector3d startingPos, double blockMovement, BoundingBox playerBoundingBox) { + private void pushPlayerBlock(BlockState state, Vector3d startingPos, double blockMovement, BoundingBox playerBoundingBox, boolean onGround) { PistonCache pistonCache = session.getPistonCache(); Vector3d movement = getMovement().toDouble(); // Check if the player collides with the movingBlock block entity @@ -481,12 +497,12 @@ private void pushPlayerBlock(BlockState state, Vector3d startingPos, double bloc if (state.is(Blocks.SLIME_BLOCK)) { pistonCache.setPlayerSlimeCollision(true); - applySlimeBlockMotion(finalBlockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ())); + applySlimeBlockMotion(finalBlockPos, playerBoundingBox); } } Vector3d blockPos = startingPos.add(movement.mul(blockMovement)); - if (state.is(Blocks.HONEY_BLOCK) && isPlayerAttached(blockPos, playerBoundingBox)) { + if (state.is(Blocks.HONEY_BLOCK) && isPlayerAttached(blockPos, playerBoundingBox, onGround)) { pistonCache.setPlayerCollided(true); pistonCache.setPlayerAttachedToHoney(true); @@ -509,7 +525,7 @@ private void pushPlayerBlock(BlockState state, Vector3d startingPos, double bloc if (state.is(Blocks.SLIME_BLOCK)) { pistonCache.setPlayerSlimeCollision(true); - applySlimeBlockMotion(blockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ())); + applySlimeBlockMotion(blockPos, playerBoundingBox); } } } @@ -585,7 +601,14 @@ private void createMovingBlocks() { movingBlockMap.put(getPistonHeadPos(), this); Vector3i movement = getMovement(); - BoundingBox playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox().clone(); + + BoundingBox playerBoundingBox; + if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + playerBoundingBox = clientVehicle.getVehicleComponent().getBoundingBox().clone(); + } else { + playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox().clone(); + } + if (orientation == Direction.UP) { // Extend the bounding box down, to catch collisions when the player is falling down playerBoundingBox.extend(0, -256, 0); @@ -629,17 +652,25 @@ private void placeFinalBlocks() { return; } placedFinalBlocks = true; + + BoundingBox playerBoundingBox; + if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + playerBoundingBox = clientVehicle.getVehicleComponent().getBoundingBox().clone(); + } else { + playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox().clone(); + } + Vector3i movement = getMovement(); attachedBlocks.forEach((blockPos, state) -> { blockPos = blockPos.add(movement); // Don't place blocks that collide with the player - if (!SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), session.getCollisionManager().getPlayerBoundingBox())) { + if (!SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), playerBoundingBox)) { ChunkUtils.updateBlock(session, state, blockPos); } }); if (action == PistonValueType.PUSHING) { Vector3i pistonHeadPos = getPistonHeadPos().add(movement); - if (!SOLID_BOUNDING_BOX.checkIntersection(pistonHeadPos.toDouble(), session.getCollisionManager().getPlayerBoundingBox())) { + if (!SOLID_BOUNDING_BOX.checkIntersection(pistonHeadPos.toDouble(), playerBoundingBox)) { ChunkUtils.updateBlock(session, Blocks.PISTON_HEAD.defaultBlockState() .withValue(Properties.SHORT, false) .withValue(Properties.FACING, orientation), pistonHeadPos); From cdbb27669c797b903b6e68ee0b3389661aa57921 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sat, 15 Jun 2024 01:11:41 -0400 Subject: [PATCH 20/26] Tick boost for Pig and Strider if any player is controlling --- .../entity/type/living/animal/PigEntity.java | 33 ++++++++++++++++--- .../type/living/animal/StriderEntity.java | 33 ++++++++++++++++--- .../vehicle/BoostableVehicleComponent.java | 9 ++--- .../entity/vehicle/CamelVehicleComponent.java | 4 +-- .../entity/vehicle/VehicleComponent.java | 6 ++-- .../inventory/item/StoredItemMappings.java | 4 +++ 6 files changed, 65 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index d6f0ff95e92..b2702751c8b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -29,8 +29,11 @@ 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; @@ -46,7 +49,7 @@ import java.util.UUID; -public class PigEntity extends AnimalEntity implements ClientVehicle { +public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle { private final BoostableVehicleComponent 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) { @@ -96,6 +99,21 @@ 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; @@ -111,11 +129,16 @@ 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 getFlag(EntityFlag.SADDLED) - && !passengers.isEmpty() - && passengers.get(0) == session.getPlayerEntity() - && session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK); + return getPlayerPassenger() == session.getPlayerEntity() && session.getPlayerInventory().isHolding(Items.CARROT_ON_A_STICK); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index bee133c53fd..ec9bfb99377 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -29,9 +29,12 @@ 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; @@ -48,7 +51,7 @@ import java.util.UUID; -public class StriderEntity extends AnimalEntity implements ClientVehicle { +public class StriderEntity extends AnimalEntity implements Tickable, ClientVehicle { private final BoostableVehicleComponent vehicleComponent = new BoostableVehicleComponent<>(this, 1.0f); private boolean isCold = false; @@ -143,6 +146,21 @@ 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; @@ -158,12 +176,17 @@ 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 getFlag(EntityFlag.SADDLED) - && !passengers.isEmpty() - && passengers.get(0) == session.getPlayerEntity() - && session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK); + return getPlayerPassenger() == session.getPlayerEntity() && session.getPlayerInventory().isHolding(Items.WARPED_FUNGUS_ON_A_STICK); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java index febe78ab448..41224012db5 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/BoostableVehicleComponent.java @@ -52,14 +52,9 @@ public boolean isBoosting() { return boostTicks <= boostLength; } - @Override - public boolean tickVehicle() { - boolean clientControlled = super.tickVehicle(); - if (clientControlled && isBoosting()) { - // TODO: the client ticks boost if any player is controlling + public void tickBoost() { + if (isBoosting()) { boostTicks++; } - - return clientControlled; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index 55dc7b0be01..07068b48ee8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -55,7 +55,7 @@ public void startDashCooldown() { } @Override - public boolean tickVehicle() { + public void tickVehicle() { if (this.dashTick != 0) { if (vehicle.getSession().getTicks() > this.dashTick) { vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); @@ -67,8 +67,6 @@ public boolean tickVehicle() { vehicle.setFlag(EntityFlag.CAN_DASH, vehicle.getFlag(EntityFlag.SADDLED) && !isStationary()); vehicle.updateBedrockMetadata(); - - return super.tickVehicle(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index d3839e47f53..0ee3172b7b5 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -165,9 +165,9 @@ public void onDismount() { // } - public boolean tickVehicle() { + public void tickVehicle() { if (!vehicle.isClientControlled()) { - return false; + return; } VehicleContext ctx = new VehicleContext(); @@ -185,8 +185,6 @@ public boolean tickVehicle() { } case EMPTY -> landMovement(ctx); } - - return true; } protected ObjectDoublePair updateFluidMovement(VehicleContext ctx) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java b/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java index 05f6ba6cca0..b698436c957 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java @@ -43,6 +43,7 @@ public class StoredItemMappings { private final ItemMapping banner; private final ItemMapping barrier; private final ItemMapping bow; + private final ItemMapping carrotOnAStick; private final ItemMapping compass; private final ItemMapping crossbow; private final ItemMapping egg; @@ -51,6 +52,7 @@ public class StoredItemMappings { private final ItemMapping powderSnowBucket; private final ItemMapping shield; private final ItemMapping upgradeTemplate; + private final ItemMapping warpedFungusOnAStick; private final ItemMapping wheat; private final ItemMapping writableBook; private final ItemMapping writtenBook; @@ -59,6 +61,7 @@ public StoredItemMappings(Map itemMappings) { this.banner = load(itemMappings, Items.WHITE_BANNER); // As of 1.17.10, all banners have the same Bedrock ID this.barrier = load(itemMappings, Items.BARRIER); this.bow = load(itemMappings, Items.BOW); + this.carrotOnAStick = load(itemMappings, Items.CARROT_ON_A_STICK); this.compass = load(itemMappings, Items.COMPASS); this.crossbow = load(itemMappings, Items.CROSSBOW); this.egg = load(itemMappings, Items.EGG); @@ -67,6 +70,7 @@ public StoredItemMappings(Map itemMappings) { this.powderSnowBucket = load(itemMappings, Items.POWDER_SNOW_BUCKET); this.shield = load(itemMappings, Items.SHIELD); this.upgradeTemplate = load(itemMappings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE); + this.warpedFungusOnAStick = load(itemMappings, Items.WARPED_FUNGUS_ON_A_STICK); this.wheat = load(itemMappings, Items.WHEAT); this.writableBook = load(itemMappings, Items.WRITABLE_BOOK); this.writtenBook = load(itemMappings, Items.WRITTEN_BOOK); From 97c42d34d5894fb830fb912e749c4054f40984ee Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 17 Jun 2024 04:02:33 -0400 Subject: [PATCH 21/26] Submodule --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 88e50df1008..420cbe173ff 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 88e50df1008916c266428ac11f76f07dc24638c5 +Subproject commit 420cbe173ffa0667d4607715f9e3d43402e1ab77 From d49a3ec5dd1e6267f8f2abfe223dbcbf22178333 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 21 Jun 2024 00:35:22 -0400 Subject: [PATCH 22/26] Address some reviews --- .../geysermc/geyser/entity/type/Entity.java | 16 ---------- .../geyser/entity/type/LivingEntity.java | 30 +++++++++++++++++++ .../level/physics/CollisionManager.java | 25 ++++++++++++++++ .../translator/collision/BlockCollision.java | 11 +++++-- .../level/block/entity/PistonBlockEntity.java | 17 ++--------- 5 files changed, 66 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 8114ee1e4fa..08e87dc0325 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -40,7 +40,6 @@ 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; @@ -224,13 +223,6 @@ 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) { - 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(); @@ -466,10 +458,6 @@ public boolean setBoundingBoxHeight(float height) { dirtyMetadata.put(EntityDataTypes.HEIGHT, boundingBoxHeight); updatePassengerOffsets(); - - if (valid && this instanceof ClientVehicle clientVehicle) { - clientVehicle.getVehicleComponent().setHeight(boundingBoxHeight); - } return true; } return false; @@ -479,10 +467,6 @@ 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); - } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 064a6b4e58c..312d73a65e3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -289,6 +289,36 @@ public InteractionResult interact(Hand hand) { 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) { + clientVehicle.getVehicleComponent().setHeight(height); + } + + return super.setBoundingBoxHeight(height); + } + + @Override + public void setBoundingBoxWidth(float width) { + if (valid && this instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().setWidth(width); + } + + super.setBoundingBoxWidth(width); + } + /** * Checks to see if a nametag interaction would go through. */ diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index ec2ddd2698b..a0fb312b487 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -38,6 +38,7 @@ import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.property.Properties; @@ -132,6 +133,21 @@ public void updatePlayerBoundingBox() { playerBoundingBox.setSizeY(playerHeight); } + /** + * Gets the bounding box to use for player movement. + *

+ * This will return either the bounding box of a {@link ClientVehicle}, or the player's own bounding box. + * + * @return the bounding box to use for movement calculations + */ + public BoundingBox getActiveBoundingBox() { + if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + return clientVehicle.getVehicleComponent().getBoundingBox(); + } + + return playerBoundingBox; + } + /** * Adjust the Bedrock position before sending to the Java server to account for inaccuracies in movement between * the two versions. Will also send corrected movement packets back to Bedrock if they collide with pistons. @@ -154,6 +170,15 @@ public void updatePlayerBoundingBox() { Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY, Double.parseDouble(Float.toString(bedrockPosition.getZ()))); + // Don't correct position if controlling a vehicle + if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { + playerBoundingBox.setMiddleX(position.getX()); + playerBoundingBox.setMiddleY(position.getY() + playerBoundingBox.getSizeY() / 2); + playerBoundingBox.setMiddleZ(position.getZ()); + + return playerBoundingBox.getBottomCenter(); + } + Vector3d startingPos = playerBoundingBox.getBottomCenter(); Vector3d movement = position.sub(startingPos); Vector3d adjustedMovement = correctPlayerMovement(movement, false, teleported); diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java index f47ac2abe14..bfe3f4417de 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java @@ -167,10 +167,17 @@ public double computeCollisionOffset(double x, double y, double z, BoundingBox b return offset; } - public boolean isBelow(double y, BoundingBox boundingBox) { + /** + * Checks if this block collision is below the given bounding box. + * + * @param blockY the y position of the block in the world + * @param boundingBox the bounding box to compare + * @return true if this block collision is below the bounding box + */ + public boolean isBelow(int blockY, BoundingBox boundingBox) { double minY = boundingBox.getMiddleY() - boundingBox.getSizeY() / 2; for (BoundingBox b : boundingBoxes) { - double offset = y + b.getMiddleY() + b.getSizeY() / 2 - minY; + double offset = blockY + b.getMiddleY() + b.getSizeY() / 2 - minY; if (offset > CollisionManager.COLLISION_TOLERANCE) { return false; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java index 2cde9855305..ba2ffc711e9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java @@ -601,14 +601,7 @@ private void createMovingBlocks() { movingBlockMap.put(getPistonHeadPos(), this); Vector3i movement = getMovement(); - - BoundingBox playerBoundingBox; - if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { - playerBoundingBox = clientVehicle.getVehicleComponent().getBoundingBox().clone(); - } else { - playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox().clone(); - } - + BoundingBox playerBoundingBox = session.getCollisionManager().getActiveBoundingBox().clone(); if (orientation == Direction.UP) { // Extend the bounding box down, to catch collisions when the player is falling down playerBoundingBox.extend(0, -256, 0); @@ -653,14 +646,8 @@ private void placeFinalBlocks() { } placedFinalBlocks = true; - BoundingBox playerBoundingBox; - if (session.getPlayerEntity().getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) { - playerBoundingBox = clientVehicle.getVehicleComponent().getBoundingBox().clone(); - } else { - playerBoundingBox = session.getCollisionManager().getPlayerBoundingBox().clone(); - } - Vector3i movement = getMovement(); + BoundingBox playerBoundingBox = session.getCollisionManager().getActiveBoundingBox().clone(); attachedBlocks.forEach((blockPos, state) -> { blockPos = blockPos.add(movement); // Don't place blocks that collide with the player From c86b41d198e41ddc394761c36b4524bba13fecc6 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Tue, 2 Jul 2024 08:03:15 -0400 Subject: [PATCH 23/26] Support world border --- .../entity/type/living/animal/PigEntity.java | 6 +- .../type/living/animal/StriderEntity.java | 6 +- .../entity/vehicle/VehicleComponent.java | 2 + .../geyser/session/cache/WorldBorder.java | 57 +++++++++++++++++++ .../player/BedrockMovePlayerTranslator.java | 4 +- 5 files changed, 72 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index b2702751c8b..2ec23d673db 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -102,11 +102,15 @@ public void setBoost(IntEntityMetadata entityMetadata) { @Override public void tick() { PlayerEntity player = getPlayerPassenger(); + if (player == null) { + return; + } + 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 + } else { // 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(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index ec9bfb99377..e06af278640 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -149,11 +149,15 @@ public void setBoost(IntEntityMetadata entityMetadata) { @Override public void tick() { PlayerEntity player = getPlayerPassenger(); + if (player == null) { + return; + } + 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 + } else { // 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(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 0ee3172b7b5..7834027f10a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -559,6 +559,8 @@ protected boolean travel(VehicleContext ctx, float speed) { motion.toDouble(), boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() ); + correctedMovement = vehicle.getSession().getWorldBorder().correctMovement(boundingBox, correctedMovement); + boundingBox.translate(correctedMovement); ctx.loadSurroundingBlocks(); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java index 8cb590f5768..397941b6fb3 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.math.GenericMath; import org.cloudburstmc.math.vector.Vector2d; +import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.LevelEventType; @@ -36,8 +37,11 @@ import lombok.Setter; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.session.GeyserSession; +import static org.geysermc.geyser.level.physics.CollisionManager.COLLISION_TOLERANCE; + public class WorldBorder { private static final double DEFAULT_WORLD_BORDER_SIZE = 5.9999968E7D; @@ -190,6 +194,59 @@ public boolean isWithinWarningBoundaries() { return entityPosition.getX() > warningMinX && entityPosition.getX() < warningMaxX && entityPosition.getZ() > warningMinZ && entityPosition.getZ() < warningMaxZ; } + /** + * Adjusts the movement of an entity so that it does not cross the world border. + * + * @param boundingBox bounding box of the entity + * @param movement movement of the entity + * @return the corrected movement + */ + public Vector3d correctMovement(BoundingBox boundingBox, Vector3d movement) { + Vector3d bbMin = boundingBox.getMin(); + Vector3d bbMax = boundingBox.getMax(); + + // If outside border, don't adjust movement + if (bbMin.getX() + COLLISION_TOLERANCE < GenericMath.floor(minX) + || bbMin.getZ() + COLLISION_TOLERANCE < GenericMath.floor(minZ) + || bbMax.getX() - COLLISION_TOLERANCE > GenericMath.ceil(maxX) + || bbMax.getZ() - COLLISION_TOLERANCE > GenericMath.ceil(maxZ)) { + return movement; + } + + double diff; // Difference between edge of bounding box and world border + double correctedX = movement.getX(); + diff = GenericMath.floor(minX) - (bbMin.getX() + movement.getX()); + if (diff > 0) { + correctedX += diff; + } else { + diff = (bbMax.getX() + movement.getX()) - GenericMath.ceil(maxX); + if (diff > 0) { + correctedX -= diff; + } + } + + if (Math.abs(correctedX) < COLLISION_TOLERANCE) { + correctedX = 0; + } + + double correctedZ = movement.getZ(); + diff = GenericMath.floor(minZ) - (bbMin.getZ() + movement.getZ()); + if (diff > 0) { + correctedZ += diff; + } else { + diff = (bbMax.getZ() + movement.getZ()) - GenericMath.ceil(maxZ); + if (diff > 0) { + correctedZ -= diff; + } + } + + if (Math.abs(correctedZ) < COLLISION_TOLERANCE) { + correctedZ = 0; + } + + return Vector3d.from(correctedX, movement.getY(), correctedZ); + } + /** * Updates the world border's minimum and maximum properties */ diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 3d612c481da..c8d167c9f9f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -30,6 +30,7 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -84,7 +85,8 @@ public void translate(GeyserSession session, MovePlayerPacket packet) { session.sendDownstreamGamePacket(playerRotationPacket); } else { - if (session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), true)) { + if (!(entity.getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) + && session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), true)) { return; } From 3a18ca9d16e9c004ac084c9bb4173ffd5993b955 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Wed, 3 Jul 2024 07:13:37 -0400 Subject: [PATCH 24/26] Optimize world border check --- .../entity/vehicle/VehicleComponent.java | 7 ++- .../geyser/session/cache/WorldBorder.java | 53 +++++++++---------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 7834027f10a..1d5e3d46f3c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -555,12 +555,11 @@ protected boolean travel(VehicleContext ctx, float speed) { motion = motion.mul(movementMultiplier); } - Vector3d correctedMovement = vehicle.getSession().getCollisionManager().correctMovement( - motion.toDouble(), boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() + Vector3d correctedMovement = vehicle.getSession().getWorldBorder().correctMovement(boundingBox, motion.toDouble()); + correctedMovement = vehicle.getSession().getCollisionManager().correctMovement( + correctedMovement, boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() ); - correctedMovement = vehicle.getSession().getWorldBorder().correctMovement(boundingBox, correctedMovement); - boundingBox.translate(correctedMovement); ctx.loadSurroundingBlocks(); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java index 397941b6fb3..0d16ae21143 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java @@ -205,46 +205,43 @@ public Vector3d correctMovement(BoundingBox boundingBox, Vector3d movement) { Vector3d bbMin = boundingBox.getMin(); Vector3d bbMax = boundingBox.getMax(); - // If outside border, don't adjust movement - if (bbMin.getX() + COLLISION_TOLERANCE < GenericMath.floor(minX) - || bbMin.getZ() + COLLISION_TOLERANCE < GenericMath.floor(minZ) - || bbMax.getX() - COLLISION_TOLERANCE > GenericMath.ceil(maxX) - || bbMax.getZ() - COLLISION_TOLERANCE > GenericMath.ceil(maxZ)) { + double correctedX; + if (movement.getX() < 0) { + correctedX = -limitMovement(-movement.getX(), bbMin.getX() - GenericMath.floor(minX)); + } else { + correctedX = limitMovement(movement.getX(), GenericMath.ceil(maxX) - bbMax.getX()); + } + + // Outside of border, don't adjust movement + if (Double.isNaN(correctedX)) { return movement; } - double diff; // Difference between edge of bounding box and world border - double correctedX = movement.getX(); - diff = GenericMath.floor(minX) - (bbMin.getX() + movement.getX()); - if (diff > 0) { - correctedX += diff; + double correctedZ; + if (movement.getZ() < 0) { + correctedZ = -limitMovement(-movement.getZ(), bbMin.getZ() - GenericMath.floor(minZ)); } else { - diff = (bbMax.getX() + movement.getX()) - GenericMath.ceil(maxX); - if (diff > 0) { - correctedX -= diff; - } + correctedZ = limitMovement(movement.getZ(), GenericMath.ceil(maxZ) - bbMax.getZ()); } - if (Math.abs(correctedX) < COLLISION_TOLERANCE) { - correctedX = 0; + if (Double.isNaN(correctedZ)) { + return movement; } - double correctedZ = movement.getZ(); - diff = GenericMath.floor(minZ) - (bbMin.getZ() + movement.getZ()); - if (diff > 0) { - correctedZ += diff; - } else { - diff = (bbMax.getZ() + movement.getZ()) - GenericMath.ceil(maxZ); - if (diff > 0) { - correctedZ -= diff; - } + return Vector3d.from(correctedX, movement.getY(), correctedZ); + } + + private double limitMovement(double movement, double limit) { + if (limit < 0) { + // Return NaN to indicate outside of border + return Double.NaN; } - if (Math.abs(correctedZ) < COLLISION_TOLERANCE) { - correctedZ = 0; + if (limit < COLLISION_TOLERANCE) { + return 0; } - return Vector3d.from(correctedX, movement.getY(), correctedZ); + return Math.min(movement, limit); } /** From 4c95d52bd080288a27d9ab5bd6dd018d1b47134c Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Tue, 9 Jul 2024 19:45:01 -0400 Subject: [PATCH 25/26] Small optimizations --- .../entity/vehicle/VehicleComponent.java | 21 ++++++++++++------- .../geyser/level/physics/BoundingBox.java | 16 ++++++++++++++ .../geyser/session/cache/WorldBorder.java | 12 +++++------ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 1d5e3d46f3c..2819617e648 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -824,21 +824,28 @@ protected float getJumpVelocityMultiplier(VehicleContext ctx) { protected class VehicleContext { private Vector3d centerPos; + private Vector3d cachePos; private BlockState centerBlock; private Vector3i supportingBlockPos; private BlockPositionIterator blockIter; private int[] blocks; protected void loadSurroundingBlocks() { - BoundingBox box = boundingBox.clone(); - box.expand(2); + this.centerPos = boundingBox.getBottomCenter(); - Vector3i min = box.getMin().toInt(); - Vector3i max = box.getMax().toInt(); - this.blockIter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); - this.blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), this.blockIter); + // 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); + + Vector3i min = box.getMin().toInt(); + Vector3i max = box.getMax().toInt(); + this.blockIter = BlockPositionIterator.fromMinMax(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); + this.blocks = vehicle.getSession().getGeyser().getWorldManager().getBlocksAt(vehicle.getSession(), this.blockIter); + + this.cachePos = this.centerPos; + } - this.centerPos = boundingBox.getBottomCenter(); this.centerBlock = getBlock(this.centerPos.toInt()); this.supportingBlockPos = null; } diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java index c4245964b9d..395467c0288 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java @@ -94,6 +94,14 @@ public Vector3d getMin() { return Vector3d.from(x, y, z); } + public double getMin(Axis axis) { + return switch (axis) { + case X -> middleX - sizeX / 2; + case Y -> middleY - sizeY / 2; + case Z -> middleZ - sizeZ / 2; + }; + } + public Vector3d getMax() { double x = middleX + sizeX / 2; double y = middleY + sizeY / 2; @@ -101,6 +109,14 @@ public Vector3d getMax() { return Vector3d.from(x, y, z); } + public double getMax(Axis axis) { + return switch (axis) { + case X -> middleX + sizeX / 2; + case Y -> middleY + sizeY / 2; + case Z -> middleZ + sizeZ / 2; + }; + } + public Vector3d getBottomCenter() { return Vector3d.from(middleX, middleY - sizeY / 2, middleZ); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java index 0d16ae21143..35579db1309 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java @@ -37,6 +37,7 @@ import lombok.Setter; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.level.physics.Axis; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.session.GeyserSession; @@ -202,14 +203,11 @@ public boolean isWithinWarningBoundaries() { * @return the corrected movement */ public Vector3d correctMovement(BoundingBox boundingBox, Vector3d movement) { - Vector3d bbMin = boundingBox.getMin(); - Vector3d bbMax = boundingBox.getMax(); - double correctedX; if (movement.getX() < 0) { - correctedX = -limitMovement(-movement.getX(), bbMin.getX() - GenericMath.floor(minX)); + correctedX = -limitMovement(-movement.getX(), boundingBox.getMin(Axis.X) - GenericMath.floor(minX)); } else { - correctedX = limitMovement(movement.getX(), GenericMath.ceil(maxX) - bbMax.getX()); + correctedX = limitMovement(movement.getX(), GenericMath.ceil(maxX) - boundingBox.getMax(Axis.X)); } // Outside of border, don't adjust movement @@ -219,9 +217,9 @@ public Vector3d correctMovement(BoundingBox boundingBox, Vector3d movement) { double correctedZ; if (movement.getZ() < 0) { - correctedZ = -limitMovement(-movement.getZ(), bbMin.getZ() - GenericMath.floor(minZ)); + correctedZ = -limitMovement(-movement.getZ(), boundingBox.getMin(Axis.Z) - GenericMath.floor(minZ)); } else { - correctedZ = limitMovement(movement.getZ(), GenericMath.ceil(maxZ) - bbMax.getZ()); + correctedZ = limitMovement(movement.getZ(), GenericMath.ceil(maxZ) - boundingBox.getMax(Axis.Z)); } if (Double.isNaN(correctedZ)) { From 64415144c2a1b4306b142f90221740f5f6f4cf4e Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Wed, 17 Jul 2024 00:40:11 -0400 Subject: [PATCH 26/26] Add comments --- .../entity/vehicle/CamelVehicleComponent.java | 3 + .../entity/vehicle/VehicleComponent.java | 141 +++++++++++++----- .../player/BedrockMovePlayerTranslator.java | 1 + 3 files changed, 107 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index eb96b66b666..7d022ed7c82 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -51,6 +51,8 @@ public CamelVehicleComponent(CamelEntity vehicle) { } public void startDashCooldown() { + // tickVehicle is only called while the vehicle is mounted. Use session ticks to keep + // track of time instead of counting down this.dashTick = vehicle.getSession().getTicks() + DASH_TICKS; } @@ -72,6 +74,7 @@ public void tickVehicle() { @Override public void onDismount() { + // Prevent camel from getting stuck in dash animation vehicle.setFlag(EntityFlag.HAS_DASH_COOLDOWN, false); vehicle.updateBedrockMetadata(); super.onDismount(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index 2819617e648..db703a3cbd2 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -62,7 +62,6 @@ public class VehicleComponent { 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; private static final float MIN_VELOCITY = 0.003f; - private static final float CLIMB_SPEED = 0.15f; protected final T vehicle; protected final BoundingBox boundingBox; @@ -134,12 +133,6 @@ public void removeEffect(Effect effect) { } } - public Vector3d correctMovement(Vector3d movement) { - return vehicle.getSession().getCollisionManager().correctMovement( - movement, boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() - ); - } - public void setMoveSpeed(float moveSpeed) { this.moveSpeed = moveSpeed; } @@ -156,6 +149,12 @@ public void setGravity(double gravity) { this.gravity = MathUtils.constrain(gravity, -1.0, 1.0); } + public Vector3d correctMovement(Vector3d movement) { + return vehicle.getSession().getCollisionManager().correctMovement( + movement, boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() + ); + } + public void onMount() { vehicle.getSession().getPlayerEntity().setVehicleInput(Vector2f.ZERO); vehicle.getSession().getPlayerEntity().setVehicleJumpStrength(0); @@ -165,6 +164,9 @@ public void onDismount() { // } + /** + * Called every session tick while the player is mounted on the vehicle. + */ public void tickVehicle() { if (!vehicle.isClientControlled()) { return; @@ -187,6 +189,12 @@ public void tickVehicle() { } } + /** + * Adds velocity of all colliding fluids to the vehicle, and returns the height of the fluid to use for movement. + * + * @param ctx context + * @return type and height of fluid to use for movement + */ protected ObjectDoublePair updateFluidMovement(VehicleContext ctx) { BoundingBox box = boundingBox.clone(); box.expand(-0.001); @@ -199,10 +207,10 @@ protected ObjectDoublePair updateFluidMovement(VehicleContext ctx) { double waterHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.WATER, 0.014, min.getY()); double lavaHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY()); + // Apply upward motion if the vehicle is a Strider, and it is submerged in lava if (lavaHeight > 0 && vehicle.getDefinition().entityType() == EntityType.STRIDER) { Vector3i blockPos = ctx.centerPos().toInt(); - if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) - || ctx.getBlock(blockPos.up()).is(Blocks.LAVA)) { + if (!CollisionManager.FLUID_COLLISION.isBelow(blockPos.getY(), boundingBox) || ctx.getBlock(blockPos.up()).is(Blocks.LAVA)) { vehicle.setMotion(vehicle.getMotion().mul(0.5f).add(0, 0.05f, 0)); } else { vehicle.setOnGround(true); @@ -221,6 +229,16 @@ protected ObjectDoublePair updateFluidMovement(VehicleContext ctx) { return EMPTY_FLUID_PAIR; } + /** + * Calculates how deep the vehicle is in a fluid, and applies its velocity. + * + * @param ctx context + * @param iter iterator of colliding blocks + * @param fluid type of fluid + * @param speed multiplier for fluid motion + * @param minY minY of the bounding box used to check for fluid collision; not exactly the same as the vehicle's bounding box + * @return height of fluid compared to minY + */ protected double getFluidHeightAndApplyMovement(VehicleContext ctx, BlockPositionIterator iter, Fluid fluid, double speed, double minY) { Vector3d totalVelocity = Vector3d.ZERO; double maxFluidHeight = 0; @@ -237,10 +255,14 @@ protected double getFluidHeightAndApplyMovement(VehicleContext ctx, BlockPositio double vehicleFluidHeight = blockPos.getY() + worldFluidHeight - minY; if (vehicleFluidHeight < 0) { + // Vehicle is not submerged in this fluid block continue; } - boolean flowBlocked = worldFluidHeight != 1; // This is only used for determining if a falling fluid should drag the vehicle downwards + // flowBlocked is only used when determining if a falling fluid should drag the vehicle downwards. + // If this block is not a falling fluid, set to true to avoid unnecessary checks. + boolean flowBlocked = worldFluidHeight != 1; + Vector3d velocity = Vector3d.ZERO; for (Direction direction : Direction.HORIZONTAL) { Vector3i adjacentBlockPos = blockPos.add(direction.getUnitVector()); @@ -260,6 +282,7 @@ protected double getFluidHeightAndApplyMovement(VehicleContext ctx, BlockPositio fluidHeightDiff = getLogicalFluidHeight(fluid, blockId) - (adjacentFluidHeight - MAX_LOGICAL_FLUID_HEIGHT); } } else if (!flowBlocked) { + // No need to check if flow is already blocked from another direction, or if this isn't a falling fluid. flowBlocked = isFlowBlocked(fluid, adjacentBlockId); } } @@ -398,7 +421,14 @@ protected void landMovement(VehicleContext ctx) { boolean horizontalCollision = travel(ctx, speed); if (isClimbing(ctx)) { - vehicle.setMotion(getClimbingSpeed(horizontalCollision)); + Vector3f motion = vehicle.getMotion(); + vehicle.setMotion( + Vector3f.from( + MathUtils.clamp(motion.getX(), -0.15f, 0.15f), + horizontalCollision ? 0.2f : Math.max(motion.getY(), -0.15f), + MathUtils.clamp(motion.getZ(), -0.15f, 0.15f) + ) + ); // NOT IMPLEMENTED: climbing in powdered snow } @@ -435,15 +465,6 @@ protected boolean shouldApplyFluidJumpBoost(VehicleContext ctx, double originalY return true; } - protected Vector3f getClimbingSpeed(boolean horizontalCollision) { - Vector3f motion = vehicle.getMotion(); - return Vector3f.from( - MathUtils.clamp(motion.getX(), -CLIMB_SPEED, CLIMB_SPEED), - horizontalCollision ? 0.2f : Math.max(motion.getY(), -CLIMB_SPEED), - MathUtils.clamp(motion.getZ(), -CLIMB_SPEED, CLIMB_SPEED) - ); - } - protected Vector3f getFluidGravity(double gravity, boolean falling) { Vector3f motion = vehicle.getMotion(); if (gravity != 0 && !vehicle.getFlag(EntityFlag.SPRINTING)) { @@ -456,6 +477,14 @@ protected Vector3f getFluidGravity(double gravity, boolean falling) { return motion; } + /** + * Check if any blocks the vehicle is colliding with should multiply movement. (Cobweb, powder snow, berry bush) + *

+ * This is different from the speed factor of a block the vehicle is standing on, such as soul sand. + * + * @param ctx context + * @return the multiplier + */ protected @Nullable Vector3f getBlockMovementMultiplier(VehicleContext ctx) { BoundingBox box = boundingBox.clone(); box.expand(-1.0E-7); @@ -464,6 +493,7 @@ protected Vector3f getFluidGravity(double gravity, boolean falling) { Vector3i max = box.getMax().toInt(); // Iterate xyz backwards + // Minecraft iterates forwards but only the last multiplier affects movement for (int x = max.getX(); x >= min.getX(); x--) { for (int y = max.getY(); y >= min.getY(); y--) { for (int z = max.getZ(); z >= min.getZ(); z--) { @@ -532,7 +562,9 @@ protected void onBubbleColumnCollision(boolean drag) { } /** - * @return True if there was a horizontal collision + * Calculates the next position of the vehicle while checking for collision and adjusting velocity. + * + * @return true if there was a horizontal collision */ protected boolean travel(VehicleContext ctx, float speed) { Vector3f motion = vehicle.getMotion(); @@ -546,22 +578,24 @@ protected boolean travel(VehicleContext ctx, float speed) { Math.abs(motion.getZ()) < MIN_VELOCITY ? 0 : motion.getZ() ); - // TODO: isImmobile? set input to 0 and jump to false - - motion = motion.add(getInputVelocity(ctx, speed)); + // !isImmobile + if (vehicle.isAlive()) { + motion = motion.add(getInputVelocity(ctx, speed)); + } Vector3f movementMultiplier = getBlockMovementMultiplier(ctx); if (movementMultiplier != null) { motion = motion.mul(movementMultiplier); } + // Check world border before blocks Vector3d correctedMovement = vehicle.getSession().getWorldBorder().correctMovement(boundingBox, motion.toDouble()); correctedMovement = vehicle.getSession().getCollisionManager().correctMovement( correctedMovement, boundingBox, vehicle.isOnGround(), this.stepHeight, true, vehicle.canWalkOnLava() ); boundingBox.translate(correctedMovement); - ctx.loadSurroundingBlocks(); + ctx.loadSurroundingBlocks(); // Context must be reloaded after vehicle is moved // Non-zero values indicate a collision on that axis Vector3d moveDiff = motion.toDouble().sub(correctedMovement); @@ -632,16 +666,13 @@ protected boolean isClimbing(VehicleContext ctx) { return false; } - protected Vector2f normalizeInput(Vector2f input) { - float lenSquared = input.lengthSquared(); - if (lenSquared < 1.0E-7) { - return Vector2f.ZERO; - } else if (lenSquared > 1.0) { - return input.normalize(); - } - return input; - } - + /** + * Translates the player's input into velocity. + * + * @param ctx context + * @param speed multiplier for input + * @return velocity + */ protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput(); input = input.mul(0.98f); @@ -649,17 +680,37 @@ protected Vector3f getInputVelocity(VehicleContext ctx, float speed) { input = normalizeInput(input); input = input.mul(speed); + // Match player rotation float yaw = vehicle.getSession().getPlayerEntity().getYaw(); float sin = TrigMath.sin(yaw * TrigMath.DEG_TO_RAD); float cos = TrigMath.cos(yaw * TrigMath.DEG_TO_RAD); return Vector3f.from(input.getX() * cos - input.getY() * sin, 0, input.getY() * cos + input.getX() * sin); } + protected Vector2f normalizeInput(Vector2f input) { + float lenSquared = input.lengthSquared(); + if (lenSquared < 1.0E-7) { + return Vector2f.ZERO; + } else if (lenSquared > 1.0) { + return input.normalize(); + } + return input; + } + + /** + * Gets the rotation to use for the vehicle. This is based on the player's head rotation. + */ protected Vector2f getVehicleRotation() { LivingEntity player = vehicle.getSession().getPlayerEntity(); return Vector2f.from(player.getYaw(), player.getPitch() * 0.5f); } + /** + * Sets the new position for the vehicle and sends packets to both the java server and bedrock client. + *

+ * This also updates the session's last vehicle move timestamp. + * @param javaPos the new java position of the vehicle + */ protected void moveVehicle(Vector3d javaPos) { Vector3f bedrockPos = javaPos.toFloat(); Vector2f rotation = getVehicleRotation(); @@ -769,7 +820,10 @@ protected double getGravity() { return result; } - protected BlockState getBlockUnder(VehicleContext ctx, float dist) { + /** + * Returns the block that is x amount of blocks under the main supporting block. + */ + protected BlockState getBlockUnderSupport(VehicleContext ctx, float dist) { Vector3i supportingBlockPos = ctx.supportingBlockPos(); Vector3i blockPos; @@ -782,12 +836,18 @@ protected BlockState getBlockUnder(VehicleContext ctx, float dist) { return ctx.getBlock(blockPos); } + /** + * The block to use when determining if the vehicle should bounce after landing. Currently just slime and bed blocks. + */ protected BlockState getLandingBlock(VehicleContext ctx) { - return getBlockUnder(ctx, 0.2f); + return getBlockUnderSupport(ctx, 0.2f); } + /** + * The block to use when calculating slipperiness and speed. If on a slab, this will be the block under the slab. + */ protected BlockState getVelocityBlock(VehicleContext ctx) { - return getBlockUnder(ctx, 0.500001f); + return getBlockUnderSupport(ctx, 0.500001f); } protected float getVelocityMultiplier(VehicleContext ctx) { @@ -830,6 +890,11 @@ protected class VehicleContext { private BlockPositionIterator blockIter; private int[] blocks; + /** + * Cache frequently used data and blocks used in movement calculations. + *

+ * Can be called multiple times, and must be called at least once before using the VehicleContext. + */ protected void loadSurroundingBlocks() { this.centerPos = boundingBox.getBottomCenter(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index c8d167c9f9f..3faa3242bba 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -85,6 +85,7 @@ public void translate(GeyserSession session, MovePlayerPacket packet) { session.sendDownstreamGamePacket(playerRotationPacket); } else { + // World border collision will be handled by client vehicle if (!(entity.getVehicle() instanceof ClientVehicle clientVehicle && clientVehicle.isClientControlled()) && session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), true)) { return;