Merge branch 'master' into feature/extensions

# Conflicts:
#	ap/pom.xml
#	api/base/pom.xml
#	api/geyser/pom.xml
#	api/pom.xml
#	bootstrap/bungeecord/pom.xml
#	bootstrap/pom.xml
#	bootstrap/spigot/pom.xml
#	bootstrap/sponge/pom.xml
#	bootstrap/standalone/pom.xml
#	bootstrap/velocity/pom.xml
#	common/pom.xml
#	core/pom.xml
#	core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java
#	core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java
#	core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
#	core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockFilterTextTranslator.java
#	pom.xml
This commit is contained in:
RednedEpic 2022-04-24 11:23:26 -05:00
commit 43f23674d6
34 changed files with 313 additions and 154 deletions

View File

@ -55,9 +55,9 @@ body:
required: true required: true
- type: input - type: input
attributes: attributes:
label: "Minecraft: Bedrock Edition Version" label: "Minecraft: Bedrock Edition Device/Version"
description: "What version of Minecraft: Bedrock Edition are you using? Leave empty if the bug happens before you can connect with Minecraft: Bedrock Edition." description: "What version of Minecraft: Bedrock Edition are you using, and what device(s) does the bug occur on? Leave empty if the bug happens before you can connect with Minecraft: Bedrock Edition."
placeholder: "For example: 1.16.201" placeholder: "For example: 1.16.201, Nintendo Switch"
- type: textarea - type: textarea
attributes: attributes:
label: Additional Context label: Additional Context

View File

@ -30,7 +30,7 @@ object Versions {
const val guavaVersion = "29.0-jre" const val guavaVersion = "29.0-jre"
const val nbtVersion = "2.1.0" const val nbtVersion = "2.1.0"
const val websocketVersion = "1.5.1" const val websocketVersion = "1.5.1"
const val protocolVersion = "29ecd7a" const val protocolVersion = "f32c76d"
const val raknetVersion = "1.6.28-SNAPSHOT" const val raknetVersion = "1.6.28-SNAPSHOT"
const val mcauthlibVersion = "d9d773e" const val mcauthlibVersion = "d9d773e"
const val mcprotocollibversion = "0771504" const val mcprotocollibversion = "0771504"

View File

@ -240,8 +240,21 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
public static class MetricsInfo implements IMetricsInfo { public static class MetricsInfo implements IMetricsInfo {
private boolean enabled = true; private boolean enabled = true;
@JsonDeserialize(using = MetricsIdDeserializer.class)
@JsonProperty("uuid") @JsonProperty("uuid")
private String uniqueId = UUID.randomUUID().toString(); private String uniqueId = UUID.randomUUID().toString();
private static class MetricsIdDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String uuid = p.getValueAsString();
if ("generateduuid".equals(uuid)) {
// Compensate for configs not copied from the jar
return UUID.randomUUID().toString();
}
return uuid;
}
}
} }
@JsonProperty("scoreboard-packet-threshold") @JsonProperty("scoreboard-packet-threshold")

View File

@ -70,8 +70,8 @@ public class AbstractArrowEntity extends Entity {
super.setMotion(motion); super.setMotion(motion);
double horizontalSpeed = Math.sqrt(motion.getX() * motion.getX() + motion.getZ() * motion.getZ()); double horizontalSpeed = Math.sqrt(motion.getX() * motion.getX() + motion.getZ() * motion.getZ());
this.yaw = (float) Math.toDegrees(Math.atan2(motion.getX(), motion.getZ())); setYaw((float) Math.toDegrees(Math.atan2(motion.getX(), motion.getZ())));
this.pitch = (float) Math.toDegrees(Math.atan2(motion.getY(), horizontalSpeed)); setPitch((float) Math.toDegrees(Math.atan2(motion.getY(), horizontalSpeed)));
this.headYaw = yaw; setHeadYaw(getYaw());
} }
} }

View File

