3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-19 14:30:17 +01:00

Merge remote-tracking branch 'upstream/master' into client-vehicle

Dieser Commit ist enthalten in:
AJ Ferguson 2024-06-22 02:43:20 -04:00
Commit 727604f82c
8 geänderte Dateien mit 131 neuen und 38 gelöschten Zeilen

Datei anzeigen

@ -47,6 +47,7 @@ import java.util.function.BiConsumer;
* metadata translators needed to translate the properties sent from the server. The translators are structured in such * metadata translators needed to translate the properties sent from the server. The translators are structured in such
* a way that inserting a new one (for example in version updates) is convenient. * a way that inserting a new one (for example in version updates) is convenient.
* *
* @param identifier the Bedrock identifier of this entity
* @param <T> the entity type this definition represents * @param <T> the entity type this definition represents
*/ */
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier, public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier,

Datei anzeigen

@ -27,16 +27,19 @@ package org.geysermc.geyser.entity.type.player;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.AttributeData; import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.DimensionUtils;
@ -81,6 +84,15 @@ public class SessionPlayerEntity extends PlayerEntity {
private int lastAirSupply = getMaxAir(); private int lastAirSupply = getMaxAir();
/**
* 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) { public SessionPlayerEntity(GeyserSession session) {
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null); super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
@ -99,10 +111,25 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { 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); super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset())); 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 @Override
public void setPosition(Vector3f position) { public void setPosition(Vector3f position) {
if (valid) { // Don't update during session init if (valid) { // Don't update during session init
@ -237,6 +264,9 @@ public class SessionPlayerEntity extends PlayerEntity {
} else { } else {
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false); 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 @Override
@ -299,4 +329,48 @@ public class SessionPlayerEntity extends PlayerEntity {
public void setVehicleJumpStrength(int vehicleJumpStrength) { public void setVehicleJumpStrength(int vehicleJumpStrength) {
this.vehicleJumpStrength = MathUtils.constrain(vehicleJumpStrength, 0, 100); this.vehicleJumpStrength = MathUtils.constrain(vehicleJumpStrength, 0, 100);
} }
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.getChunkCache().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);
}
} }

Datei anzeigen

@ -51,6 +51,7 @@ public class FilledMapItem extends MapItem {
switch (mapColor) { switch (mapColor) {
case 3830373 -> builder.damage(3); // Ocean Monument case 3830373 -> builder.damage(3); // Ocean Monument
case 5393476 -> builder.damage(4); // Woodland explorer case 5393476 -> builder.damage(4); // Woodland explorer
case 12741452 -> builder.damage(14); // Trial Chamber
} }
} }
} }

Datei anzeigen

@ -63,7 +63,8 @@ public enum BedrockMapIcon {
ICON_SNOWY_VILLAGE(MapIconType.SNOWY_VILLAGE, 20), ICON_SNOWY_VILLAGE(MapIconType.SNOWY_VILLAGE, 20),
ICON_TAIGA_VILLAGE(MapIconType.TAIGA_VILLAGE, 21), ICON_TAIGA_VILLAGE(MapIconType.TAIGA_VILLAGE, 21),
ICON_JUNGLE_TEMPLE(MapIconType.JUNGLE_TEMPLE, 22), ICON_JUNGLE_TEMPLE(MapIconType.JUNGLE_TEMPLE, 22),
ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23); ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23),
ICON_TRIAL_CHAMBERS(MapIconType.TRIAL_CHAMBERS, 24);
private static final BedrockMapIcon[] VALUES = values(); private static final BedrockMapIcon[] VALUES = values();

Datei anzeigen

@ -418,13 +418,15 @@ public final class ItemTranslator {
if (components != null) { if (components != null) {
// ItemStack#getHoverName as of 1.20.5 // ItemStack#getHoverName as of 1.20.5
Component customName = components.get(DataComponentType.CUSTOM_NAME); Component customName = components.get(DataComponentType.CUSTOM_NAME);
if (customName == null) {
customName = components.get(DataComponentType.ITEM_NAME);
}
if (customName != null) { if (customName != null) {
// Get the translated name and prefix it with a reset char
return MessageTranslator.convertMessage(customName, session.locale()); return MessageTranslator.convertMessage(customName, session.locale());
} }
customName = components.get(DataComponentType.ITEM_NAME);
if (customName != null) {
// Get the translated name and prefix it with a reset char to prevent italics - matches Java Edition
// behavior as of 1.21
return ChatColor.RESET + ChatColor.ESCAPE + translationColor + MessageTranslator.convertMessage(customName, session.locale());
}
} }
if (mapping.hasTranslation()) { if (mapping.hasTranslation()) {

Datei anzeigen

@ -107,7 +107,7 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
bedrockNbt.put("isMovable", (byte) 1); bedrockNbt.put("isMovable", (byte) 1);
} }
static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) { private static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) {
if (spawnData == null) { if (spawnData == null) {
return; return;
} }

Datei anzeigen

@ -27,22 +27,31 @@ package org.geysermc.geyser.translator.level.block.entity;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType; import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
@BlockEntity(type = BlockEntityType.TRIAL_SPAWNER) @BlockEntity(type = BlockEntityType.TRIAL_SPAWNER)
public class TrialSpawnerBlockEntityTranslator extends BlockEntityTranslator { public class TrialSpawnerBlockEntityTranslator extends BlockEntityTranslator {
// Note that it would appear block entity updates don't include the NBT, but we do need it on chunk load.
@Override @Override
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) { public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) {
if (javaNbt == null) { if (javaNbt == null) {
return; return;
} }
// trial spawners have "spawn_data" instead of "SpawnData" NbtMap entityData = javaNbt.getCompound("spawn_data").getCompound("entity");
SpawnerBlockEntityTranslator.translateSpawnData(bedrockNbt, javaNbt.getCompound("spawn_data", null)); if (entityData.isEmpty()) {
return;
// Because trial spawners don't exist on bedrock yet }
bedrockNbt.put("id", "MobSpawner"); NbtMapBuilder spawnData = NbtMap.builder();
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityData.getString("id"));
if (definition != null) {
spawnData.putString("TypeId", definition.identifier());
}
spawnData.putInt("Weight", entityData.getInt("Size", 1)); // ??? presumably since these are the only other two extra attributes
bedrockNbt.putCompound("spawn_data", spawnData.build());
} }
} }

Datei anzeigen

@ -25,20 +25,19 @@
package org.geysermc.geyser.translator.protocol.bedrock.entity.player; package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
import org.geysermc.mcprotocollib.network.packet.Packet;
import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.network.packet.Packet;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
@Translator(packet = MovePlayerPacket.class) @Translator(packet = MovePlayerPacket.class)
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> { public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
@ -93,29 +92,42 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT); Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
if (position != null) { // A null return value cancels the packet if (position != null) { // A null return value cancels the packet
boolean onGround = packet.isOnGround(); boolean onGround = packet.isOnGround();
boolean isBelowVoid = entity.isVoidPositionDesynched();
boolean teleportThroughVoidFloor; boolean teleportThroughVoidFloor, mustResyncPosition;
// Compare positions here for void floor fix below before the player's position variable is set to the packet position // 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()) { if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
int floorY = position.getFloorY(); int floorY = position.getFloorY();
// The void floor is offset about 40 blocks below the bottom of the world int voidFloorLocation = entity.voidFloorPosition();
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
int voidFloorLocation = bedrockDimension.minY() - 40;
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation;
if (teleportThroughVoidFloor) {
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
onGround = false;
}
} else { } else {
teleportThroughVoidFloor = false; teleportThroughVoidFloor = false;
} }
if (teleportThroughVoidFloor || isBelowVoid) {
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
onGround = 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; Packet movePacket;
if (rotationChanged) { if (rotationChanged) {
// Send rotation updates as well // Send rotation updates as well
movePacket = new ServerboundMovePlayerPosRotPacket( movePacket = new ServerboundMovePlayerPosRotPacket(
onGround, onGround,
position.getX(), position.getY(), position.getZ(), position.getX(), yPosition, position.getZ(),
yaw, pitch yaw, pitch
); );
entity.setYaw(yaw); entity.setYaw(yaw);
@ -123,7 +135,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
entity.setHeadYaw(headYaw); entity.setHeadYaw(headYaw);
} else { } else {
// Rotation did not change; don't send an update with rotation // Rotation did not change; don't send an update with rotation
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), position.getY(), position.getZ()); movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), yPosition, position.getZ());
} }
entity.setPositionManual(packet.getPosition()); entity.setPositionManual(packet.getPosition());
@ -133,16 +145,9 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
session.sendDownstreamGamePacket(movePacket); session.sendDownstreamGamePacket(movePacket);
if (teleportThroughVoidFloor) { if (teleportThroughVoidFloor) {
// Work around there being a floor at the bottom of the world and teleport the player below it entity.teleportVoidFloorFix(false);
// Moving from below to above the void floor works fine } else if (mustResyncPosition) {
entity.setPosition(entity.getPosition().sub(0, 4f, 0)); entity.teleportVoidFloorFix(true);
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
movePlayerPacket.setPosition(entity.getPosition());
movePlayerPacket.setRotation(entity.getBedrockRotation());
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
session.sendUpstreamPacket(movePlayerPacket);
} }
session.getSkullCache().updateVisibleSkulls(); session.getSkullCache().updateVisibleSkulls();