mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Various improvements to movement, teleport, collisions (#5703)
* Initial work. * Fixed keep velocity desync. * More work. * More work. * Fix this comment. * Little oopsie. * Save player motion when updating rotation. * Implement ROTATE_DELTA. * More work. * Fixed void floor properly. * Always set isOnGround to false if near the void floor. * Fixed collision correction. * Also use recalculate position method for this one. * Make no clip void conditional. * Update core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java Co-authored-by: chris <github@onechris.mozmail.com> * Some changes. * Fix: Collision check, there's more than one bamboo/dripstone block state, and minor touchups * Use Math.toRadians. * Oops, make this compile. * Specify the teleportation cause. * Only specify the teleport cause if mode is teleport. --------- Co-authored-by: chris <github@onechris.mozmail.com>
This commit is contained in:
@@ -204,16 +204,15 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
movePlayerPacket.setPosition(this.position);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setOnGround(isOnGround);
|
||||
movePlayerPacket.setMode(teleported ? MovePlayerPacket.Mode.TELEPORT : MovePlayerPacket.Mode.NORMAL);
|
||||
|
||||
if (teleported) {
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN);
|
||||
movePlayerPacket.setMode(this instanceof SessionPlayerEntity || teleported ? MovePlayerPacket.Mode.TELEPORT : MovePlayerPacket.Mode.NORMAL);
|
||||
if (movePlayerPacket.getMode() == MovePlayerPacket.Mode.TELEPORT) {
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
|
||||
if (teleported) {
|
||||
// As of 1.19.0, head yaw seems to be ignored during teleports.
|
||||
if (teleported && !(this instanceof SessionPlayerEntity)) {
|
||||
// As of 1.19.0, head yaw seems to be ignored during teleports, also don't do this for session player.
|
||||
updateHeadLookRotation(headYaw);
|
||||
}
|
||||
|
||||
@@ -239,7 +238,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
movePlayerPacket.setPosition(position);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setOnGround(isOnGround);
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
|
||||
movePlayerPacket.setMode(this instanceof SessionPlayerEntity ? MovePlayerPacket.Mode.TELEPORT : MovePlayerPacket.Mode.NORMAL);
|
||||
// If the player is moved while sleeping, we have to adjust their y, so it appears
|
||||
// correctly on Bedrock. This fixes GSit's lay.
|
||||
if (getFlag(EntityFlag.SLEEPING)) {
|
||||
@@ -247,9 +246,13 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
// Force the player movement by using a teleport
|
||||
movePlayerPacket.setPosition(Vector3f.from(position.getX(), position.getY() - definition.offset() + 0.2f, position.getZ()));
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
if (movePlayerPacket.getMode() == MovePlayerPacket.Mode.TELEPORT) {
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
if (leftParrot != null) {
|
||||
leftParrot.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, true);
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
@@ -43,7 +44,6 @@ import org.geysermc.geyser.entity.type.BoatEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
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;
|
||||
@@ -117,15 +117,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
@Getter @Setter
|
||||
private Vector2f bedrockInteractRotation = Vector2f.ZERO;
|
||||
|
||||
/**
|
||||
* Determines if our position is currently out-of-sync with the Java server
|
||||
* due to our workaround for the void floor
|
||||
* <p>
|
||||
* Must be reset when dying, switching worlds, or being teleported out of the void
|
||||
*/
|
||||
@Getter @Setter
|
||||
private boolean voidPositionDesynched;
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
@@ -152,29 +143,18 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
if (valid) { // Don't update during session init
|
||||
session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||
|
||||
if (session.isNoClip() && position.getY() >= session.getBedrockDimension().minY() - 5) {
|
||||
session.setNoClip(false);
|
||||
}
|
||||
}
|
||||
this.position = position.add(0, definition.offset(), 0);
|
||||
}
|
||||
@@ -200,6 +180,12 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
|
||||
// We're just setting rotation, player shouldn't lose motion, send motion packet to account for that.
|
||||
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
|
||||
entityMotionPacket.setRuntimeEntityId(geyserId);
|
||||
entityMotionPacket.setMotion(motion);
|
||||
session.sendUpstreamPacket(entityMotionPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,6 +197,11 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
*/
|
||||
public void setPositionManual(Vector3f position) {
|
||||
this.position = position;
|
||||
|
||||
// Player is "above" the void so they're not supposed to no clip.
|
||||
if (session.isNoClip() && position.getY() - EntityDefinitions.PLAYER.offset() >= session.getBedrockDimension().minY() - 5) {
|
||||
session.setNoClip(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,9 +351,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
} else {
|
||||
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
|
||||
}
|
||||
|
||||
// We're either respawning or switching worlds, either way, we are no longer desynched
|
||||
this.setVoidPositionDesynched(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -449,50 +437,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
super.setVehicle(entity);
|
||||
}
|
||||
|
||||
private boolean isBelowVoidFloor() {
|
||||
return position.getY() < voidFloorPosition();
|
||||
}
|
||||
|
||||
public int voidFloorPosition() {
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getBedrockDimension();
|
||||
return bedrockDimension.minY() - 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles teleporting the player below or above the Bedrock void floor.
|
||||
* The Java server should never see this desync as we adjust the position that we send to it
|
||||
*
|
||||
* @param up in which direction to teleport - true to resync our position, or false to be
|
||||
* teleported below the void floor.
|
||||
*/
|
||||
public void teleportVoidFloorFix(boolean up) {
|
||||
// Safety to avoid double teleports
|
||||
if ((voidPositionDesynched && !up) || (!voidPositionDesynched && up)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
Vector3f newPosition = this.getPosition();
|
||||
if (up) {
|
||||
newPosition = newPosition.up(4f);
|
||||
voidPositionDesynched = false;
|
||||
} else {
|
||||
newPosition = newPosition.down(4f);
|
||||
voidPositionDesynched = true;
|
||||
}
|
||||
|
||||
this.setPositionManual(newPosition);
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(newPosition);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacketImmediately(movePlayerPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to calculate player jumping velocity for ground status calculation.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ 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.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateClientInputLocksPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
@@ -166,7 +166,7 @@ public class CollisionManager {
|
||||
}
|
||||
// We need to parse the float as a string since casting a float to a double causes us to
|
||||
// lose precision and thus, causes players to get stuck when walking near walls
|
||||
double javaY = bedrockPosition.getY() - EntityDefinitions.PLAYER.offset();
|
||||
double javaY = Double.parseDouble(Float.toString(bedrockPosition.getY() - EntityDefinitions.PLAYER.offset()));
|
||||
|
||||
Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY,
|
||||
Double.parseDouble(Float.toString(bedrockPosition.getZ())));
|
||||
@@ -197,18 +197,19 @@ public class CollisionManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
position = playerBoundingBox.getBottomCenter();
|
||||
|
||||
boolean newOnGround = adjustedMovement.getY() != movement.getY() && movement.getY() < 0 || onGround;
|
||||
// Send corrected position to Bedrock if they differ by too much to prevent de-syncs
|
||||
if (onGround != newOnGround || movement.distanceSquared(adjustedMovement) > INCORRECT_MOVEMENT_THRESHOLD) {
|
||||
if (onGround != newOnGround || position.distanceSquared(playerBoundingBox.getBottomCenter()) > INCORRECT_MOVEMENT_THRESHOLD) {
|
||||
PlayerEntity playerEntity = session.getPlayerEntity();
|
||||
// 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(), onGround, true);
|
||||
recalculatePosition();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
position = playerBoundingBox.getBottomCenter();
|
||||
|
||||
if (!newOnGround) {
|
||||
// Trim the position to prevent rounding errors that make Java think we are clipping into a block
|
||||
position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ());
|
||||
@@ -217,18 +218,14 @@ public class CollisionManager {
|
||||
return new CollisionResult(position, TriState.byBoolean(onGround));
|
||||
}
|
||||
|
||||
// TODO: This makes the player look upwards for some reason, rotation values must be wrong
|
||||
public void recalculatePosition() {
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
// Gravity might need to be reset...
|
||||
entity.updateBedrockMetadata(); // TODO may not be necessary
|
||||
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
// This does the job and won't interrupt velocity + rotation.
|
||||
UpdateClientInputLocksPacket inputLocksPacket = new UpdateClientInputLocksPacket();
|
||||
inputLocksPacket.setLockComponentData(0); // Don't actually lock anything.
|
||||
inputLocksPacket.setServerPosition(entity.getPosition());
|
||||
session.sendUpstreamPacket(inputLocksPacket);
|
||||
}
|
||||
|
||||
public BlockPositionIterator collidableBlocksIterator(BoundingBox box) {
|
||||
@@ -280,7 +277,15 @@ public class CollisionManager {
|
||||
|
||||
// Main correction code
|
||||
for (iter.reset(); iter.hasNext(); iter.next()) {
|
||||
BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]);
|
||||
final int blockId = blocks[iter.getIteration()];
|
||||
|
||||
// These block have different offset between BE and JE so we ignore them because if we "correct" the position
|
||||
// it will lead to complication and more inaccurate movement.
|
||||
if (session.getBlockMappings().getCollisionIgnoredBlocks().contains(blockId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockCollision blockCollision = BlockUtils.getCollision(blockId);
|
||||
if (blockCollision != null) {
|
||||
if (!blockCollision.correctPosition(session, iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox)) {
|
||||
return false;
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
@@ -243,6 +244,7 @@ public final class BlockRegistryPopulator {
|
||||
.toList();
|
||||
Map<Block, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
|
||||
IntArrayList collisionIgnoredBlocks = new IntArrayList();
|
||||
|
||||
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
|
||||
Map<String, BlockDefinition> structureBlockDefinitions = new Object2ObjectOpenHashMap<>();
|
||||
@@ -308,6 +310,10 @@ public final class BlockRegistryPopulator {
|
||||
netherPortalBlockDefinition = bedrockDefinition;
|
||||
}
|
||||
|
||||
if (block == Blocks.BAMBOO || block == Blocks.POINTED_DRIPSTONE) {
|
||||
collisionIgnoredBlocks.add(javaRuntimeId);
|
||||
}
|
||||
|
||||
boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|
||||
|| block == Blocks.BUBBLE_COLUMN || block == Blocks.KELP || block == Blocks.KELP_PLANT
|
||||
|| block == Blocks.SEAGRASS || block == Blocks.TALL_SEAGRASS;
|
||||
@@ -326,6 +332,8 @@ public final class BlockRegistryPopulator {
|
||||
javaToBedrockBlocks[javaRuntimeId] = bedrockDefinition;
|
||||
}
|
||||
|
||||
builder.collisionIgnoredBlocks(collisionIgnoredBlocks);
|
||||
|
||||
if (commandBlockDefinition == null) {
|
||||
throw new AssertionError("Unable to find command block in palette");
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package org.geysermc.geyser.registry.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
@@ -66,6 +67,8 @@ public class BlockMappings implements DefinitionRegistry<BlockDefinition> {
|
||||
BlockDefinition mobSpawnerBlock;
|
||||
BlockDefinition netherPortalBlock;
|
||||
|
||||
IntArrayList collisionIgnoredBlocks;
|
||||
|
||||
Map<NbtMap, BlockDefinition> itemFrames;
|
||||
Map<Block, NbtMap> flowerPotBlocks;
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetCommandsEnabledPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SyncEntityPropertyPacket;
|
||||
@@ -632,6 +633,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
*/
|
||||
private boolean flying = false;
|
||||
|
||||
/**
|
||||
* If the current player should be able to noclip through blocks, this is used for void floor workaround and not spectator.
|
||||
*/
|
||||
private boolean noClip = false;
|
||||
|
||||
@Setter
|
||||
private boolean instabuild = false;
|
||||
|
||||
@@ -1380,6 +1386,15 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
playerEntity.updateBedrockMetadata();
|
||||
}
|
||||
|
||||
public void setNoClip(boolean noClip) {
|
||||
if (this.noClip == noClip) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.noClip = noClip;
|
||||
this.sendAdventureSettings();
|
||||
}
|
||||
|
||||
public void setFlying(boolean flying) {
|
||||
this.flying = flying;
|
||||
|
||||
@@ -1835,7 +1850,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
return itemNetId.getAndIncrement();
|
||||
}
|
||||
|
||||
public void confirmTeleport(Vector3d position) {
|
||||
public void confirmTeleport(Vector3f position) {
|
||||
if (unconfirmedTeleport == null) {
|
||||
return;
|
||||
}
|
||||
@@ -1850,8 +1865,15 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
if (unconfirmedTeleport.shouldResend()) {
|
||||
unconfirmedTeleport.resetUnconfirmedFor();
|
||||
geyser.getLogger().debug("Resending teleport " + unconfirmedTeleport.getTeleportConfirmId());
|
||||
getPlayerEntity().moveAbsolute(Vector3f.from(unconfirmedTeleport.getX(), unconfirmedTeleport.getY(), unconfirmedTeleport.getZ()),
|
||||
getPlayerEntity().moveAbsolute(unconfirmedTeleport.getPosition(),
|
||||
unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true);
|
||||
|
||||
if (unconfirmedTeleport.getTeleportType() == TeleportCache.TeleportType.KEEP_VELOCITY) {
|
||||
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
|
||||
entityMotionPacket.setRuntimeEntityId(playerEntity.getGeyserId());
|
||||
entityMotionPacket.setMotion(unconfirmedTeleport.getVelocity());
|
||||
this.sendUpstreamPacket(entityMotionPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2035,6 +2057,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
abilities.add(Ability.INSTABUILD);
|
||||
}
|
||||
|
||||
if (noClip && !spectator) {
|
||||
abilities.add(Ability.NO_CLIP);
|
||||
}
|
||||
|
||||
if (commandPermission == CommandPermission.GAME_DIRECTORS) {
|
||||
// Fixes a bug? since 1.19.11 where the player can change their gamemode in Bedrock settings and
|
||||
// a packet is not sent to the server.
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
|
||||
/**
|
||||
* Represents a teleport ID and corresponding coordinates that need to be confirmed. <br>
|
||||
@@ -42,24 +42,37 @@ import lombok.RequiredArgsConstructor;
|
||||
@Data
|
||||
public class TeleportCache {
|
||||
|
||||
private static final double ERROR_X_AND_Z = 0.1;
|
||||
private static final double ERROR_Y = 0.1;
|
||||
private static final float ERROR_X_AND_Z = 0.1f;
|
||||
private static final float ERROR_Y = 0.1f;
|
||||
|
||||
/**
|
||||
* How many move packets the teleport can be unconfirmed for before it gets resent to the client
|
||||
*/
|
||||
private static final int RESEND_THRESHOLD = 20; // Make it one full second with auth input
|
||||
|
||||
private final double x, y, z;
|
||||
public TeleportCache(Vector3f position, float pitch, float yaw, int teleportConfirmId) {
|
||||
this.position = position;
|
||||
this.velocity = Vector3f.ZERO;
|
||||
this.pitch = pitch;
|
||||
this.yaw = yaw;
|
||||
this.teleportConfirmId = teleportConfirmId;
|
||||
this.teleportType = TeleportType.NORMAL;
|
||||
}
|
||||
|
||||
private final Vector3f position;
|
||||
private final Vector3f velocity;
|
||||
private final float pitch, yaw;
|
||||
private final int teleportConfirmId;
|
||||
private final TeleportType teleportType;
|
||||
|
||||
private int unconfirmedFor = 0;
|
||||
|
||||
public boolean canConfirm(Vector3d position) {
|
||||
return (Math.abs(this.x - position.getX()) < ERROR_X_AND_Z &&
|
||||
Math.abs(this.y - position.getY()) < ERROR_Y &&
|
||||
Math.abs(this.z - position.getZ()) < ERROR_X_AND_Z);
|
||||
public boolean canConfirm(Vector3f position) {
|
||||
final float distanceX = Math.abs(this.position.getX() - position.getX());
|
||||
final float distanceY = Math.abs(this.position.getY() - position.getY());
|
||||
final float distanceZ = Math.abs(this.position.getZ() - position.getZ());
|
||||
|
||||
return distanceX < ERROR_X_AND_Z && distanceY < ERROR_Y && distanceZ < ERROR_X_AND_Z;
|
||||
}
|
||||
|
||||
public void incrementUnconfirmedFor() {
|
||||
@@ -73,4 +86,9 @@ public class TeleportCache {
|
||||
public boolean shouldResend() {
|
||||
return unconfirmedFor >= RESEND_THRESHOLD;
|
||||
}
|
||||
|
||||
public enum TeleportType {
|
||||
NORMAL,
|
||||
KEEP_VELOCITY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,13 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.BoatEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.level.physics.CollisionResult;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
@@ -55,11 +59,12 @@ final class BedrockMovePlayer {
|
||||
|
||||
// Ignore movement packets until Bedrock's position matches the teleported position
|
||||
if (session.getUnconfirmedTeleport() != null) {
|
||||
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0));
|
||||
session.confirmTeleport(packet.getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean actualPositionChanged = !entity.getPosition().equals(packet.getPosition());
|
||||
// This is vanilla behaviour, LocalPlayer#sendPosition 1.21.8.
|
||||
boolean actualPositionChanged = entity.getPosition().distanceSquared(packet.getPosition()) > 4e-8;
|
||||
|
||||
if (actualPositionChanged) {
|
||||
// Send book update before the player moves
|
||||
@@ -96,14 +101,47 @@ final class BedrockMovePlayer {
|
||||
|
||||
// Client is telling us it wants to move down, but something is blocking it from doing so.
|
||||
boolean isOnGround;
|
||||
if (hasVehicle) {
|
||||
if (hasVehicle || session.isNoClip()) {
|
||||
// VERTICAL_COLLISION is not accurate while in a vehicle (as of 1.21.62)
|
||||
// If the player is riding a vehicle or is in spectator mode, onGround is always set to false for the player
|
||||
// Also do this if player have no clip ability since they shouldn't be able to collide with anything.
|
||||
isOnGround = false;
|
||||
} else {
|
||||
isOnGround = packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && entity.getLastTickEndVelocity().getY() < 0;
|
||||
}
|
||||
|
||||
// Resolve https://github.com/GeyserMC/Geyser/issues/3521, no void floor on java so player not supposed to collide with anything.
|
||||
// Therefore, we're fixing this by allowing player to no clip to clip through the floor, not only this fixed the issue but
|
||||
// player y velocity should match java perfectly, much better than teleport player right down :)
|
||||
// Shouldn't mess with anything because beyond this point there is nothing to collide and not even entities since they're prob dead.
|
||||
if (packet.getPosition().getY() - EntityDefinitions.PLAYER.offset() < session.getBedrockDimension().minY() - 5) {
|
||||
// Ensuring that we still can collide with collidable entity that are also in the void (eg: boat, shulker)
|
||||
boolean possibleOnGround = false;
|
||||
|
||||
BoundingBox boundingBox = session.getCollisionManager().getPlayerBoundingBox().clone();
|
||||
|
||||
// Extend down by y velocity subtract by 2 so that we are a "little" ahead and can send no clip in time before player hit the entity.
|
||||
boundingBox.extend(0, packet.getDelta().getY() - 2, 0);
|
||||
|
||||
for (Entity other : session.getEntityCache().getEntities().values()) {
|
||||
if (!other.getFlag(EntityFlag.COLLIDABLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final BoundingBox entityBoundingBox = new BoundingBox(0, 0, 0, other.getBoundingBoxWidth(), other.getBoundingBoxHeight(), other.getBoundingBoxWidth());
|
||||
|
||||
// Also offset the position down for boat as their position is offset.
|
||||
entityBoundingBox.translate(other.getPosition().down(other instanceof BoatEntity ? entity.getDefinition().offset() : 0).toDouble());
|
||||
|
||||
if (entityBoundingBox.checkIntersection(boundingBox)) {
|
||||
possibleOnGround = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
session.setNoClip(!possibleOnGround);
|
||||
}
|
||||
|
||||
entity.setLastTickEndVelocity(packet.getDelta());
|
||||
entity.setMotion(packet.getDelta());
|
||||
|
||||
@@ -135,35 +173,6 @@ final class BedrockMovePlayer {
|
||||
CollisionResult result = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), isOnGround, packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT));
|
||||
if (result != null) { // A null return value cancels the packet
|
||||
Vector3d position = result.correctedMovement();
|
||||
boolean isBelowVoid = entity.isVoidPositionDesynched();
|
||||
|
||||
boolean teleportThroughVoidFloor, mustResyncPosition;
|
||||
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||
} else {
|
||||
teleportThroughVoidFloor = false;
|
||||
}
|
||||
|
||||
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
isOnGround = false;
|
||||
}
|
||||
|
||||
if (isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||
} else {
|
||||
mustResyncPosition = false;
|
||||
}
|
||||
|
||||
double yPosition = position.getY();
|
||||
if (entity.isVoidPositionDesynched()) { // not using the cached variable on purpose
|
||||
yPosition += 4; // We are de-synched since we had to teleport below the void floor.
|
||||
}
|
||||
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
@@ -171,7 +180,7 @@ final class BedrockMovePlayer {
|
||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||
isOnGround,
|
||||
horizontalCollision,
|
||||
position.getX(), yPosition, position.getZ(),
|
||||
position.getX(), position.getY(), position.getZ(),
|
||||
yaw, pitch
|
||||
);
|
||||
entity.setYaw(yaw);
|
||||
@@ -179,7 +188,7 @@ final class BedrockMovePlayer {
|
||||
entity.setHeadYaw(headYaw);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ServerboundMovePlayerPosPacket(isOnGround, horizontalCollision, position.getX(), yPosition, position.getZ());
|
||||
movePacket = new ServerboundMovePlayerPosPacket(isOnGround, horizontalCollision, position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
@@ -187,12 +196,6 @@ final class BedrockMovePlayer {
|
||||
// Send final movement changes
|
||||
session.sendDownstreamGamePacket(movePacket);
|
||||
|
||||
if (teleportThroughVoidFloor) {
|
||||
entity.teleportVoidFloorFix(false);
|
||||
} else if (mustResyncPosition) {
|
||||
entity.teleportVoidFloorFix(true);
|
||||
}
|
||||
|
||||
session.getInputCache().markPositionPacketSent();
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package org.geysermc.geyser.translator.protocol.java.entity;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
@@ -58,7 +59,7 @@ public class JavaSetPassengersTranslator extends PacketTranslator<ClientboundSet
|
||||
if (passenger == session.getPlayerEntity()) {
|
||||
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().down(EntityDefinitions.PLAYER.offset()).toDouble());
|
||||
session.confirmTeleport(passenger.getPosition().down(EntityDefinitions.PLAYER.offset()));
|
||||
|
||||
if (entity instanceof ClientVehicle clientVehicle) {
|
||||
clientVehicle.getVehicleComponent().onMount();
|
||||
|
||||
@@ -30,14 +30,15 @@ import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.RespawnPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.item.hashing.DataComponentHashers;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.TeleportCache;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PositionElement;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket;
|
||||
@@ -48,16 +49,27 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPlayerPositionPacket packet) {
|
||||
if (!session.isLoggedIn())
|
||||
if (!session.isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
Vector3d pos = packet.getPosition();
|
||||
final SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
Vector3d position = packet.getPosition();
|
||||
|
||||
position = position.add(
|
||||
packet.getRelatives().contains(PositionElement.X) ? entity.getPosition().getX() : 0,
|
||||
packet.getRelatives().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityDefinitions.PLAYER.offset() : 0,
|
||||
packet.getRelatives().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
|
||||
|
||||
float newPitch = MathUtils.clamp(packet.getXRot() + (packet.getRelatives().contains(PositionElement.X_ROT) ? entity.getPitch() : 0), -90, 90);
|
||||
float newYaw = packet.getYRot() + (packet.getRelatives().contains(PositionElement.Y_ROT) ? entity.getYaw() : 0);
|
||||
|
||||
final int teleportId = packet.getId();
|
||||
|
||||
acceptTeleport(session, position, newYaw, newPitch, teleportId);
|
||||
|
||||
if (!session.isSpawned()) {
|
||||
// TODO this behavior seems outdated (1.21.2).
|
||||
// The server sends an absolute teleport everytime the player is respawned
|
||||
entity.setPosition(pos.toFloat());
|
||||
entity.setPosition(position.toFloat());
|
||||
entity.setYaw(packet.getYRot());
|
||||
entity.setPitch(packet.getXRot());
|
||||
entity.setHeadYaw(packet.getYRot());
|
||||
@@ -75,21 +87,17 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);
|
||||
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
|
||||
// Fixes incorrect rotation upon login
|
||||
// Yes, even that's not respected by Bedrock. Try it out in singleplayer!
|
||||
// Log out and back in - and you're looking elsewhere :)
|
||||
entity.updateOwnRotation(entity.getYaw(), entity.getPitch(), entity.getHeadYaw());
|
||||
|
||||
session.setSpawned(true);
|
||||
// DataComponentHashers.testHashing(session); // TODO remove me
|
||||
|
||||
// Make sure the player moves away from (0, 32767, 0) before accepting movement packets
|
||||
session.setUnconfirmedTeleport(new TeleportCache(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(), packet.getXRot(), packet.getYRot(), packet.getId())); // TODO
|
||||
|
||||
acceptTeleport(session, packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(), packet.getYRot(), packet.getXRot(), packet.getId());
|
||||
session.setUnconfirmedTeleport(new TeleportCache(entity.position(), packet.getXRot(), packet.getYRot(), packet.getId()));
|
||||
|
||||
if (session.getServerRenderDistance() > 32 && !session.isEmulatePost1_13Logic()) {
|
||||
// See DimensionUtils for an explanation
|
||||
@@ -100,7 +108,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
|
||||
session.setLastChunkPosition(null);
|
||||
}
|
||||
|
||||
ChunkUtils.updateChunkPosition(session, pos.toInt());
|
||||
ChunkUtils.updateChunkPosition(session, position.toInt());
|
||||
|
||||
if (session.getGeyser().getConfig().isDebugMode()) {
|
||||
session.getGeyser().getLogger().debug("Spawned player at " + packet.getPosition());
|
||||
@@ -108,45 +116,54 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
|
||||
return;
|
||||
}
|
||||
|
||||
// If coordinates are relative, then add to the existing coordinate
|
||||
double newX = pos.getX() +
|
||||
(packet.getRelatives().contains(PositionElement.X) ? entity.getPosition().getX() : 0);
|
||||
double newY = pos.getY() +
|
||||
(packet.getRelatives().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityDefinitions.PLAYER.offset() : 0);
|
||||
double newZ = pos.getZ() +
|
||||
(packet.getRelatives().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
|
||||
|
||||
float newPitch = packet.getXRot() + (packet.getRelatives().contains(PositionElement.X_ROT) ? entity.getPitch() : 0);
|
||||
float newYaw = packet.getYRot() + (packet.getRelatives().contains(PositionElement.Y_ROT) ? entity.getYaw() : 0);
|
||||
|
||||
int id = packet.getId();
|
||||
|
||||
session.getGeyser().getLogger().debug("Teleport (" + id + ") from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
|
||||
session.getGeyser().getLogger().debug("Teleport (" + teleportId + ") from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
|
||||
|
||||
Vector3f lastPlayerPosition = entity.getPosition().down(EntityDefinitions.PLAYER.offset());
|
||||
float lastPlayerPitch = entity.getPitch();
|
||||
Vector3f teleportDestination = Vector3f.from(newX, newY, newZ);
|
||||
entity.moveAbsolute(teleportDestination, newYaw, newPitch, false, true);
|
||||
float lastPlayerYaw = entity.getYaw();
|
||||
Vector3f teleportDestination = position.toFloat();
|
||||
|
||||
session.getGeyser().getLogger().debug("to " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
|
||||
Vector3f deltaMovement = packet.getDeltaMovement().toFloat().add(
|
||||
packet.getRelatives().contains(PositionElement.DELTA_X) ? entity.getMotion().getX() : 0,
|
||||
packet.getRelatives().contains(PositionElement.DELTA_Y) ? entity.getMotion().getY() : 0,
|
||||
packet.getRelatives().contains(PositionElement.DELTA_Z) ? entity.getMotion().getZ() : 0
|
||||
);
|
||||
|
||||
// Bedrock ignores teleports that are extremely close to the player's original position and orientation,
|
||||
// so check if we need to cache the teleport
|
||||
if (lastPlayerPosition.distanceSquared(teleportDestination) < 0.001 && Math.abs(newPitch - lastPlayerPitch) < 5) {
|
||||
session.setUnconfirmedTeleport(null);
|
||||
} else {
|
||||
session.setUnconfirmedTeleport(new TeleportCache(newX, newY, newZ, newPitch, newYaw, id));
|
||||
if (packet.getRelatives().contains(PositionElement.ROTATE_DELTA)) {
|
||||
deltaMovement = MathUtils.xYRot(deltaMovement, (float) Math.toRadians(lastPlayerPitch - newPitch), (float) Math.toRadians(lastPlayerYaw - newYaw));
|
||||
}
|
||||
|
||||
acceptTeleport(session, newX, newY, newZ, newYaw, newPitch, id);
|
||||
entity.moveAbsolute(teleportDestination, newYaw, newPitch, false, true);
|
||||
|
||||
TeleportCache.TeleportType type = TeleportCache.TeleportType.NORMAL;
|
||||
if (deltaMovement.distanceSquared(Vector3f.ZERO) > 1.0E-8F) {
|
||||
entity.setMotion(deltaMovement);
|
||||
|
||||
// Our motion got reset by the teleport but the deltaMovement is not 0 so send a motion packet to fix that.
|
||||
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
|
||||
entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
entityMotionPacket.setMotion(entity.getMotion());
|
||||
session.sendUpstreamPacket(entityMotionPacket);
|
||||
|
||||
type = TeleportCache.TeleportType.KEEP_VELOCITY;
|
||||
}
|
||||
|
||||
// Bedrock ignores teleports that are extremely close to the player's original position and orientation, so check if we need to cache the teleport
|
||||
if (lastPlayerPosition.distanceSquared(teleportDestination) < 0.001 && Math.abs(newPitch - lastPlayerPitch) < 5 && Math.abs(newYaw - lastPlayerYaw) < 5) {
|
||||
session.setUnconfirmedTeleport(null);
|
||||
} else {
|
||||
session.setUnconfirmedTeleport(new TeleportCache(teleportDestination, deltaMovement, newPitch, newYaw, teleportId, type));
|
||||
}
|
||||
|
||||
session.getGeyser().getLogger().debug("to " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
|
||||
}
|
||||
|
||||
private void acceptTeleport(GeyserSession session, double x, double y, double z, float yaw, float pitch, int id) {
|
||||
private void acceptTeleport(GeyserSession session, Vector3d position, float yaw, float pitch, int id) {
|
||||
// Confirm the teleport when we receive it to match Java edition
|
||||
ServerboundAcceptTeleportationPacket teleportConfirmPacket = new ServerboundAcceptTeleportationPacket(id);
|
||||
session.sendDownstreamGamePacket(teleportConfirmPacket);
|
||||
// Servers (especially ones like Hypixel) expect exact coordinates given back to them.
|
||||
ServerboundMovePlayerPosRotPacket positionPacket = new ServerboundMovePlayerPosRotPacket(false, false, x, y, z, yaw, pitch);
|
||||
ServerboundMovePlayerPosRotPacket positionPacket = new ServerboundMovePlayerPosRotPacket(false, false, position.getX(), position.getY(), position.getZ(), yaw, pitch);
|
||||
session.sendDownstreamGamePacket(positionPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,28 @@
|
||||
|
||||
package org.geysermc.geyser.util;
|
||||
|
||||
import org.cloudburstmc.math.TrigMath;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
|
||||
public class MathUtils {
|
||||
public static final double SQRT_OF_TWO = Math.sqrt(2);
|
||||
|
||||
public static Vector3f xYRot(Vector3f velocity, float pitch, float yaw) {
|
||||
float pitchCos = TrigMath.cos(pitch);
|
||||
float pitchSin = TrigMath.sin(pitch);
|
||||
float yawCos = TrigMath.cos(yaw);
|
||||
float yawSin = TrigMath.sin(yaw);
|
||||
|
||||
float e = velocity.getY() * pitchCos + velocity.getZ() * pitchSin;
|
||||
float i = velocity.getZ() * pitchCos - velocity.getY() * pitchSin;
|
||||
velocity = Vector3f.from(velocity.getX(), e, i);
|
||||
|
||||
float d1 = velocity.getX() * yawCos + velocity.getZ() * yawSin;
|
||||
float i1 = velocity.getZ() * yawCos - velocity.getX() * yawSin;
|
||||
|
||||
return Vector3f.from(d1, e, i1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given float degrees to be between -180.0 and 180.0.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user