@ -81,8 +81,8 @@ public class BoatEntity extends Entity {
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
// We don't include the rotation (y) as it causes the boat to appear sideways // We don't include the rotation (y) as it causes the boat to appear sideways
setPosition(position.add(0d, this.definition.offset(), 0d)); setPosition(position.add(0d, this.definition.offset(), 0d));
this.yaw = yaw + 90; setYaw(yaw + 90);
this.headYaw = yaw + 90; setHeadYaw(yaw + 90);
setOnGround(isOnGround); setOnGround(isOnGround);
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();

View File

@ -204,7 +204,7 @@ public class Entity {
} }
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) { public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) {
moveRelative(relX, relY, relZ, yaw, pitch, this.headYaw, isOnGround); moveRelative(relX, relY, relZ, yaw, pitch, getHeadYaw(), isOnGround);
} }
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) {
@ -225,7 +225,7 @@ public class Entity {
} }
public void moveAbsolute(Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) { public void moveAbsolute(Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) {
moveAbsolute(position, yaw, pitch, this.headYaw, isOnGround, teleported); moveAbsolute(position, yaw, pitch, getHeadYaw(), isOnGround, teleported);
} }
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
@ -254,7 +254,8 @@ public class Entity {
* @param isOnGround Whether the entity is currently on the ground. * @param isOnGround Whether the entity is currently on the ground.
*/ */
public void teleport(Vector3f position, float yaw, float pitch, boolean isOnGround) { public void teleport(Vector3f position, float yaw, float pitch, boolean isOnGround) {
moveAbsolute(position, yaw, pitch, isOnGround, false); // teleport will always set the headYaw to yaw
moveAbsolute(position, yaw, pitch, yaw, isOnGround, false);
} }
/** /**
@ -262,7 +263,7 @@ public class Entity {
* @param headYaw The new head rotation of the entity. * @param headYaw The new head rotation of the entity.
*/ */
public void updateHeadLookRotation(float headYaw) { public void updateHeadLookRotation(float headYaw) {
moveRelative(0, 0, 0, headYaw, pitch, this.headYaw, onGround); moveRelative(0, 0, 0, getYaw(), getPitch(), headYaw, isOnGround());
} }
/** /**
@ -275,7 +276,7 @@ public class Entity {
* @param isOnGround Whether the entity is currently on the ground. * @param isOnGround Whether the entity is currently on the ground.
*/ */
public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
moveRelative(moveX, moveY, moveZ, this.yaw, pitch, yaw, isOnGround); moveRelative(moveX, moveY, moveZ, yaw, pitch, getHeadYaw(), isOnGround);
} }
/** /**
@ -436,12 +437,12 @@ public class Entity {
} }
/** /**
* x = Pitch, y = HeadYaw, z = Yaw * x = Pitch, y = Yaw, z = HeadYaw
* *
* @return the bedrock rotation * @return the bedrock rotation
*/ */
public Vector3f getBedrockRotation() { public Vector3f getBedrockRotation() {
return Vector3f.from(pitch, headYaw, yaw); return Vector3f.from(getPitch(), getYaw(), getHeadYaw());
} }
/** /**

View File

@ -72,6 +72,6 @@ public class FireballEntity extends ThrowableEntity {
@Override @Override
public void tick() { public void tick() {
moveAbsoluteImmediate(tickMovement(position), yaw, pitch, headYaw, false, false); moveAbsoluteImmediate(tickMovement(position), getYaw(), getPitch(), getHeadYaw(), false, false);
} }
} }

View File

@ -152,7 +152,7 @@ public class FishingHookEntity extends ThrowableEntity {
float gravity = getGravity(); float gravity = getGravity();
motion = motion.down(gravity); motion = motion.down(gravity);
moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false); moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
float drag = getDrag(); float drag = getDrag();
motion = motion.mul(drag); motion = motion.mul(drag);
@ -160,7 +160,7 @@ public class FishingHookEntity extends ThrowableEntity {
@Override @Override
protected float getGravity() { protected float getGravity() {
if (!isInWater() && !onGround) { if (!isInWater() && !isOnGround()) {
return 0.03f; return 0.03f;
} }
return 0; return 0;

View File

@ -76,10 +76,10 @@ public class ItemEntity extends ThrowableEntity {
if (isInWater()) { if (isInWater()) {
return; return;
} }
if (!onGround || (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) > 0.00001) { if (!isOnGround() || (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) > 0.00001) {
float gravity = getGravity(); float gravity = getGravity();
motion = motion.down(gravity); motion = motion.down(gravity);
moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false); moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
float drag = getDrag(); float drag = getDrag();
motion = motion.mul(drag, 0.98f, drag); motion = motion.mul(drag, 0.98f, drag);
} }
@ -124,7 +124,7 @@ public class ItemEntity extends ThrowableEntity {
@Override @Override
protected float getGravity() { protected float getGravity() {
if (getFlag(EntityFlag.HAS_GRAVITY) && !onGround && !isInWater()) { if (getFlag(EntityFlag.HAS_GRAVITY) && !isOnGround() && !isInWater()) {
// Gravity can change if the item is in water/lava, but // Gravity can change if the item is in water/lava, but
// the server calculates the motion & position for us // the server calculates the motion & position for us
return 0.04f; return 0.04f;
@ -134,7 +134,7 @@ public class ItemEntity extends ThrowableEntity {
@Override @Override
protected float getDrag() { protected float getDrag() {
if (onGround) { if (isOnGround()) {
Vector3i groundBlockPos = position.toInt().down(1); Vector3i groundBlockPos = position.toInt().down(1);
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, groundBlockPos); int blockState = session.getGeyser().getWorldManager().getBlockAt(session, groundBlockPos);
return BlockStateValues.getSlipperiness(blockState) * 0.98f; return BlockStateValues.getSlipperiness(blockState) * 0.98f;

View File

@ -66,7 +66,7 @@ public class MinecartEntity extends Entity {
@Override @Override
public Vector3f getBedrockRotation() { public Vector3f getBedrockRotation() {
// Note: minecart rotation on rails does not care about the actual rotation value // Note: minecart rotation on rails does not care about the actual rotation value
return Vector3f.from(0, yaw, 0); return Vector3f.from(0, getYaw(), 0);
} }
@Override @Override

View File

@ -56,7 +56,7 @@ public class ThrowableEntity extends Entity implements Tickable {
*/ */
@Override @Override
public void tick() { public void tick() {
moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false); moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
float drag = getDrag(); float drag = getDrag();
float gravity = getGravity(); float gravity = getGravity();
motion = motion.mul(drag).down(gravity); motion = motion.mul(drag).down(gravity);
@ -89,20 +89,20 @@ public class ThrowableEntity extends Entity implements Tickable {
} }
setPosition(position); setPosition(position);
if (this.yaw != yaw) { if (getYaw() != yaw) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW); moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
moveEntityDeltaPacket.setYaw(yaw); moveEntityDeltaPacket.setYaw(yaw);
this.yaw = yaw; setYaw(yaw);
} }
if (this.pitch != pitch) { if (getPitch() != pitch) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH); moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
moveEntityDeltaPacket.setPitch(pitch); moveEntityDeltaPacket.setPitch(pitch);
this.pitch = pitch; setPitch(pitch);
} }
if (this.headYaw != headYaw) { if (getHeadYaw() != headYaw) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW); moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW);
moveEntityDeltaPacket.setHeadYaw(headYaw); moveEntityDeltaPacket.setHeadYaw(headYaw);
this.headYaw = headYaw; setHeadYaw(headYaw);
} }
if (!moveEntityDeltaPacket.getFlags().isEmpty()) { if (!moveEntityDeltaPacket.getFlags().isEmpty()) {

View File

@ -42,6 +42,7 @@ import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.MathUtils;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -87,8 +88,6 @@ public class ArmorStandEntity extends LivingEntity {
@Override @Override
public void spawnEntity() { public void spawnEntity() {
this.pitch = yaw;
this.headYaw = yaw;
super.spawnEntity(); super.spawnEntity();
} }
@ -205,9 +204,9 @@ public class ArmorStandEntity extends LivingEntity {
// Indicate that rotation should be checked // Indicate that rotation should be checked
setFlag(EntityFlag.BRIBED, true); setFlag(EntityFlag.BRIBED, true);
int rotationX = getRotation(rotation.getPitch()); int rotationX = MathUtils.wrapDegreesToInt(rotation.getPitch());
int rotationY = getRotation(rotation.getYaw()); int rotationY = MathUtils.wrapDegreesToInt(rotation.getYaw());
int rotationZ = getRotation(rotation.getRoll()); int rotationZ = MathUtils.wrapDegreesToInt(rotation.getRoll());
// The top bit acts like binary and determines if each rotation goes above 100 // The top bit acts like binary and determines if each rotation goes above 100
// We don't do this for the negative values out of concerns of the number being too big // We don't do this for the negative values out of concerns of the number being too big
int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0); int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0);
@ -319,7 +318,7 @@ public class ArmorStandEntity extends LivingEntity {
// Create the second entity. It doesn't need to worry about the items, but it does need to worry about // Create the second entity. It doesn't need to worry about the items, but it does need to worry about
// the metadata as it will hold the name tag. // the metadata as it will hold the name tag.
secondEntity = new ArmorStandEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(), null, secondEntity = new ArmorStandEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(), null,
EntityDefinitions.ARMOR_STAND, position, motion, yaw, pitch, headYaw); EntityDefinitions.ARMOR_STAND, position, motion, getYaw(), getPitch(), getHeadYaw());
secondEntity.primaryEntity = false; secondEntity.primaryEntity = false;
if (!this.positionRequiresOffset) { if (!this.positionRequiresOffset) {
// Ensure the offset is applied for the 0 scale // Ensure the offset is applied for the 0 scale
@ -375,17 +374,6 @@ public class ArmorStandEntity extends LivingEntity {
} }
} }
private int getRotation(float rotation) {
rotation = rotation % 360f;
if (rotation < -180f) {
rotation += 360f;
} else if (rotation >= 180f) {
// 181 -> -179
rotation = -(180 - (rotation - 180));
}
return (int) rotation;
}
/** /**
* If this armor stand is not a marker, set its bounding box size and scale. * If this armor stand is not a marker, set its bounding box size and scale.
*/ */
@ -439,9 +427,14 @@ public class ArmorStandEntity extends LivingEntity {
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
moveEntityPacket.setRuntimeEntityId(geyserId); moveEntityPacket.setRuntimeEntityId(geyserId);
moveEntityPacket.setPosition(position); moveEntityPacket.setPosition(position);
moveEntityPacket.setRotation(Vector3f.from(yaw, yaw, yaw)); moveEntityPacket.setRotation(getBedrockRotation());
moveEntityPacket.setOnGround(onGround); moveEntityPacket.setOnGround(isOnGround());
moveEntityPacket.setTeleported(false); moveEntityPacket.setTeleported(false);
session.sendUpstreamPacket(moveEntityPacket); session.sendUpstreamPacket(moveEntityPacket);
} }
@Override
public Vector3f getBedrockRotation() {
return Vector3f.from(getYaw(), getYaw(), getYaw());
}
} }

