mirror of
https://github.com/GeyserMC/Geyser.git
synced 2026-01-04 15:31:36 +00:00
Various movement fixes: Send elytra gliding states properly, send sprinting input earlier, don't allow sprinting on water (#5518)
* Prevent elytra gliding to match Java client behavior To-Do's: - Firework boosting is still very broken - see how BDS handles this mess Other changes: Don't send player's onGround when riding vehicles Support null values for data components (removal of default components) Fix possible NPE in JavaSetEquipmentTranslator with debug mode enabled * Add bounding box height update when a player starts gliding, send sprinting state at correct time * Properly track spin attack - should fix <https://github.com/GeyserMC/Geyser/issues/5262> - and don't allow the Bedrock client to stop gliding mid-air * also correct pose * temp * Cleanup * Send sprinting state before inputs, resolve issue mentioned in https://github.com/GeyserMC/Geyser/issues/1705 * send sprinting after inputs * Send vehicle input before sprinting which is sent before player movement * Slight cleanup * Rename setDimensions -> setDimensionsFromPose * setSpinAttack should be protected * Resolve mobile players being unable to climb down scaffolding * Don't send STOP_SPRINTING packet if we weren't sprinting
This commit is contained in:
@@ -171,7 +171,7 @@ public class Entity implements GeyserEntity {
|
||||
dirtyMetadata.put(EntityDataTypes.SCALE, 1f);
|
||||
dirtyMetadata.put(EntityDataTypes.COLOR, (byte) 0);
|
||||
dirtyMetadata.put(EntityDataTypes.AIR_SUPPLY_MAX, getMaxAir());
|
||||
setDimensions(Pose.STANDING);
|
||||
setDimensionsFromPose(Pose.STANDING);
|
||||
setFlag(EntityFlag.HAS_GRAVITY, true);
|
||||
setFlag(EntityFlag.HAS_COLLISION, true);
|
||||
setFlag(EntityFlag.CAN_SHOW_NAME, true);
|
||||
@@ -405,7 +405,7 @@ public class Entity implements GeyserEntity {
|
||||
setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08);
|
||||
|
||||
// Swimming is ignored here and instead we rely on the pose
|
||||
setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
|
||||
setGliding((xd & 0x80) == 0x80);
|
||||
|
||||
setInvisible((xd & 0x20) == 0x20);
|
||||
}
|
||||
@@ -419,6 +419,13 @@ public class Entity implements GeyserEntity {
|
||||
setFlag(EntityFlag.INVISIBLE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a boolean - whether the entity is gliding
|
||||
*/
|
||||
protected void setGliding(boolean value) {
|
||||
setFlag(EntityFlag.GLIDING, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an int from 0 - this entity's maximum air - (air / maxAir) represents the percentage of bubbles left
|
||||
*/
|
||||
@@ -529,15 +536,16 @@ public class Entity implements GeyserEntity {
|
||||
*/
|
||||
public void setPose(Pose pose) {
|
||||
setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING));
|
||||
// FALL_FLYING is instead set via setFlags
|
||||
// Triggered when crawling
|
||||
setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING));
|
||||
setDimensions(pose);
|
||||
setDimensionsFromPose(pose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the height and width of the entity's bounding box
|
||||
*/
|
||||
protected void setDimensions(Pose pose) {
|
||||
protected void setDimensionsFromPose(Pose pose) {
|
||||
// No flexibility options for basic entities
|
||||
setBoundingBoxHeight(definition.height());
|
||||
setBoundingBoxWidth(definition.width());
|
||||
|
||||
@@ -27,9 +27,7 @@ package org.geysermc.geyser.entity.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.TooltipOptions;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@@ -72,20 +70,22 @@ public class FireworkEntity extends Entity {
|
||||
// and checks to make sure the player that is gliding is the one getting sent the packet
|
||||
// or else every player near the gliding player will boost too.
|
||||
if (optional.isPresent() && optional.getAsInt() == session.getPlayerEntity().getEntityId()) {
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
float yaw = entity.getYaw();
|
||||
float pitch = entity.getPitch();
|
||||
// Uses math from NukkitX
|
||||
entity.setMotion(Vector3f.from(
|
||||
-Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
|
||||
-Math.sin(Math.toRadians(pitch)) * 2,
|
||||
Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
|
||||
// Need to update the EntityMotionPacket or else the player won't boost
|
||||
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
|
||||
entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
entityMotionPacket.setMotion(entity.getMotion());
|
||||
|
||||
session.sendUpstreamPacket(entityMotionPacket);
|
||||
// TODO Firework rocket boosting is client side. Sending this boost is no longer needed
|
||||
// Good luck to whoever is going to try implementing cancelling firework rocket boosting :)
|
||||
// PlayerEntity entity = session.getPlayerEntity();
|
||||
// float yaw = entity.getYaw();
|
||||
// float pitch = entity.getPitch();
|
||||
// // Uses math from NukkitX
|
||||
// entity.setMotion(Vector3f.from(
|
||||
// -Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
|
||||
// -Math.sin(Math.toRadians(pitch)) * 2,
|
||||
// Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
|
||||
// // Need to update the EntityMotionPacket or else the player won't boost
|
||||
// SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
|
||||
// entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
// entityMotionPacket.setMotion(entity.getMotion());
|
||||
//
|
||||
// session.sendUpstreamPacket(entityMotionPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,12 +206,16 @@ public class LivingEntity extends Entity {
|
||||
setFlag(EntityFlag.BLOCKING, isUsingItem && isUsingShield);
|
||||
|
||||
// Riptide spin attack
|
||||
setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
|
||||
setSpinAttack((xd & 0x04) == 0x04);
|
||||
|
||||
// OptionalPack usage
|
||||
setFlag(EntityFlag.EMERGING, isUsingItem && isUsingOffhand);
|
||||
}
|
||||
|
||||
protected void setSpinAttack(boolean value) {
|
||||
setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, value);
|
||||
}
|
||||
|
||||
public void setHealth(FloatEntityMetadata entityMetadata) {
|
||||
this.health = entityMetadata.getPrimitiveValue();
|
||||
|
||||
@@ -279,12 +283,12 @@ public class LivingEntity extends Entity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
protected void setDimensionsFromPose(Pose pose) {
|
||||
if (pose == Pose.SLEEPING) {
|
||||
setBoundingBoxWidth(0.2f);
|
||||
setBoundingBoxHeight(0.2f);
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
super.setDimensionsFromPose(pose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,12 +63,12 @@ public class GoatEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
protected void setDimensionsFromPose(Pose pose) {
|
||||
if (pose == Pose.LONG_JUMPING) {
|
||||
setBoundingBoxWidth(LONG_JUMPING_WIDTH);
|
||||
setBoundingBoxHeight(LONG_JUMPING_HEIGHT);
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
super.setDimensionsFromPose(pose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,12 @@ public class SnifferEntity extends AnimalEntity implements Tickable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
protected void setDimensionsFromPose(Pose pose) {
|
||||
if (getFlag(EntityFlag.DIGGING)) {
|
||||
setBoundingBoxHeight(DIGGING_HEIGHT);
|
||||
setBoundingBoxWidth(definition.width());
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
super.setDimensionsFromPose(pose);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class SnifferEntity extends AnimalEntity implements Tickable {
|
||||
setFlag(EntityFlag.DIGGING, snifferState == SnifferState.DIGGING);
|
||||
setFlag(EntityFlag.RISING, snifferState == SnifferState.RISING);
|
||||
|
||||
setDimensions(pose);
|
||||
setDimensionsFromPose(pose);
|
||||
|
||||
if (getFlag(EntityFlag.DIGGING)) {
|
||||
digTicks = DIG_END;
|
||||
|
||||
@@ -113,12 +113,12 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
protected void setDimensionsFromPose(Pose pose) {
|
||||
if (pose == Pose.SITTING) {
|
||||
setBoundingBoxHeight(definition.height() - SITTING_HEIGHT_DIFFERENCE);
|
||||
setBoundingBoxWidth(definition.width());
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
super.setDimensionsFromPose(pose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
public void setDimensionsFromPose(Pose pose) {
|
||||
float height;
|
||||
float width;
|
||||
switch (pose) {
|
||||
@@ -433,7 +433,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
width = 0.2f;
|
||||
}
|
||||
default -> {
|
||||
super.setDimensions(pose);
|
||||
super.setDimensionsFromPose(pose);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
@@ -38,13 +39,20 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.TrapDoorBlock;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.BlockTag;
|
||||
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.Effect;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos;
|
||||
@@ -52,6 +60,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -196,6 +206,16 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setGliding(boolean value) {
|
||||
session.setGliding(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setSpinAttack(boolean value) {
|
||||
session.setSpinAttack(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.19.40, the client must be re-informed of its bounding box on respawn
|
||||
* See <a href="https://github.com/GeyserMC/Geyser/issues/3370">issue 3370</a>
|
||||
@@ -428,4 +448,67 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
return velocity + 0.1F * session.getEffectCache().getJumpPower();
|
||||
}
|
||||
|
||||
public boolean isOnClimbableBlock() {
|
||||
if (session.getGameMode() == GameMode.SPECTATOR) {
|
||||
return false;
|
||||
}
|
||||
Vector3i pos = getPosition().down(EntityDefinitions.PLAYER.offset()).toInt();
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, pos);
|
||||
if (session.getTagCache().is(BlockTag.CLIMBABLE, state.block())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.block() instanceof TrapDoorBlock) {
|
||||
if (!state.getValue(Properties.OPEN)) {
|
||||
return false;
|
||||
} else {
|
||||
BlockState belowState = session.getGeyser().getWorldManager().blockAt(session, pos.down());
|
||||
return belowState.is(Blocks.LADDER) && belowState.getValue(Properties.HORIZONTAL_FACING) == state.getValue(Properties.HORIZONTAL_FACING);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canStartGliding() {
|
||||
// You can't start gliding when levitation is applied
|
||||
if (session.getEffectCache().getEntityEffects().contains(Effect.LEVITATION)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isOnClimbableBlock() || session.getPlayerEntity().isOnGround()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (session.getCollisionManager().isPlayerTouchingWater()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unfortunately gliding is still client-side, so we cannot force the client to glide even
|
||||
// if we wanted to. However, we still need to check that gliding is possible even with, say,
|
||||
// an elytra that does not have the glider component.
|
||||
for (Map.Entry<EquipmentSlot, GeyserItemStack> entry : session.getPlayerInventory().getEquipment().entrySet()) {
|
||||
if (entry.getValue().getComponent(DataComponentTypes.GLIDER) != null) {
|
||||
Equippable equippable = entry.getValue().getComponent(DataComponentTypes.EQUIPPABLE);
|
||||
if (equippable != null && equippable.slot() == entry.getKey() && !entry.getValue().nextDamageWillBreak()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Bedrock will NOT allow flight when not wearing an elytra; even if it doesn't have a glider component
|
||||
if (entry.getKey() == EquipmentSlot.CHESTPLATE && !entry.getValue().asItem().equals(Items.ELYTRA)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void forceFlagUpdate() {
|
||||
setFlagsDirty(true);
|
||||
}
|
||||
|
||||
public boolean isGliding() {
|
||||
return getFlag(EntityFlag.GLIDING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,14 +126,14 @@ public class GeyserItemStack {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link DataComponents} that aren't the base/default components.
|
||||
* @return the {@link DataComponents} patch that's sent over the network.
|
||||
*/
|
||||
public @Nullable DataComponents getComponents() {
|
||||
return isEmpty() ? null : components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this GeyserItemStack has any additional components on top of
|
||||
* @return whether this GeyserItemStack has any component modifications additional to
|
||||
* the base item components.
|
||||
*/
|
||||
public boolean hasNonBaseComponents() {
|
||||
@@ -159,16 +159,13 @@ public class GeyserItemStack {
|
||||
*/
|
||||
@Nullable
|
||||
public <T> T getComponent(@NonNull DataComponentType<T> type) {
|
||||
if (components == null) {
|
||||
return asItem().getComponent(type);
|
||||
// A data component patch may contain null values to remove base components
|
||||
// e.g. an elytra without the glider component
|
||||
if (components != null && components.contains(type)) {
|
||||
return components.get(type);
|
||||
}
|
||||
|
||||
T value = components.get(type);
|
||||
if (value == null) {
|
||||
return asItem().getComponent(type);
|
||||
}
|
||||
|
||||
return value;
|
||||
return asItem().getComponent(type);
|
||||
}
|
||||
|
||||
public <T> T getComponentElseGet(@NonNull DataComponentType<T> type, Supplier<T> supplier) {
|
||||
@@ -251,6 +248,24 @@ public class GeyserItemStack {
|
||||
return new ItemStackSlotDisplay(this.getItemStack());
|
||||
}
|
||||
|
||||
public int getMaxDamage() {
|
||||
return getComponentElseGet(DataComponentTypes.MAX_DAMAGE, () -> 0);
|
||||
}
|
||||
|
||||
public int getDamage() {
|
||||
// Damage can't be negative
|
||||
int damage = Math.max(this.getComponentElseGet(DataComponentTypes.DAMAGE, () -> 0), 0);
|
||||
return Math.min(damage, this.getMaxDamage());
|
||||
}
|
||||
|
||||
public boolean nextDamageWillBreak() {
|
||||
return this.isDamageable() && this.getDamage() >= this.getMaxDamage() - 1;
|
||||
}
|
||||
|
||||
public boolean isDamageable() {
|
||||
return getComponent(DataComponentTypes.MAX_DAMAGE) != null && getComponent(DataComponentTypes.UNBREAKABLE) == null && getComponent(DataComponentTypes.DAMAGE) != null;
|
||||
}
|
||||
|
||||
public Item asItem() {
|
||||
if (isEmpty()) {
|
||||
return Items.AIR;
|
||||
|
||||
@@ -31,9 +31,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class PlayerInventory extends Inventory {
|
||||
/**
|
||||
@@ -83,6 +86,18 @@ public class PlayerInventory extends Inventory {
|
||||
return items[36 + heldItemSlot];
|
||||
}
|
||||
|
||||
// TODO other equipment slots
|
||||
public Map<EquipmentSlot, GeyserItemStack> getEquipment() {
|
||||
return Map.of(
|
||||
EquipmentSlot.MAIN_HAND, getItemInHand(),
|
||||
EquipmentSlot.OFF_HAND, items[45],
|
||||
EquipmentSlot.BOOTS, items[8],
|
||||
EquipmentSlot.LEGGINGS, items[7],
|
||||
EquipmentSlot.CHESTPLATE, items[6],
|
||||
EquipmentSlot.HELMET, items[5]
|
||||
);
|
||||
}
|
||||
|
||||
public boolean eitherHandMatchesItem(@NonNull Item item) {
|
||||
return getItemInHand().asItem() == item || getItemInHand(Hand.OFF_HAND).asItem() == item;
|
||||
}
|
||||
|
||||
@@ -424,6 +424,14 @@ public class CollisionManager {
|
||||
return state.is(Blocks.WATER) && state.getValue(Properties.LEVEL) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if the player is currently touching water
|
||||
*/
|
||||
public boolean isPlayerTouchingWater() {
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getPlayerEntity().position().toInt());
|
||||
return state.is(Blocks.WATER);
|
||||
}
|
||||
|
||||
public boolean isWaterInEyes() {
|
||||
double eyeX = playerBoundingBox.getMiddleX();
|
||||
double eyeY = playerBoundingBox.getMiddleY() - playerBoundingBox.getSizeY() / 2d + session.getEyeHeight();
|
||||
|
||||
@@ -1260,6 +1260,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
setSneaking(false);
|
||||
}
|
||||
|
||||
public void setSpinAttack(boolean spinAttack) {
|
||||
switchPose(spinAttack, EntityFlag.DAMAGE_NEARBY_MOBS, Pose.SPIN_ATTACK);
|
||||
}
|
||||
|
||||
public void setGliding(boolean gliding) {
|
||||
switchPose(gliding, EntityFlag.GLIDING, Pose.FALL_FLYING);
|
||||
}
|
||||
|
||||
private void setSneaking(boolean sneaking) {
|
||||
this.sneaking = sneaking;
|
||||
|
||||
@@ -1296,22 +1304,17 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
playerEntity.updateBedrockMetadata();
|
||||
return;
|
||||
}
|
||||
toggleSwimmingPose(swimming, EntityFlag.SWIMMING);
|
||||
switchPose(swimming, EntityFlag.SWIMMING, Pose.SWIMMING);
|
||||
}
|
||||
|
||||
public void setCrawling(boolean crawling) {
|
||||
toggleSwimmingPose(crawling, EntityFlag.CRAWLING);
|
||||
switchPose(crawling, EntityFlag.CRAWLING, Pose.SWIMMING);
|
||||
}
|
||||
|
||||
private void toggleSwimmingPose(boolean crawling, EntityFlag flag) {
|
||||
if (crawling) {
|
||||
this.pose = Pose.SWIMMING;
|
||||
playerEntity.setBoundingBoxHeight(0.6f);
|
||||
} else {
|
||||
this.pose = Pose.STANDING;
|
||||
playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height());
|
||||
}
|
||||
playerEntity.setFlag(flag, crawling);
|
||||
private void switchPose(boolean value, EntityFlag flag, Pose pose) {
|
||||
this.pose = value ? pose : Pose.STANDING;
|
||||
playerEntity.setDimensionsFromPose(this.pose);
|
||||
playerEntity.setFlag(flag, value);
|
||||
playerEntity.updateBedrockMetadata();
|
||||
}
|
||||
|
||||
@@ -1975,7 +1978,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
FALL_FLYING, // Elytra
|
||||
SPIN_ATTACK -> 0.4f; // Trident spin attack
|
||||
case SLEEPING -> 0.2f;
|
||||
default -> EntityDefinitions.PLAYER.offset();
|
||||
default -> EntityDefinitions.PLAYER.offset(); // 1.62F
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.InputMode;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket;
|
||||
@@ -43,6 +43,7 @@ import java.util.Set;
|
||||
public final class InputCache {
|
||||
private final GeyserSession session;
|
||||
private ServerboundPlayerInputPacket inputPacket = new ServerboundPlayerInputPacket(false, false, false, false, false, false, false);
|
||||
@Setter
|
||||
private boolean lastHorizontalCollision;
|
||||
private int ticksSinceLastMovePacket;
|
||||
@Getter @Setter
|
||||
@@ -56,7 +57,7 @@ public final class InputCache {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void processInputs(PlayerEntity entity, PlayerAuthInputPacket packet) {
|
||||
public void processInputs(SessionPlayerEntity entity, PlayerAuthInputPacket packet) {
|
||||
// Input is sent to the server before packet positions, as of 1.21.2
|
||||
Set<PlayerAuthInputData> bedrockInput = packet.getInputData();
|
||||
var oldInputPacket = this.inputPacket;
|
||||
@@ -77,19 +78,25 @@ public final class InputCache {
|
||||
right = analogMovement.getX() < 0;
|
||||
}
|
||||
|
||||
boolean sneaking = bedrockInput.contains(PlayerAuthInputData.SNEAKING);
|
||||
|
||||
// TODO when is UP_LEFT, etc. used?
|
||||
this.inputPacket = this.inputPacket
|
||||
.withForward(up)
|
||||
.withBackward(down)
|
||||
.withLeft(left)
|
||||
.withRight(right)
|
||||
.withJump(bedrockInput.contains(PlayerAuthInputData.JUMPING)) // Looks like this only triggers when the JUMP key input is being pressed. There's also JUMP_DOWN?
|
||||
.withShift(sneaking)
|
||||
.withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINTING)); // SPRINTING will trigger even if the player isn't moving
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/enums.html
|
||||
// using the "raw" values allows us sending key presses even with locked input
|
||||
.withJump(bedrockInput.contains(PlayerAuthInputData.JUMP_CURRENT_RAW))
|
||||
.withShift(bedrockInput.contains(PlayerAuthInputData.SNEAK_CURRENT_RAW))
|
||||
.withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINT_DOWN));
|
||||
|
||||
// Send sneaking state before inputs, matches Java client
|
||||
boolean sneaking = bedrockInput.contains(PlayerAuthInputData.SNEAKING) ||
|
||||
// DESCEND_BLOCK is ONLY sent while mobile clients are descending scaffolding.
|
||||
// PERSIST_SNEAK is ALWAYS sent by mobile clients.
|
||||
// While we could use SNEAK_CURRENT_RAW, that would also be sent with locked inputs.
|
||||
// fixes https://github.com/GeyserMC/Geyser/issues/5384
|
||||
(bedrockInput.contains(PlayerAuthInputData.DESCEND_BLOCK) && bedrockInput.contains(PlayerAuthInputData.PERSIST_SNEAK));
|
||||
if (oldInputPacket.isShift() != sneaking) {
|
||||
if (sneaking) {
|
||||
session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.START_SNEAKING));
|
||||
@@ -121,8 +128,4 @@ public final class InputCache {
|
||||
public boolean lastHorizontalCollision() {
|
||||
return lastHorizontalCollision;
|
||||
}
|
||||
|
||||
public void setLastHorizontalCollision(boolean lastHorizontalCollision) {
|
||||
this.lastHorizontalCollision = lastHorizontalCollision;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.translator.protocol.bedrock;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.data.Ability;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.RequestAbilityPacket;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
/**
|
||||
* Replaces the AdventureSettingsPacket completely in 1.19.30.
|
||||
*/
|
||||
@Translator(packet = RequestAbilityPacket.class)
|
||||
public class BedrockRequestAbilityTranslator extends PacketTranslator<RequestAbilityPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, RequestAbilityPacket packet) {
|
||||
// TODO: Since 1.20.30, this was replaced by a START_FLYING and STOP_FLYING case in BedrockActionTranslator
|
||||
if (packet.getAbility() == Ability.FLYING) {
|
||||
boolean isFlying = packet.isBoolValue();
|
||||
if (!isFlying && session.getGameMode() == GameMode.SPECTATOR) {
|
||||
// We should always be flying in spectator mode
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
} else if (isFlying && session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
|
||||
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
|
||||
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
session.setFlying(isFlying);
|
||||
ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(isFlying);
|
||||
session.sendDownstreamGamePacket(abilitiesPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.level.physics.CollisionResult;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.BlockTag;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
@@ -93,7 +92,7 @@ final class BedrockMovePlayer {
|
||||
}
|
||||
|
||||
// Due to how ladder works on Bedrock, we won't get climbing velocity from tick end unless if you're colliding horizontally. So we account for it ourselves.
|
||||
boolean onClimbableBlock = session.getTagCache().is(BlockTag.CLIMBABLE, session.getGeyser().getWorldManager().blockAt(session, entity.getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0).toInt()).block());
|
||||
boolean onClimbableBlock = entity.isOnClimbableBlock();
|
||||
if (onClimbableBlock && packet.getInputData().contains(PlayerAuthInputData.JUMPING)) {
|
||||
entity.setLastTickEndVelocity(Vector3f.from(entity.getLastTickEndVelocity().getX(), 0.2F, entity.getLastTickEndVelocity().getZ()));
|
||||
}
|
||||
@@ -102,7 +101,8 @@ final class BedrockMovePlayer {
|
||||
boolean isOnGround;
|
||||
if (hasVehicle) {
|
||||
// VERTICAL_COLLISION is not accurate while in a vehicle (as of 1.21.62)
|
||||
isOnGround = Math.abs(entity.getLastTickEndVelocity().getY()) < 0.1;
|
||||
// If the player is riding a vehicle or is in spectator mode, onGround is always set to false for the player
|
||||
isOnGround = false;
|
||||
} else {
|
||||
isOnGround = packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && entity.getLastTickEndVelocity().getY() < 0;
|
||||
}
|
||||
|
||||
@@ -28,17 +28,15 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input;
|
||||
import org.cloudburstmc.math.GenericMath;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.InputMode;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.ItemUseTransaction;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.BoatEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
@@ -48,6 +46,7 @@ import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
@@ -66,6 +65,7 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.Serv
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Translator(packet = PlayerAuthInputPacket.class)
|
||||
@@ -78,33 +78,44 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
boolean wasJumping = session.getInputCache().wasJumping();
|
||||
session.getInputCache().processInputs(entity, packet);
|
||||
|
||||
BedrockMovePlayer.translate(session, packet);
|
||||
|
||||
processVehicleInput(session, packet, wasJumping);
|
||||
ServerboundPlayerCommandPacket sprintPacket = null;
|
||||
|
||||
Set<PlayerAuthInputData> inputData = packet.getInputData();
|
||||
// These inputs are sent in order, so if e.g. START_GLIDING and STOP_GLIDING are both present,
|
||||
// it's important to make sure we send the last known status instead of both to the Java server.
|
||||
Set<PlayerAuthInputData> leftOverInputData = new HashSet<>(packet.getInputData());
|
||||
for (PlayerAuthInputData input : inputData) {
|
||||
leftOverInputData.remove(input);
|
||||
switch (input) {
|
||||
case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction());
|
||||
case PERFORM_BLOCK_ACTIONS -> BedrockBlockActions.translate(session, packet.getPlayerActions());
|
||||
case START_SPRINTING -> {
|
||||
if (!entity.getFlag(EntityFlag.SWIMMING)) {
|
||||
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING);
|
||||
session.sendDownstreamGamePacket(startSprintPacket);
|
||||
session.setSprinting(true);
|
||||
}
|
||||
}
|
||||
case STOP_SPRINTING -> {
|
||||
if (!entity.getFlag(EntityFlag.SWIMMING)) {
|
||||
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING);
|
||||
session.sendDownstreamGamePacket(stopSprintPacket);
|
||||
}
|
||||
session.setSprinting(false);
|
||||
}
|
||||
case START_SWIMMING -> session.setSwimming(true);
|
||||
case STOP_SWIMMING -> session.setSwimming(false);
|
||||
case START_CRAWLING -> session.setCrawling(true);
|
||||
case STOP_CRAWLING -> session.setCrawling(false);
|
||||
case START_SPRINTING -> {
|
||||
if (!leftOverInputData.contains(PlayerAuthInputData.STOP_SPRINTING)) {
|
||||
// Check if the player is standing on but not surrounded by water; don't allow sprinting in that case
|
||||
// resolves <https://github.com/GeyserMC/Geyser/issues/1705>
|
||||
if (!GameProtocol.is1_21_80orHigher(session) && session.getCollisionManager().isPlayerTouchingWater() && !session.getCollisionManager().isPlayerInWater()) {
|
||||
// Update movement speed attribute to prevent sprinting on water. This is fixed in 1.21.80+ natively.
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
} else {
|
||||
sprintPacket = new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.START_SPRINTING);
|
||||
session.setSprinting(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
case STOP_SPRINTING -> {
|
||||
// Don't send sprinting update when we weren't sprinting
|
||||
if (!leftOverInputData.contains(PlayerAuthInputData.START_SPRINTING) && session.isSprinting()) {
|
||||
sprintPacket = new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.STOP_SPRINTING);
|
||||
session.setSprinting(false);
|
||||
}
|
||||
}
|
||||
case START_FLYING -> { // Since 1.20.30
|
||||
if (session.isCanFly()) {
|
||||
if (session.getGameMode() == GameMode.SPECTATOR) {
|
||||
@@ -123,16 +134,9 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
session.setFlying(true);
|
||||
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true));
|
||||
} else {
|
||||
// update whether we can fly
|
||||
// Stop flying & remind the client about not trying to fly :)
|
||||
session.setFlying(false);
|
||||
session.sendAdventureSettings();
|
||||
// stop flying
|
||||
PlayerActionPacket stopFlyingPacket = new PlayerActionPacket();
|
||||
stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING);
|
||||
stopFlyingPacket.setBlockPosition(Vector3i.ZERO);
|
||||
stopFlyingPacket.setResultPosition(Vector3i.ZERO);
|
||||
stopFlyingPacket.setFace(0);
|
||||
session.sendUpstreamPacket(stopFlyingPacket);
|
||||
}
|
||||
}
|
||||
case STOP_FLYING -> {
|
||||
@@ -140,12 +144,37 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
|
||||
}
|
||||
case START_GLIDING -> {
|
||||
// Otherwise gliding will not work in creative
|
||||
ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket(false);
|
||||
session.sendDownstreamGamePacket(playerAbilitiesPacket);
|
||||
sendPlayerGlideToggle(session, entity);
|
||||
// Bedrock can send both start_glide and stop_glide in the same packet.
|
||||
// We only want to start gliding if the client has not stopped gliding in the same tick.
|
||||
// last replicated on 1.21.70 by "walking" and jumping while in water
|
||||
if (!leftOverInputData.contains(PlayerAuthInputData.STOP_GLIDING)) {
|
||||
if (entity.canStartGliding()) {
|
||||
// On Java you can't start gliding while flying
|
||||
if (session.isFlying()) {
|
||||
session.setFlying(false);
|
||||
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
|
||||
}
|
||||
session.setGliding(true);
|
||||
session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING));
|
||||
} else {
|
||||
entity.forceFlagUpdate();
|
||||
session.setGliding(false);
|
||||
// return to flying if we can't start gliding
|
||||
if (session.isFlying()) {
|
||||
session.sendAdventureSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case START_SPIN_ATTACK -> session.setSpinAttack(true);
|
||||
case STOP_SPIN_ATTACK -> session.setSpinAttack(false);
|
||||
case STOP_GLIDING -> {
|
||||
// Java doesn't allow elytra gliding to stop mid-air.
|
||||
boolean shouldBeGliding = entity.isGliding() && entity.canStartGliding();
|
||||
// Always update; Bedrock can get real weird if the gliding state is mismatching
|
||||
entity.forceFlagUpdate();
|
||||
session.setGliding(shouldBeGliding);
|
||||
}
|
||||
case STOP_GLIDING -> sendPlayerGlideToggle(session, entity);
|
||||
case MISSED_SWING -> {
|
||||
session.setLastAirHitTick(session.getTicks());
|
||||
|
||||
@@ -168,6 +197,16 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
}
|
||||
}
|
||||
|
||||
// Vehicle input is send before player movement
|
||||
processVehicleInput(session, packet, wasJumping);
|
||||
|
||||
// Java edition sends sprinting after vehicle input, but before player movement
|
||||
if (sprintPacket != null) {
|
||||
session.sendDownstreamGamePacket(sprintPacket);
|
||||
}
|
||||
|
||||
BedrockMovePlayer.translate(session, packet);
|
||||
|
||||
// Only set steering values when the vehicle is a boat and when the client is actually in it
|
||||
if (entity.getVehicle() instanceof BoatEntity && inputData.contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
|
||||
boolean up = inputData.contains(PlayerAuthInputData.UP);
|
||||
@@ -178,11 +217,6 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendPlayerGlideToggle(GeyserSession session, Entity entity) {
|
||||
ServerboundPlayerCommandPacket glidePacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING);
|
||||
session.sendDownstreamGamePacket(glidePacket);
|
||||
}
|
||||
|
||||
private static void processItemUseTransaction(GeyserSession session, ItemUseTransaction transaction) {
|
||||
if (transaction.getActionType() == 2) {
|
||||
int blockState = session.getGameMode() == GameMode.CREATIVE ?
|
||||
@@ -251,15 +285,8 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) {
|
||||
sendMovement = !(vehicle instanceof ClientVehicle);
|
||||
} else if (vehicle instanceof BoatEntity) {
|
||||
if (vehicle.getPassengers().size() == 1) {
|
||||
// The player is the only rider
|
||||
sendMovement = true;
|
||||
} else {
|
||||
// Check if the player is the front rider
|
||||
if (session.getPlayerEntity().isRidingInFront()) {
|
||||
sendMovement = true;
|
||||
}
|
||||
}
|
||||
// The player is either the only or the front rider.
|
||||
sendMovement = vehicle.getPassengers().size() == 1 || session.getPlayerEntity().isRidingInFront();
|
||||
}
|
||||
|
||||
if (vehicle instanceof AbstractHorseEntity && !vehicle.getFlag(EntityFlag.HAS_DASH_COOLDOWN)) {
|
||||
|
||||
@@ -49,8 +49,8 @@ public class JavaSetEquipmentTranslator extends PacketTranslator<ClientboundSetE
|
||||
return;
|
||||
|
||||
if (!(entity instanceof LivingEntity livingEntity)) {
|
||||
session.getGeyser().getLogger().debug("Attempted to add armor to a non-living entity type (" +
|
||||
entity.getDefinition().entityType().name() + ").");
|
||||
session.getGeyser().getLogger().debug("Attempted to add armor to a non-living entity (" +
|
||||
entity.getDefinition().identifier() + ").");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ protocol-common = "3.0.0.Beta6-20250506.012145-17"
|
||||
protocol-codec = "3.0.0.Beta6-20250506.012145-17"
|
||||
raknet = "1.0.0.CR3-20250218.160705-18"
|
||||
minecraftauth = "4.1.1"
|
||||
mcprotocollib = "1.21.5-20250410.194555-25"
|
||||
mcprotocollib = "1.21.5-20250509.144049-29"
|
||||
adventure = "4.14.0"
|
||||
adventure-platform = "4.3.0"
|
||||
junit = "5.9.2"
|
||||
|
||||
Reference in New Issue
Block a user