diff --git a/protocol/src/main/java/mc/protocol/State.java b/protocol/src/main/java/mc/protocol/State.java index 9f6cf71..12be60f 100644 --- a/protocol/src/main/java/mc/protocol/State.java +++ b/protocol/src/main/java/mc/protocol/State.java @@ -51,7 +51,8 @@ public enum State { Map.of( JoinGamePacket.class, 0x23, SpawnPositionPacket.class, 0x46, - PlayerAbilitiesPacket.class,0x2C + PlayerAbilitiesPacket.class,0x2C, + PlayerPositionAndLookPacket.class, 0x2F ) ); diff --git a/protocol/src/main/java/mc/protocol/model/Look.java b/protocol/src/main/java/mc/protocol/model/Look.java new file mode 100644 index 0000000..b4b9d4d --- /dev/null +++ b/protocol/src/main/java/mc/protocol/model/Look.java @@ -0,0 +1,11 @@ +package mc.protocol.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@AllArgsConstructor +@Data +public class Look { + private float yaw; + private float pitch; +} diff --git a/protocol/src/main/java/mc/protocol/packets/server/PlayerPositionAndLookPacket.java b/protocol/src/main/java/mc/protocol/packets/server/PlayerPositionAndLookPacket.java new file mode 100644 index 0000000..e2fd34e --- /dev/null +++ b/protocol/src/main/java/mc/protocol/packets/server/PlayerPositionAndLookPacket.java @@ -0,0 +1,81 @@ +package mc.protocol.packets.server; + +import lombok.Data; +import mc.protocol.io.NetByteBuf; +import mc.protocol.model.Location; +import mc.protocol.model.Look; +import mc.protocol.packets.ServerSidePacket; + +/** + * Установка позиции и угла осмотра Игрока. + * + *

Структура пакета

+ *
+ * | FIELD       | TYPE   | NOTES                                                                             |
+ * |-------------|--------|-----------------------------------------------------------------------------------|
+ * | X           | Double | Абсолютная или относительная позиция по X. Зависит от "Flags"                     |
+ * | Y           | Double | Абсолютная или относительная позиция по Y. Зависит от "Flags"                     |
+ * | Z           | Double | Абсолютная или относительная позиция по Z. Зависит от "Flags"                     |
+ * | Yaw         | Float  | Абсолютный или относительный поворот головы по OX, в градусах. Зависит от "Flags" |
+ * | Pitch       | Float  | Абсолютный или относительный поворот головы по OY, в градусах. Зависит от "Flags" |
+ * | Flags       | Byte   | Битовая маска значений флагов. См. значения ниже                                  |
+ * | Teleport ID | VarInt | ID для подтверждения клиентом перемещения Игрока                                  |
+ * 
+ * + *

Значения "Flags"

+ *
+ * | Field | Bit  |
+ * |-------|------|
+ * | X     | 0x01 |
+ * | Y     | 0x02 |
+ * | Z     | 0x04 |
+ * | X_ROT | 0x08 |
+ * | Y_ROT | 0x10 |
+ * 
+ * + *

Примечание от Dinnerbone про "Flags":

+ * "It's a bitfield, X/Y/Z/Y_ROT/X_ROT. If X is set, the x value is relative and not absolute." + * + * @see Player Position And Look + */ +@Data +public class PlayerPositionAndLookPacket implements ServerSidePacket { + + private Location position; + private Look look; + @SuppressWarnings("java:S116") + private byte $flags = 0; + private int teleportId; + + @Override + public void writeSelf(NetByteBuf netByteBuf) { + netByteBuf.writeDouble(this.position.getX()); + netByteBuf.writeDouble(this.position.getY()); + netByteBuf.writeDouble(this.position.getZ()); + netByteBuf.writeFloat(this.look.getYaw()); + netByteBuf.writeFloat(this.look.getPitch()); + netByteBuf.writeByte(this.$flags); + netByteBuf.writeVarInt(teleportId); + } + + //FIXME использовать value значения + public void setFlagX(boolean value) { + this.$flags = (byte) (this.$flags | 0x01); + } + + public void setFlagY(boolean value) { + this.$flags = (byte) (this.$flags | 0x02); + } + + public void setFlagZ(boolean value) { + this.$flags = (byte) (this.$flags | 0x04); + } + + public void setFlagXRot(boolean value) { + this.$flags = (byte) (this.$flags | 0x08); + } + + public void setFlagYRot(boolean value) { + this.$flags = (byte) (this.$flags | 0x10); + } +} diff --git a/server/src/main/java/mc/server/PacketHandler.java b/server/src/main/java/mc/server/PacketHandler.java index bbffca9..7135487 100644 --- a/server/src/main/java/mc/server/PacketHandler.java +++ b/server/src/main/java/mc/server/PacketHandler.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import mc.protocol.*; import mc.protocol.model.Location; +import mc.protocol.model.Look; import mc.protocol.model.ServerInfo; import mc.protocol.packets.PingPacket; import mc.protocol.packets.client.HandshakePacket; @@ -59,7 +60,7 @@ public class PacketHandler { public void onLoginStart(ChannelContext channel) { LoginStartPacket loginStartPacket = channel.getPacket(); - LoginSuccessPacket loginSuccessPacket = new LoginSuccessPacket(); + var loginSuccessPacket = new LoginSuccessPacket(); loginSuccessPacket.setUuid(UUID.randomUUID()); loginSuccessPacket.setName(loginStartPacket.getName()); @@ -67,7 +68,7 @@ public class PacketHandler { channel.getCtx().writeAndFlush(loginSuccessPacket); channel.setState(State.PLAY); - JoinGamePacket joinGamePacket = new JoinGamePacket(); + var joinGamePacket = new JoinGamePacket(); joinGamePacket.setEntityId(random.nextInt()); joinGamePacket.setGameMode(GameMode.SPECTATOR); joinGamePacket.setDimension(0/*Overworld*/); @@ -77,13 +78,15 @@ public class PacketHandler { log.info("{}", joinGamePacket); channel.getCtx().write(joinGamePacket); - SpawnPositionPacket spawnPositionPacket = new SpawnPositionPacket(); - spawnPositionPacket.setSpawn(new Location(0d, 63d, 0d)); + Location spawnLocation = new Location(0d, 63d, 0d); + + var spawnPositionPacket = new SpawnPositionPacket(); + spawnPositionPacket.setSpawn(spawnLocation); log.info("{}", spawnPositionPacket); channel.getCtx().write(spawnPositionPacket); - PlayerAbilitiesPacket playerAbilitiesPacket = new PlayerAbilitiesPacket(); + var playerAbilitiesPacket = new PlayerAbilitiesPacket(); playerAbilitiesPacket.setCatFly(true); playerAbilitiesPacket.setFlying(true); playerAbilitiesPacket.setCreativeMode(false); @@ -95,6 +98,14 @@ public class PacketHandler { channel.getCtx().write(playerAbilitiesPacket); channel.getCtx().flush(); + + var playerPositionAndLookPacket = new PlayerPositionAndLookPacket(); + playerPositionAndLookPacket.setPosition(spawnLocation); + playerPositionAndLookPacket.setLook(new Look(0f, 0f)); + playerPositionAndLookPacket.setTeleportId(random.nextInt()); + + log.info("{}", playerPositionAndLookPacket); + channel.getCtx().writeAndFlush(playerPositionAndLookPacket); } private static String faviconToBase64(Path iconPath) {