View File

@ -117,7 +117,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
@Override @Override
public Vector3f getBedrockRotation() { public Vector3f getBedrockRotation() {
return Vector3f.from(pitch, yaw, yaw); return Vector3f.from(getPitch(), getYaw(), getYaw());
} }
@Override @Override

View File

@ -130,7 +130,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
for (int i = 0; i < segmentHistory.length; i++) { for (int i = 0; i < segmentHistory.length; i++) {
segmentHistory[i] = new Segment(); segmentHistory[i] = new Segment();
segmentHistory[i].yaw = headYaw; segmentHistory[i].yaw = getHeadYaw();
segmentHistory[i].y = position.getY(); segmentHistory[i].y = position.getY();
} }
} }
@ -168,7 +168,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
* Updates the positions of the Ender Dragon's multiple bounding boxes * Updates the positions of the Ender Dragon's multiple bounding boxes
*/ */
private void updateBoundingBoxes() { private void updateBoundingBoxes() {
Vector3f facingDir = Vector3f.createDirectionDeg(0, headYaw); Vector3f facingDir = Vector3f.createDirectionDeg(0, getHeadYaw());
Segment baseSegment = getSegment(5); Segment baseSegment = getSegment(5);
// Used to angle the head, neck, and tail when the dragon flies up/down // Used to angle the head, neck, and tail when the dragon flies up/down
float pitch = (float) Math.toRadians(10 * (baseSegment.getY() - getSegment(10).getY())); float pitch = (float) Math.toRadians(10 * (baseSegment.getY() - getSegment(10).getY()));
@ -187,7 +187,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
neck.setPosition(facingDir.up(pitchY).mul(pitchXZ, 1, -pitchXZ).mul(5.5f).up(headDuck)); neck.setPosition(facingDir.up(pitchY).mul(pitchXZ, 1, -pitchXZ).mul(5.5f).up(headDuck));
body.setPosition(facingDir.mul(0.5f, 0f, -0.5f)); body.setPosition(facingDir.mul(0.5f, 0f, -0.5f));
Vector3f wingPos = Vector3f.createDirectionDeg(0, 90f - headYaw).mul(4.5f).up(2f); Vector3f wingPos = Vector3f.createDirectionDeg(0, 90f - getHeadYaw()).mul(4.5f).up(2f);
rightWing.setPosition(wingPos); rightWing.setPosition(wingPos);
leftWing.setPosition(wingPos.mul(-1, 1, -1)); // Mirror horizontally leftWing.setPosition(wingPos.mul(-1, 1, -1)); // Mirror horizontally
@ -196,7 +196,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
float distance = (i + 1) * 2f; float distance = (i + 1) * 2f;
// Curls the tail when the dragon turns // Curls the tail when the dragon turns
Segment targetSegment = getSegment(12 + 2 * i); Segment targetSegment = getSegment(12 + 2 * i);
float angle = headYaw + targetSegment.yaw - baseSegment.yaw; float angle = getHeadYaw() + targetSegment.yaw - baseSegment.yaw;
float tailYOffset = targetSegment.y - baseSegment.y - (distance + 1.5f) * pitchY + 1.5f; float tailYOffset = targetSegment.y - baseSegment.y - (distance + 1.5f) * pitchY + 1.5f;
tail[i].setPosition(Vector3f.createDirectionDeg(0, angle).mul(distance).add(tailBase).mul(-pitchXZ, 1, pitchXZ).up(tailYOffset)); tail[i].setPosition(Vector3f.createDirectionDeg(0, angle).mul(distance).add(tailBase).mul(-pitchXZ, 1, pitchXZ).up(tailYOffset));
@ -306,7 +306,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
*/ */
private void pushSegment() { private void pushSegment() {
latestSegment = (latestSegment + 1) % segmentHistory.length; latestSegment = (latestSegment + 1) % segmentHistory.length;
segmentHistory[latestSegment].yaw = headYaw; segmentHistory[latestSegment].yaw = getHeadYaw();
segmentHistory[latestSegment].y = position.getY(); segmentHistory[latestSegment].y = position.getY();
} }

View File

@ -213,7 +213,7 @@ public class PlayerEntity extends LivingEntity {
@Override @Override
public void updateHeadLookRotation(float headYaw) { public void updateHeadLookRotation(float headYaw) {
moveRelative(0, 0, 0, yaw, pitch, headYaw, onGround); moveRelative(0, 0, 0, getYaw(), getPitch(), headYaw, isOnGround());
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(geyserId); movePlayerPacket.setRuntimeEntityId(geyserId);
movePlayerPacket.setPosition(position); movePlayerPacket.setPosition(position);
@ -233,9 +233,11 @@ public class PlayerEntity extends LivingEntity {
} }
} }
@Override public void updateRotation(float yaw, float pitch, float headYaw, boolean isOnGround) {
public void updateRotation(float yaw, float pitch, boolean isOnGround) { // the method below is called by super.updateRotation(yaw, pitch, isOnGround).
super.updateRotation(yaw, pitch, isOnGround); // but we have to be able to set the headYaw, so we call the method below directly.
super.moveRelative(0, 0, 0, yaw, pitch, headYaw, isOnGround);
// Both packets need to be sent or else player head rotation isn't correctly updated // Both packets need to be sent or else player head rotation isn't correctly updated
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(geyserId); movePlayerPacket.setRuntimeEntityId(geyserId);
@ -252,6 +254,11 @@ public class PlayerEntity extends LivingEntity {
} }
} }
@Override
public void updateRotation(float yaw, float pitch, boolean isOnGround) {
updateRotation(yaw, pitch, getHeadYaw(), isOnGround);
}
@Override @Override
public void setPosition(Vector3f position) { public void setPosition(Vector3f position) {
super.setPosition(position.add(0, definition.offset(), 0)); super.setPosition(position.add(0, definition.offset(), 0));
@ -300,7 +307,7 @@ public class PlayerEntity extends LivingEntity {
} }
// The parrot is a separate entity in Bedrock, but part of the player entity in Java //TODO is a UUID provided in NBT? // The parrot is a separate entity in Bedrock, but part of the player entity in Java //TODO is a UUID provided in NBT?
ParrotEntity parrot = new ParrotEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(), ParrotEntity parrot = new ParrotEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(),
null, EntityDefinitions.PARROT, position, motion, yaw, pitch, headYaw); null, EntityDefinitions.PARROT, position, motion, getYaw(), getPitch(), getHeadYaw());
parrot.spawnEntity(); parrot.spawnEntity();
parrot.getDirtyMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue()); parrot.getDirtyMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue());
// Different position whether the parrot is left or right // Different position whether the parrot is left or right

