diff --git a/protocol/src/main/java/mc/protocol/State.java b/protocol/src/main/java/mc/protocol/State.java index 5704a93..f41c80e 100644 --- a/protocol/src/main/java/mc/protocol/State.java +++ b/protocol/src/main/java/mc/protocol/State.java @@ -53,17 +53,18 @@ public enum State { PLAY(3, // client side - Map.of( - 0x00, TeleportConfirmPacket.class, - 0x04, ClientSettingsPacket.class, - 0x09, PluginMessagePacket.class, - 0x0B, KeepAlivePacket.class, - 0x0C, PlayerOnGroundPacket.class, - 0x0D, PlayerPositionPacket.class, - 0x0E, CPlayerPositionAndLookPacket.class, - 0x0F, PlayerLookPacket.class, - 0x13, CPlayerAbilitiesPacket.class, - 0x15, EntityActionPacket.class + Map.ofEntries( + Map.entry(0x00, TeleportConfirmPacket.class), + Map.entry(0x04, ClientSettingsPacket.class), + Map.entry(0x09, PluginMessagePacket.class), + Map.entry(0x0B, KeepAlivePacket.class), + Map.entry(0x0C, PlayerOnGroundPacket.class), + Map.entry(0x0D, PlayerPositionPacket.class), + Map.entry(0x0E, CPlayerPositionAndLookPacket.class), + Map.entry(0x0F, PlayerLookPacket.class), + Map.entry(0x13, CPlayerAbilitiesPacket.class), + Map.entry(0x14, PlayerDiggingAndMorePacket.class), + Map.entry(0x15, EntityActionPacket.class) ), // server side Map.of( diff --git a/protocol/src/main/java/mc/protocol/packets/play/client/PlayerDiggingAndMorePacket.java b/protocol/src/main/java/mc/protocol/packets/play/client/PlayerDiggingAndMorePacket.java new file mode 100644 index 0000000..aee863b --- /dev/null +++ b/protocol/src/main/java/mc/protocol/packets/play/client/PlayerDiggingAndMorePacket.java @@ -0,0 +1,82 @@ +package mc.protocol.packets.play.client; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import mc.protocol.buffer.NetByteBuf; +import mc.protocol.model.BlockLocation; +import mc.protocol.packets.ClientSidePacket; +import mc.protocol.utils.DiggingStatus; +import mc.protocol.utils.Face; +import mc.protocol.utils.SerializeUtil; + +/** + * Клиент копает, ломает блок (и не только). + * + *

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

+ *
+ * | FIELD    | TYPE     | NOTES                                    |
+ * |----------|----------|------------------------------------------|
+ * | Status   | VarInt   | Действие Игрока к блоку                  |
+ * | Location | Position | Позиция блока                            |
+ * | Face     | Byte     | Сторона блока, с которой взаимодействуют |
+ * 
+ * + *

Возможные действия Игрока к блоку

+ *
+ * | VALUE    | DESCRIPTION             | NOTES                                                                                 |
+ * |--------- |-------------------------|---------------------------------------------------------------------------------------|
+ * | 0        | Начал ломать            |                                                                                       |
+ * | 1        | Прекратил ломать        | Отправляется, когда Игрок отпустил клавишу "копания"                                  |
+ * | 2        | Закончил ломать         | Отправляется, когда Игрок закончил ломать блок. Т.е. блок готов сломаться             |
+ * | 3        | Бросает стек предметов  | Отправляется, когда Игрок с помощью клавиши "Выкинуть предмет"(Q)                     |
+ * |          |                         | с модификатором(??) для выкидывания полного стека.                                    |
+ * |          |                         | Поле Location всегда будет 0/0/0, а Face всегда -Y.                                   |
+ * | 4        | Бросает предмет         | Отправляется, когда Игрок выкидывает предмет.                                         |
+ * |          |                         | Поле Location всегда будет 0/0/0, а Face всегда -Y.                                   |
+ * | 5        | Стреляет стрелой /      | Указывает, что текущего состояние удерживаемого предмета должно                       |
+ * |          | Заканчивает есть /      | быть обновлено. Например, поедание еды, натягивание луков, использование ведер и т.д. |
+ * |          | и т.д.                  | Поле Location всегда будет 0/0/0, а Face всегда -Y.                                   |
+ * | 6        | Поменять предмет в руке | Отправляется когда Игрок меняет предмет предмет во второй руке через "свап".          |
+ * |          |                         | Поле Location всегда будет 0/0/0, а Face всегда -Y.                                   |
+ * 
+ * + *

Значения Стороны блока

+ *
+ * | VALUE | OFFCET | FACE   |
+ * |-------|--------|--------|
+ * | 0     | -Y     | Bottom |
+ * | 1     | +Y     | Top    |
+ * | 2     | -Z     | North  |
+ * | 3     | +Z     | South  |
+ * | 4     | -X     | West   |
+ * | 5     | +X     | East   |
+ * 
+ * + * @see Player Digging + */ +@NoArgsConstructor +@Getter +@EqualsAndHashCode +@ToString +public class PlayerDiggingAndMorePacket implements ClientSidePacket { + + private DiggingStatus status; + private BlockLocation location; + private Face face; + + @Override + public void readSelf(NetByteBuf netByteBuf) { + this.status = DiggingStatus.valueById(netByteBuf.readVarInt()); + this.location = SerializeUtil.long2location(netByteBuf.readLong()); + this.face = Face.valueById(netByteBuf.readByte()); + } + + @Override + public void passivate() { + this.status = null; + this.location = null; + this.face = null; + } +} diff --git a/protocol/src/main/java/mc/protocol/packets/play/server/ChunkDataPacket.java b/protocol/src/main/java/mc/protocol/packets/play/server/ChunkDataPacket.java index 44f73b6..6ea0611 100644 --- a/protocol/src/main/java/mc/protocol/packets/play/server/ChunkDataPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/play/server/ChunkDataPacket.java @@ -5,7 +5,7 @@ import lombok.Data; import mc.protocol.buffer.NetByteBuf; import mc.protocol.packets.ServerSidePacket; import mc.protocol.pool.ProtocolObjectPool; -import mc.protocol.utils.ChunkSerializeUtil; +import mc.protocol.utils.SerializeUtil; import mc.protocol.world.Chunk; import mc.protocol.world.ChunkSection; @@ -101,7 +101,7 @@ public class ChunkDataPacket implements ServerSidePacket { for (int h = 0; h < 16; h++) { ChunkSection section = chunk.getSection(h); - NetByteBuf data = ChunkSerializeUtil.serializeSection(section); + NetByteBuf data = SerializeUtil.serializeChunkSection(section); dataStructure.writeBytes(data); // Data ProtocolObjectPool.netByteBuf().returnObject(data); } diff --git a/protocol/src/main/java/mc/protocol/utils/DiggingStatus.java b/protocol/src/main/java/mc/protocol/utils/DiggingStatus.java new file mode 100644 index 0000000..332b1a1 --- /dev/null +++ b/protocol/src/main/java/mc/protocol/utils/DiggingStatus.java @@ -0,0 +1,32 @@ +package mc.protocol.utils; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import javax.annotation.Nullable; + +@RequiredArgsConstructor +public enum DiggingStatus { + + STARTED(0), + CANCELLED(1), + FINISHED(2), + DROP_ITEM_STACK(3), + DROP_ITEM(4), + HELT_UPDATE_STATE(5), + SWAP_HAND(6); + + @Nullable + public static DiggingStatus valueById(int id) { + for (DiggingStatus diggingStatus : DiggingStatus.values()) { + if (diggingStatus.getId() == id) { + return diggingStatus; + } + } + + return null; + } + + @Getter + private final int id; +} diff --git a/protocol/src/main/java/mc/protocol/utils/Face.java b/protocol/src/main/java/mc/protocol/utils/Face.java new file mode 100644 index 0000000..58c305a --- /dev/null +++ b/protocol/src/main/java/mc/protocol/utils/Face.java @@ -0,0 +1,31 @@ +package mc.protocol.utils; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import javax.annotation.Nullable; + +@RequiredArgsConstructor +public enum Face { + + BOTTOM(0), + TOP(1), + NORTH(2), + SOUTH(3), + WEST(4), + EAST(5); + + @Nullable + public static Face valueById(int id) { + for (Face face : Face.values()) { + if (face.getId() == id) { + return face; + } + } + + return null; + } + + @Getter + private final int id; +} diff --git a/protocol/src/main/java/mc/protocol/utils/ChunkSerializeUtil.java b/protocol/src/main/java/mc/protocol/utils/SerializeUtil.java similarity index 74% rename from protocol/src/main/java/mc/protocol/utils/ChunkSerializeUtil.java rename to protocol/src/main/java/mc/protocol/utils/SerializeUtil.java index fe49109..34479d7 100644 --- a/protocol/src/main/java/mc/protocol/utils/ChunkSerializeUtil.java +++ b/protocol/src/main/java/mc/protocol/utils/SerializeUtil.java @@ -4,6 +4,7 @@ import io.netty.buffer.Unpooled; import lombok.AccessLevel; import lombok.NoArgsConstructor; import mc.protocol.buffer.NetByteBuf; +import mc.protocol.model.BlockLocation; import mc.protocol.pool.ProtocolObjectPool; import mc.protocol.world.Block; import mc.protocol.world.ChunkSection; @@ -12,12 +13,12 @@ import mc.utils.array.BitByteArray; import mc.utils.array.BitLongArray; @NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class ChunkSerializeUtil { +public final class SerializeUtil { private static final int BITS_PER_BLOCK = 13; private static final int ALL_BLOCKS = 16 * 16 * 16; - public static NetByteBuf serializeSection(ChunkSection section) { + public static NetByteBuf serializeChunkSection(ChunkSection section) { BitArray blockArray = new BitLongArray(BITS_PER_BLOCK, ALL_BLOCKS); BitArray blockLight = new BitByteArray(4, ALL_BLOCKS); BitArray skyLight = new BitByteArray(4, ALL_BLOCKS); @@ -56,4 +57,21 @@ public final class ChunkSerializeUtil { blockState & 0b1111 }; } + + public static long location2long(BlockLocation blockLocation) { + return ((long) (blockLocation.getX() & 0x3FFFFFF) << 38) + | ((long) (blockLocation.getZ() & 0x3FFFFFF) << 12) + | (blockLocation.getY() & 0xFFF); + } + + public static BlockLocation long2location(long value) { + BlockLocation blockLocation = new BlockLocation(); + blockLocation.set( + (int) (value >> 38), + (int) (value & 0xFFF), + (int) (value << 26 >> 38) + ); + + return blockLocation; + } } diff --git a/server/src/main/java/mc/server/di/FlatWorldModule.java b/server/src/main/java/mc/server/di/FlatWorldModule.java index ffb538f..192d3c4 100644 --- a/server/src/main/java/mc/server/di/FlatWorldModule.java +++ b/server/src/main/java/mc/server/di/FlatWorldModule.java @@ -2,7 +2,7 @@ package mc.server.di; import com.typesafe.config.Config; import mc.protocol.model.Location; -import mc.protocol.utils.ChunkSerializeUtil; +import mc.protocol.utils.SerializeUtil; import mc.protocol.world.World; import mc.server.world.FlatWorld; import mc.utils.array.BitArray; @@ -46,7 +46,7 @@ public class FlatWorldModule extends WorldModule { k += count; for (int j = 0; j < count; j++) { - flatConfig.put(ChunkSerializeUtil.blockIdMetaSerialize(blockId, blockMeta)); + flatConfig.put(SerializeUtil.blockIdMetaSerialize(blockId, blockMeta)); } } diff --git a/server/src/main/java/mc/server/world/FlatChunkSection.java b/server/src/main/java/mc/server/world/FlatChunkSection.java index e767cb8..4a788fd 100644 --- a/server/src/main/java/mc/server/world/FlatChunkSection.java +++ b/server/src/main/java/mc/server/world/FlatChunkSection.java @@ -3,7 +3,7 @@ package mc.server.world; import lombok.Getter; import lombok.RequiredArgsConstructor; import mc.protocol.model.BlockLocation; -import mc.protocol.utils.ChunkSerializeUtil; +import mc.protocol.utils.SerializeUtil; import mc.protocol.world.Block; import mc.protocol.world.ChunkSection; import mc.utils.array.BitArray; @@ -26,7 +26,7 @@ public class FlatChunkSection implements ChunkSection { public Block getBlock(int x, int y, int z) { return blocks.computeIfAbsent(y, y0 -> { int blockState = chunkConfig.get((this.y << 4) + y); - int[] blockIdMeta = ChunkSerializeUtil.blockIdMetaDeserialize(blockState); + int[] blockIdMeta = SerializeUtil.blockIdMetaDeserialize(blockState); SomeBlock block = new SomeBlock(); block.setId(blockIdMeta[0]); @@ -44,7 +44,7 @@ public class FlatChunkSection implements ChunkSection { public int getSkyLight(int x, int y, int z) { return sky.computeIfAbsent(y, y0 -> { int blockState = chunkConfig.get((this.y << 4) + y); - int[] blockIdMeta = ChunkSerializeUtil.blockIdMetaDeserialize(blockState); + int[] blockIdMeta = SerializeUtil.blockIdMetaDeserialize(blockState); if (blockIdMeta[0] != 0/*AIR*/) { return 0;