View File

@ -26,8 +26,14 @@
package org.geysermc.geyser.inventory; package org.geysermc.geyser.inventory;
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ItemUtils;
import javax.annotation.Nullable;
/** /**
* Used to determine if rename packets should be sent and stores * Used to determine if rename packets should be sent and stores
@ -48,6 +54,7 @@ public class AnvilContainer extends Container {
/** /**
* The new name of the item as received from Bedrock * The new name of the item as received from Bedrock
*/ */
@Nullable
private String newName = null; private String newName = null;
private GeyserItemStack lastInput = GeyserItemStack.EMPTY; private GeyserItemStack lastInput = GeyserItemStack.EMPTY;
@ -59,6 +66,36 @@ public class AnvilContainer extends Container {
super(title, id, size, containerType, playerInventory); super(title, id, size, containerType, playerInventory);
} }
/**
* @return the name to use instead for renaming.
*/
public String checkForRename(GeyserSession session, String rename) {
String correctRename;
newName = rename;
String originalName = ItemUtils.getCustomName(getInput().getNbt());
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
String plainNewName = MessageTranslator.convertToPlainText(rename, session.locale());
if (!plainOriginalName.equals(plainNewName)) {
// Strip out formatting since Java Edition does not allow it
correctRename = plainNewName;
// Java Edition sends a packet every time an item is renamed even slightly in GUI. Fortunately, this works out for us now
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainNewName);
session.sendDownstreamPacket(renameItemPacket);
} else {
// Restore formatting for item since we're not renaming
correctRename = MessageTranslator.convertMessageLenient(originalName);
// Java Edition sends the original custom name when not renaming,
// if there isn't a custom name an empty string is sent
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainOriginalName);
session.sendDownstreamPacket(renameItemPacket);
}
useJavaLevelCost = false;
return correctRename;
}
public GeyserItemStack getInput() { public GeyserItemStack getInput() {
return getItem(0); return getItem(0);
} }

View File

@ -0,0 +1,35 @@
/*
* 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.inventory.recipe;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
/**
* @param buttonId the button that needs to be pressed for Java Edition to accept this item.
* @param output the expected output of this item when cut.
*/
public record GeyserStonecutterData(int buttonId, ItemStack output) {
}

View File

@ -384,19 +384,19 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
if (enchantTag.get("id") instanceof StringTag javaEnchId) { if (enchantTag.get("id") instanceof StringTag javaEnchId) {
JavaEnchantment enchantment = JavaEnchantment.getByJavaIdentifier(javaEnchId.getValue()); JavaEnchantment enchantment = JavaEnchantment.getByJavaIdentifier(javaEnchId.getValue());
if (enchantment == null) { if (enchantment == null) {
GeyserImpl.getInstance().getLogger().debug("Unknown java enchantment: " + javaEnchId.getValue()); GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment in anvil: " + javaEnchId.getValue());
continue; continue;
} }
Tag javaEnchLvl = enchantTag.get("lvl"); Tag javaEnchLvl = enchantTag.get("lvl");
if (!(javaEnchLvl instanceof ShortTag || javaEnchLvl instanceof IntTag)) if (javaEnchLvl == null || !(javaEnchLvl.getValue() instanceof Number number))
continue; continue;
// Handle duplicate enchantments // Handle duplicate enchantments
if (bedrock) { if (bedrock) {
enchantments.putIfAbsent(enchantment, ((Number) javaEnchLvl.getValue()).intValue()); enchantments.putIfAbsent(enchantment, number.intValue());
} else { } else {
enchantments.mergeInt(enchantment, ((Number) javaEnchLvl.getValue()).intValue(), Math::max); enchantments.mergeInt(enchantment, number.intValue(), Math::max);
} }
} }
} }

View File

@ -47,6 +47,8 @@ import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
public class ConnectorServerEventHandler implements BedrockServerEventHandler { public class ConnectorServerEventHandler implements BedrockServerEventHandler {
private static final boolean PRINT_DEBUG_PINGS = Boolean.parseBoolean(System.getProperty("Geyser.PrintPingsInDebugMode", "true"));
/* /*
The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client
*/ */
@ -88,7 +90,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
@Override @Override
public BedrockPong onQuery(InetSocketAddress inetSocketAddress) { public BedrockPong onQuery(InetSocketAddress inetSocketAddress) {
if (geyser.getConfig().isDebugMode()) { if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) {
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", inetSocketAddress)); geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", inetSocketAddress));
} }

View File

@ -30,7 +30,6 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.data.ExperimentData; import com.nukkitx.protocol.bedrock.data.ExperimentData;
import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.data.ResourcePackType;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.v471.Bedrock_v471;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
@ -166,11 +165,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true)); stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
} }
if (session.getUpstream().getProtocolVersion() <= Bedrock_v471.V471_CODEC.getProtocolVersion()) {
// Allow extended world height in the overworld to work for pre-1.18 clients
stackPacket.getExperiments().add(new ExperimentData("caves_and_cliffs", true));
}
session.sendUpstreamPacket(stackPacket); session.sendUpstreamPacket(stackPacket);
break; break;

View File

@ -100,6 +100,7 @@ import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.level.physics.CollisionManager; import org.geysermc.geyser.level.physics.CollisionManager;
import org.geysermc.geyser.network.netty.LocalSession; import org.geysermc.geyser.network.netty.LocalSession;
@ -362,7 +363,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
* The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier * The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier
*/ */
@Setter @Setter
private Int2ObjectMap<IntList> stonecutterRecipes; private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes;
/** /**
* Whether to work around 1.13's different behavior in villager trading menus. * Whether to work around 1.13's different behavior in villager trading menus.

View File

@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.Ser
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.level.GeyserAdvancement; import org.geysermc.geyser.level.GeyserAdvancement;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
@ -140,7 +141,7 @@ public class AdvancementsCache {
if (advancement != null) { if (advancement != null) {
if (advancement.getParentId() != null && currentAdvancementCategoryId.equals(advancement.getRootId(this))) { if (advancement.getParentId() != null && currentAdvancementCategoryId.equals(advancement.getRootId(this))) {
boolean color = isEarned(advancement) || !advancement.getDisplayData().isShowToast(); boolean color = isEarned(advancement) || !advancement.getDisplayData().isShowToast();
builder.button((color ? "§6" : "") + MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()) + '\n'); builder.button((color ? ChatColor.DARK_GREEN : "") + MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()) + '\n');
} }
} }
} }
@ -266,10 +267,9 @@ public class AdvancementsCache {
} }
public String getColorFromAdvancementFrameType(GeyserAdvancement advancement) { public String getColorFromAdvancementFrameType(GeyserAdvancement advancement) {
String base = "\u00a7";
if (advancement.getDisplayData().getFrameType() == Advancement.DisplayData.FrameType.CHALLENGE) { if (advancement.getDisplayData().getFrameType() == Advancement.DisplayData.FrameType.CHALLENGE) {
return base + "5"; return ChatColor.DARK_PURPLE;
} }
return base + "a"; // Used for types TASK and GOAL return ChatColor.GREEN; // Used for types TASK and GOAL
} }
} }

View File

@ -27,7 +27,12 @@ package org.geysermc.geyser.translator.inventory;
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest;
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftRecipeOptionalStackRequestActionData;
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData;
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType;
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
import org.geysermc.geyser.inventory.AnvilContainer; import org.geysermc.geyser.inventory.AnvilContainer;
import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.PlayerInventory;
@ -35,12 +40,34 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.updater.AnvilInventoryUpdater; import org.geysermc.geyser.inventory.updater.AnvilInventoryUpdater;
import java.util.Objects;
public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator { public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator {
public AnvilInventoryTranslator() { public AnvilInventoryTranslator() {
super(3, "minecraft:anvil[facing=north]", com.nukkitx.protocol.bedrock.data.inventory.ContainerType.ANVIL, AnvilInventoryUpdater.INSTANCE, super(3, "minecraft:anvil[facing=north]", com.nukkitx.protocol.bedrock.data.inventory.ContainerType.ANVIL, AnvilInventoryUpdater.INSTANCE,
"minecraft:chipped_anvil", "minecraft:damaged_anvil"); "minecraft:chipped_anvil", "minecraft:damaged_anvil");
} }
@Override
protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
return action.getType() == StackRequestActionType.CRAFT_RECIPE_OPTIONAL;
}
@Override
protected ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
// Guarded by shouldHandleRequestFirst check
CraftRecipeOptionalStackRequestActionData data = (CraftRecipeOptionalStackRequestActionData) request.getActions()[0];
AnvilContainer container = (AnvilContainer) inventory;
// Required as of 1.18.30 - FilterTextPackets no longer appear to be sent
String name = request.getFilterStrings()[data.getFilteredStringIndex()];
if (!Objects.equals(name, container.getNewName())) {
container.checkForRename(session, name);
}
return super.translateRequest(session, inventory, request);
}
@Override @Override
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
return switch (slotInfoData.getContainer()) { return switch (slotInfoData.getContainer()) {

View File

@ -144,7 +144,7 @@ public abstract class InventoryTranslator {
/** /**
* If {@link #shouldHandleRequestFirst(StackRequestActionData, Inventory)} returns true, this will be called * If {@link #shouldHandleRequestFirst(StackRequestActionData, Inventory)} returns true, this will be called
*/ */
public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { protected ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
return rejectRequest(request); return rejectRequest(request);
} }

View File

@ -31,20 +31,14 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.Ser
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest; import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest;
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftResultsDeprecatedStackRequestActionData; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftRecipeStackRequestActionData;
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData;
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType;
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.StonecutterContainer;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.session.GeyserSession;
public class StonecutterInventoryTranslator extends AbstractBlockInventoryTranslator { public class StonecutterInventoryTranslator extends AbstractBlockInventoryTranslator {
public StonecutterInventoryTranslator() { public StonecutterInventoryTranslator() {
@ -53,31 +47,26 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl
@Override @Override
protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
// First is pre-1.18. TODO remove after 1.17.40 support is dropped and refactor stonecutter support to use CraftRecipeStackRequestActionData's recipe ID return action.getType() == StackRequestActionType.CRAFT_RECIPE;
return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED || action.getType() == StackRequestActionType.CRAFT_RECIPE;
} }
@Override @Override
public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { protected ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
// TODO: Also surely to change in the future // Guarded by shouldHandleRequestFirst
StackRequestActionData data = request.getActions()[1]; CraftRecipeStackRequestActionData data = (CraftRecipeStackRequestActionData) request.getActions()[0];
if (!(data instanceof CraftResultsDeprecatedStackRequestActionData craftData)) {
// Look up all possible options of cutting from this ID
GeyserStonecutterData craftingData = session.getStonecutterRecipes().get(data.getRecipeNetworkId());
if (craftingData == null) {
return rejectRequest(request); return rejectRequest(request);
} }
StonecutterContainer container = (StonecutterContainer) inventory; StonecutterContainer container = (StonecutterContainer) inventory;
// Get the ID of the item we are cutting int button = craftingData.buttonId();
int id = inventory.getItem(0).getJavaId();
// Look up all possible options of cutting from this ID
IntList results = session.getStonecutterRecipes().get(id);
if (results == null) {
return rejectRequest(request);
}
ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0], session.getItemMappings());
int button = results.indexOf(javaOutput.getId());
// If we've already pressed the button with this item, no need to press it again! // If we've already pressed the button with this item, no need to press it again!
if (container.getStonecutterButton() != button) { if (container.getStonecutterButton() != button) {
ItemStack javaOutput = craftingData.output();
// Getting the index of the item in the Java stonecutter list // Getting the index of the item in the Java stonecutter list
ServerboundContainerButtonClickPacket packet = new ServerboundContainerButtonClickPacket(inventory.getId(), button); ServerboundContainerButtonClickPacket packet = new ServerboundContainerButtonClickPacket(inventory.getId(), button);
session.sendDownstreamPacket(packet); session.sendDownstreamPacket(packet);

View File

@ -127,7 +127,7 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue()); Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue());
if (enchantment == null) { if (enchantment == null) {
GeyserImpl.getInstance().getLogger().debug("Unknown java enchantment: " + javaEnchId.getValue()); GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment while NBT item translating: " + javaEnchId.getValue());
return null; return null;
} }

View File

@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.Ser
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundSignUpdatePacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundSignUpdatePacket;
import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMap;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import com.nukkitx.protocol.bedrock.v503.Bedrock_v503;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
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;
@ -47,7 +48,9 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
// This is the reason why this all works - Bedrock sends packets every time you update the sign, Java only wants the final packet // This is the reason why this all works - Bedrock sends packets every time you update the sign, Java only wants the final packet
// But Bedrock sends one final packet when you're done editing the sign, which should be equal to the last message since there's no edits // But Bedrock sends one final packet when you're done editing the sign, which should be equal to the last message since there's no edits
// So if the latest update does not match the last cached update then it's still being edited // So if the latest update does not match the last cached update then it's still being edited
if (!text.equals(session.getLastSignMessage())) { // TODO check 1.19:
// Bedrock only sends one packet as of 1.18.30. I (Camotoy) am suspicious this is a bug, but if it's permanent then we don't need the lastSignMessage variable.
if (session.getUpstream().getProtocolVersion() < Bedrock_v503.V503_CODEC.getProtocolVersion() && !text.equals(session.getLastSignMessage())) {
session.setLastSignMessage(text); session.setLastSignMessage(text);
return; return;
} }

View File

@ -25,15 +25,12 @@
package org.geysermc.geyser.translator.protocol.bedrock; package org.geysermc.geyser.translator.protocol.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
import com.nukkitx.protocol.bedrock.packet.FilterTextPacket; import com.nukkitx.protocol.bedrock.packet.FilterTextPacket;
import org.geysermc.geyser.inventory.AnvilContainer; import org.geysermc.geyser.inventory.AnvilContainer;
import org.geysermc.geyser.inventory.CartographyContainer; import org.geysermc.geyser.inventory.CartographyContainer;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
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.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ItemUtils;
/** /**
* Used to send strings to the server and filter out unwanted words. * Used to send strings to the server and filter out unwanted words.
@ -50,28 +47,7 @@ public class BedrockFilterTextTranslator extends PacketTranslator<FilterTextPack
} }
packet.setFromServer(true); packet.setFromServer(true);
if (session.getOpenInventory() instanceof AnvilContainer anvilContainer) { if (session.getOpenInventory() instanceof AnvilContainer anvilContainer) {
anvilContainer.setNewName(packet.getText()); packet.setText(anvilContainer.checkForRename(session, packet.getText()));
String originalName = ItemUtils.getCustomName(anvilContainer.getInput().getNbt());
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
String plainNewName = MessageTranslator.convertToPlainText(packet.getText(), session.locale());
if (!plainOriginalName.equals(plainNewName)) {
// Strip out formatting since Java Edition does not allow it
packet.setText(plainNewName);
// Java Edition sends a packet every time an item is renamed even slightly in GUI. Fortunately, this works out for us now
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainNewName);
session.sendDownstreamPacket(renameItemPacket);
} else {
// Restore formatting for item since we're not renaming
packet.setText(MessageTranslator.convertMessageLenient(originalName));
// Java Edition sends the original custom name when not renaming,
// if there isn't a custom name an empty string is sent
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainOriginalName);
session.sendDownstreamPacket(renameItemPacket);
}
anvilContainer.setUseJavaLevelCost(false);
session.getInventoryTranslator().updateSlot(session, anvilContainer, 1); session.getInventoryTranslator().updateSlot(session, anvilContainer, 1);
} }
session.sendUpstreamPacket(packet); session.sendUpstreamPacket(packet);

View File

@ -81,8 +81,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
// This isn't needed, but it makes the packets closer to vanilla // This isn't needed, but it makes the packets closer to vanilla
// It also means you can't "lag back" while only looking, in theory // It also means you can't "lag back" while only looking, in theory
if (!positionChanged && rotationChanged) { if (!positionChanged && rotationChanged) {
ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket( ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket(packet.isOnGround(), yaw, pitch);
packet.isOnGround(), packet.getRotation().getY(), packet.getRotation().getX());
entity.setYaw(yaw); entity.setYaw(yaw);
entity.setPitch(pitch); entity.setPitch(pitch);
@ -101,8 +100,11 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
Packet movePacket; Packet movePacket;
if (rotationChanged) { if (rotationChanged) {
// Send rotation updates as well // Send rotation updates as well
movePacket = new ServerboundMovePlayerPosRotPacket(packet.isOnGround(), position.getX(), position.getY(), position.getZ(), movePacket = new ServerboundMovePlayerPosRotPacket(
packet.getRotation().getY(), packet.getRotation().getX()); packet.isOnGround(),
position.getX(), position.getY(), position.getZ(),
yaw, pitch
);
entity.setYaw(yaw); entity.setYaw(yaw);
entity.setPitch(pitch); entity.setPitch(pitch);
entity.setHeadYaw(headYaw); entity.setHeadYaw(headYaw);

View File

@ -45,6 +45,7 @@ import lombok.EqualsAndHashCode;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
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;
@ -167,7 +168,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES); craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES);
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get()); craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get());
Int2ObjectMap<IntList> stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); Int2ObjectMap<GeyserStonecutterData> stonecutterRecipeMap = new Int2ObjectOpenHashMap<>();
for (Int2ObjectMap.Entry<List<StoneCuttingRecipeData>> data : unsortedStonecutterData.int2ObjectEntrySet()) { for (Int2ObjectMap.Entry<List<StoneCuttingRecipeData>> data : unsortedStonecutterData.int2ObjectEntrySet()) {
// Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore
// We can get the correct order for button pressing // We can get the correct order for button pressing
@ -176,11 +177,13 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
.getJavaIdentifier()))); .getJavaIdentifier())));
// Now that it's sorted, let's translate these recipes // Now that it's sorted, let's translate these recipes
int buttonId = 0;
for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) { for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) {
// As of 1.16.4, all stonecutter recipes have one ingredient option // As of 1.16.4, all stonecutter recipes have one ingredient option
ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0]; ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0];
ItemData input = ItemTranslator.translateToBedrock(session, ingredient); ItemData input = ItemTranslator.translateToBedrock(session, ingredient);
ItemData output = ItemTranslator.translateToBedrock(session, stoneCuttingData.getResult()); ItemStack javaOutput = stoneCuttingData.getResult();
ItemData output = ItemTranslator.translateToBedrock(session, javaOutput);
if (input.equals(ItemData.AIR) || output.equals(ItemData.AIR)) { if (input.equals(ItemData.AIR) || output.equals(ItemData.AIR)) {
// Probably modded items // Probably modded items
continue; continue;
@ -189,12 +192,11 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
// We need to register stonecutting recipes so they show up on Bedrock // We need to register stonecutting recipes so they show up on Bedrock
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(), craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
Collections.singletonList(input), Collections.singletonList(output), uuid, "stonecutter", 0, netId++)); Collections.singletonList(input), Collections.singletonList(output), uuid, "stonecutter", 0, netId));
// Save the recipe list for reference when crafting // Save the recipe list for reference when crafting
// Add the ingredient as the key and all possible values as the value // Add the net ID as the key and the button required + output for the value
IntList outputs = stonecutterRecipeMap.computeIfAbsent(ingredient.getId(), ($) -> new IntArrayList()); stonecutterRecipeMap.put(netId++, new GeyserStonecutterData(buttonId++, javaOutput));
outputs.add(stoneCuttingData.getResult().getId());
} }
} }

View File

@ -0,0 +1,68 @@
/*
* 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.java.entity.player;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerLookAtPacket;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.MathUtils;
@Translator(packet = ClientboundPlayerLookAtPacket.class)
public class JavaPlayerLookAtTranslator extends PacketTranslator<ClientboundPlayerLookAtPacket> {
@Override
public void translate(GeyserSession session, ClientboundPlayerLookAtPacket packet) {
var targetPosition = targetPosition(session, packet);
var selfPosition = session.getPlayerEntity().getPosition();
var xDelta = targetPosition.getX() - selfPosition.getX();
var yDelta = targetPosition.getY() - selfPosition.getY();
var zDelta = targetPosition.getZ() - selfPosition.getZ();
var sqrt = Math.sqrt(xDelta * xDelta + zDelta * zDelta);
var yaw = MathUtils.wrapDegrees(-Math.toDegrees(Math.atan2(yDelta, sqrt)));
var pitch = MathUtils.wrapDegrees(Math.toDegrees(Math.atan2(zDelta, xDelta)) - 90.0);
var self = session.getPlayerEntity();
// headYaw is also set to yaw in this packet
self.updateRotation(yaw, pitch, yaw, self.isOnGround());
}
public Vector3f targetPosition(GeyserSession session, ClientboundPlayerLookAtPacket packet) {
if (packet.getTargetEntityOrigin() != null) {
var entityId = packet.getTargetEntityId();
var target = session.getEntityCache().getEntityByJavaId(entityId);
if (target != null) {
return switch (packet.getTargetEntityOrigin()) {
case FEET -> target.getPosition();
case EYES -> target.getPosition().add(0, target.getBoundingBoxHeight(), 0);
};
}
}
return Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
}
}

View File

@ -74,7 +74,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
movePlayerPacket.setPosition(entity.getPosition()); movePlayerPacket.setPosition(entity.getPosition());
movePlayerPacket.setRotation(Vector3f.from(packet.getPitch(), packet.getYaw(), 0)); movePlayerPacket.setRotation(entity.getBedrockRotation());
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN); movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);
session.sendUpstreamPacket(movePlayerPacket); session.sendUpstreamPacket(movePlayerPacket);
@ -114,17 +114,15 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
double newZ = packet.getZ() + double newZ = packet.getZ() +
(packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0); (packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
float newPitch = packet.getPitch() + float newPitch = packet.getPitch() + (packet.getRelative().contains(PositionElement.PITCH) ? entity.getPitch() : 0);
(packet.getRelative().contains(PositionElement.PITCH) ? entity.getBedrockRotation().getX() : 0); float newYaw = packet.getYaw() + (packet.getRelative().contains(PositionElement.YAW) ? entity.getYaw() : 0);
float newYaw = packet.getYaw() +
(packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0);
int id = packet.getTeleportId(); int id = packet.getTeleportId();
session.getGeyser().getLogger().debug("Teleport (" + id + ") from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ()); session.getGeyser().getLogger().debug("Teleport (" + id + ") from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
Vector3f lastPlayerPosition = entity.getPosition().down(EntityDefinitions.PLAYER.offset()); Vector3f lastPlayerPosition = entity.getPosition().down(EntityDefinitions.PLAYER.offset());
float lastPlayerPitch = entity.getBedrockRotation().getX(); float lastPlayerPitch = entity.getPitch();
Vector3f teleportDestination = Vector3f.from(newX, newY, newZ); Vector3f teleportDestination = Vector3f.from(newX, newY, newZ);
entity.moveAbsolute(teleportDestination, newYaw, newPitch, true, true); entity.moveAbsolute(teleportDestination, newYaw, newPitch, true, true);

View File

@ -27,7 +27,6 @@ package org.geysermc.geyser.translator.protocol.java.entity.player;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundSetHealthPacket; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundSetHealthPacket;
import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.AttributeData;
import com.nukkitx.protocol.bedrock.packet.SetHealthPacket;
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
@ -44,11 +43,6 @@ public class JavaSetHealthTranslator extends PacketTranslator<ClientboundSetHeal
public void translate(GeyserSession session, ClientboundSetHealthPacket packet) { public void translate(GeyserSession session, ClientboundSetHealthPacket packet) {
SessionPlayerEntity entity = session.getPlayerEntity(); SessionPlayerEntity entity = session.getPlayerEntity();
int health = (int) Math.ceil(packet.getHealth());
SetHealthPacket setHealthPacket = new SetHealthPacket();
setHealthPacket.setHealth(health);
session.sendUpstreamPacket(setHealthPacket);
entity.setHealth(packet.getHealth()); entity.setHealth(packet.getHealth());
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();

View File

@ -26,9 +26,26 @@
package org.geysermc.geyser.util; package org.geysermc.geyser.util;
public class MathUtils { public class MathUtils {
public static final double SQRT_OF_TWO = Math.sqrt(2); public static final double SQRT_OF_TWO = Math.sqrt(2);
public static float wrapDegrees(float degrees) {
degrees = degrees % 360.0f;
if (degrees < -180.0f) {
degrees += 360.0f;
} else if (degrees >= 180.0f) {
degrees -= 360.0f;
}
return degrees;
}
public static float wrapDegrees(double degrees) {
return wrapDegrees((float) degrees);
}
public static int wrapDegreesToInt(float degrees) {
return (int) wrapDegrees(degrees);
}
/** /**
* Round the given float to the next whole number * Round the given float to the next whole number
* *