From 0d40be155273bf3905aead9626ee2d655638a2a1 Mon Sep 17 00:00:00 2001 From: TheMode Date: Sun, 18 Aug 2019 23:52:11 +0200 Subject: [PATCH] custom block with custom hardness --- src/main/java/fr/themode/minestom/Main.java | 10 ++++ .../fr/themode/minestom/entity/Player.java | 51 +++++++++++++++---- .../fr/themode/minestom/instance/Block.java | 23 --------- .../themode/minestom/instance/BlockBatch.java | 2 +- .../minestom/instance/BlockManager.java | 19 +++++++ .../fr/themode/minestom/instance/Chunk.java | 47 ++++++++++++----- .../minestom/instance/CustomBlock.java | 21 ++++++++ .../themode/minestom/instance/Instance.java | 30 +++++++---- .../minestom/instance/demo/StoneBlock.java | 16 ++++++ .../packet/client/login/LoginStartPacket.java | 14 ++--- .../ClientPlayerBlockPlacementPacket.java | 2 +- .../play/ClientPlayerDiggingPacket.java | 49 ++++++++++++++++-- .../packet/server/play/ChunkDataPacket.java | 8 +-- .../packet/server/play/ParticlePacket.java | 36 +++++++++++++ 14 files changed, 253 insertions(+), 75 deletions(-) delete mode 100644 src/main/java/fr/themode/minestom/instance/Block.java create mode 100644 src/main/java/fr/themode/minestom/instance/BlockManager.java create mode 100644 src/main/java/fr/themode/minestom/instance/CustomBlock.java create mode 100644 src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/ParticlePacket.java diff --git a/src/main/java/fr/themode/minestom/Main.java b/src/main/java/fr/themode/minestom/Main.java index 96f657c1a..d7a97c89d 100644 --- a/src/main/java/fr/themode/minestom/Main.java +++ b/src/main/java/fr/themode/minestom/Main.java @@ -7,7 +7,9 @@ import fr.adamaq01.ozao.net.server.ServerHandler; import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer; import fr.themode.minestom.entity.EntityManager; import fr.themode.minestom.entity.Player; +import fr.themode.minestom.instance.BlockManager; import fr.themode.minestom.instance.InstanceManager; +import fr.themode.minestom.instance.demo.StoneBlock; import fr.themode.minestom.net.ConnectionManager; import fr.themode.minestom.net.PacketProcessor; import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket; @@ -26,6 +28,7 @@ public class Main { // In-Game Manager private static InstanceManager instanceManager; + private static BlockManager blockManager; private static EntityManager entityManager; public static void main(String[] args) { @@ -33,8 +36,11 @@ public class Main { packetProcessor = new PacketProcessor(); instanceManager = new InstanceManager(); + blockManager = new BlockManager(); entityManager = new EntityManager(); + blockManager.registerBlock("stone", StoneBlock::new); + server = new TCPServer(new MinecraftProtocol()).addHandler(new ServerHandler() { @Override public void onConnect(Server server, Connection connection) { @@ -123,6 +129,10 @@ public class Main { return instanceManager; } + public static BlockManager getBlockManager() { + return blockManager; + } + public static EntityManager getEntityManager() { return entityManager; } diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index f6b2faf04..eec97514e 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -1,6 +1,7 @@ package fr.themode.minestom.entity; import fr.themode.minestom.Main; +import fr.themode.minestom.instance.CustomBlock; import fr.themode.minestom.inventory.Inventory; import fr.themode.minestom.inventory.PlayerInventory; import fr.themode.minestom.item.ItemStack; @@ -25,6 +26,7 @@ public class Player extends LivingEntity { private short heldSlot; private Inventory openInventory; + private CustomBlock targetCustomBlock; private Position targetBlockPosition; private long targetBlockTime; @@ -41,20 +43,17 @@ public class Player extends LivingEntity { public void update() { // Target block stage - if (instance != null && targetBlockPosition != null) { - int timeBreak = 750; // In ms + if (instance != null && targetCustomBlock != null) { + int timeBreak = targetCustomBlock.getBreakDelay(this); int animationCount = 10; long since = System.currentTimeMillis() - targetBlockTime; byte stage = (byte) (since / (timeBreak / animationCount)); - BlockBreakAnimationPacket breakAnimationPacket = new BlockBreakAnimationPacket(); - breakAnimationPacket.entityId = getEntityId() + 1; - breakAnimationPacket.blockPosition = targetBlockPosition; - breakAnimationPacket.destroyStage = stage; + sendBlockBreakAnimation(targetBlockPosition, stage);// TODO send to all near players if (stage > 9) { instance.setBlock(targetBlockPosition.getX(), targetBlockPosition.getY(), targetBlockPosition.getZ(), (short) 0); - refreshTargetBlock(null); + testParticle(targetBlockPosition.getX() + 0.5f, targetBlockPosition.getY(), targetBlockPosition.getZ() + 0.5f); + resetTargetBlock(); } - playerConnection.sendPacket(breakAnimationPacket); // TODO send to all online players } @@ -74,6 +73,29 @@ public class Player extends LivingEntity { playerConnection.sendPacket(new UpdateViewPositionPacket(Math.floorDiv((int) x, 16), Math.floorDiv((int) z, 16))); } + public void sendBlockBreakAnimation(Position blockPosition, byte destroyStage) { + BlockBreakAnimationPacket breakAnimationPacket = new BlockBreakAnimationPacket(); + breakAnimationPacket.entityId = getEntityId() + 1; + breakAnimationPacket.blockPosition = blockPosition; + breakAnimationPacket.destroyStage = destroyStage; + playerConnection.sendPacket(breakAnimationPacket); + } + + private void testParticle(float x, float y, float z) { + ParticlePacket particlePacket = new ParticlePacket(); + particlePacket.particleId = 3; // Block particle + particlePacket.longDistance = false; + particlePacket.x = x; + particlePacket.y = y; + particlePacket.z = z; + particlePacket.offsetX = 0.55f; + particlePacket.offsetY = 0.75f; + particlePacket.offsetZ = 0.55f; + particlePacket.particleData = 0.25f; + particlePacket.particleCount = 100; + playerConnection.sendPacket(particlePacket); + } + public void sendMessage(String message) { ChatMessagePacket chatMessagePacket = new ChatMessagePacket("{\"text\": \"" + message + "\"}", ChatMessagePacket.Position.CHAT); playerConnection.sendPacket(chatMessagePacket); @@ -136,6 +158,10 @@ public class Player extends LivingEntity { return openInventory; } + public CustomBlock getCustomBlockTarget() { + return targetCustomBlock; + } + public void openInventory(Inventory inventory) { if (inventory == null) throw new IllegalArgumentException("Inventory cannot be null, use Player#closeInventory() to close current"); @@ -200,11 +226,18 @@ public class Player extends LivingEntity { this.openInventory = openInventory; } - public void refreshTargetBlock(Position targetBlockPosition) { + public void refreshTargetBlock(CustomBlock targetCustomBlock, Position targetBlockPosition) { + this.targetCustomBlock = targetCustomBlock; this.targetBlockPosition = targetBlockPosition; this.targetBlockTime = targetBlockPosition == null ? 0 : System.currentTimeMillis(); } + public void resetTargetBlock() { + this.targetCustomBlock = null; + this.targetBlockPosition = null; + this.targetBlockTime = 0; + } + public long getLastKeepAlive() { return lastKeepAlive; } diff --git a/src/main/java/fr/themode/minestom/instance/Block.java b/src/main/java/fr/themode/minestom/instance/Block.java deleted file mode 100644 index 219e3d3a8..000000000 --- a/src/main/java/fr/themode/minestom/instance/Block.java +++ /dev/null @@ -1,23 +0,0 @@ -package fr.themode.minestom.instance; - -public class Block { - - private short type; - - public Block(short type) { - this.type = type; - } - - public short getType() { - return type; - } - - public void setType(short type) { - this.type = type; - } - - @Override - public String toString() { - return String.format("CustomBlock{type=%s}", type); - } -} diff --git a/src/main/java/fr/themode/minestom/instance/BlockBatch.java b/src/main/java/fr/themode/minestom/instance/BlockBatch.java index f7ba906fd..cf777fa44 100644 --- a/src/main/java/fr/themode/minestom/instance/BlockBatch.java +++ b/src/main/java/fr/themode/minestom/instance/BlockBatch.java @@ -59,7 +59,7 @@ public class BlockBatch { private short blockId; public void apply(Chunk chunk) { - chunk.setBlock(x, y, z, blockId); + chunk.setBlock((byte) x, (byte) y, (byte) z, blockId); } } diff --git a/src/main/java/fr/themode/minestom/instance/BlockManager.java b/src/main/java/fr/themode/minestom/instance/BlockManager.java new file mode 100644 index 000000000..07b30ab7e --- /dev/null +++ b/src/main/java/fr/themode/minestom/instance/BlockManager.java @@ -0,0 +1,19 @@ +package fr.themode.minestom.instance; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class BlockManager { + + private Map blocks = new HashMap<>(); + + public void registerBlock(String id, Supplier blocks) { + this.blocks.put(id, blocks.get()); + } + + public CustomBlock getBlock(String id) { + return this.blocks.get(id); + } + +} diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index 6c7f353c1..5df4493ec 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -1,21 +1,24 @@ package fr.themode.minestom.instance; +import fr.themode.minestom.Main; import fr.themode.minestom.entity.Entity; import fr.themode.minestom.entity.EntityCreature; import fr.themode.minestom.entity.Player; import java.util.Collections; -import java.util.HashMap; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; public class Chunk { + private static final int CHUNK_SIZE = 16 * 256 * 16; + protected Set creatures = new CopyOnWriteArraySet<>(); protected Set players = new CopyOnWriteArraySet<>(); private int chunkX, chunkZ; private Biome biome; - private HashMap blocks = new HashMap<>(); // Index/BlockID + private short[] blocksId = new short[CHUNK_SIZE]; + private String[] customBlocks = new String[CHUNK_SIZE]; public Chunk(Biome biome, int chunkX, int chunkZ) { this.biome = biome; @@ -23,18 +26,27 @@ public class Chunk { this.chunkZ = chunkZ; } - protected void setBlock(int x, int y, int z, short blockId) { - short index = (short) (x & 0x000F); - index |= (y << 4) & 0x0FF0; - index |= (z << 12) & 0xF000; - this.blocks.put(index, blockId); + protected void setBlock(byte x, byte y, byte z, short blockId) { + int index = getIndex(x, y, z); + this.blocksId[index] = blockId; + if (blockId == 0) { + this.customBlocks[index] = null; + } } - public short getBlockId(int x, int y, int z) { - short index = (short) (x & 0x000F); - index |= (y << 4) & 0x0FF0; - index |= (z << 12) & 0xF000; - return this.blocks.getOrDefault(index, (short) 0); + protected void setBlock(byte x, byte y, byte z, String blockId) { + int index = getIndex(x, y, z); + CustomBlock customBlock = Main.getBlockManager().getBlock(blockId); + this.blocksId[index] = customBlock.getType(); + this.customBlocks[index] = blockId; + } + + public short getBlockId(byte x, byte y, byte z) { + return this.blocksId[getIndex(x, y, z)]; + } + + public String getCustomBlockId(byte x, byte y, byte z) { + return this.customBlocks[getIndex(x, y, z)]; } public void addEntity(Entity entity) { @@ -65,8 +77,8 @@ public class Chunk { } } - public HashMap getBlocks() { - return blocks; + public short[] getBlocksId() { + return blocksId; } public Biome getBiome() { @@ -88,4 +100,11 @@ public class Chunk { public Set getPlayers() { return Collections.unmodifiableSet(players); } + + private int getIndex(byte x, byte y, byte z) { + short index = (short) (x & 0x000F); + index |= (y << 4) & 0x0FF0; + index |= (z << 12) & 0xF000; + return index & 0xffff; + } } diff --git a/src/main/java/fr/themode/minestom/instance/CustomBlock.java b/src/main/java/fr/themode/minestom/instance/CustomBlock.java new file mode 100644 index 000000000..b8cf21000 --- /dev/null +++ b/src/main/java/fr/themode/minestom/instance/CustomBlock.java @@ -0,0 +1,21 @@ +package fr.themode.minestom.instance; + +import fr.themode.minestom.entity.Player; + +public abstract class CustomBlock { + + private short type; + + public CustomBlock(short type) { + this.type = type; + } + + /* + Time in ms + */ + public abstract int getBreakDelay(Player player); + + public short getType() { + return type; + } +} diff --git a/src/main/java/fr/themode/minestom/instance/Instance.java b/src/main/java/fr/themode/minestom/instance/Instance.java index c38ee220a..733c7dc89 100644 --- a/src/main/java/fr/themode/minestom/instance/Instance.java +++ b/src/main/java/fr/themode/minestom/instance/Instance.java @@ -25,20 +25,32 @@ public class Instance { this.uniqueId = uniqueId; } - // TODO BlockBatch with pool public synchronized void setBlock(int x, int y, int z, short blockId) { - final int chunkX = Math.floorDiv(x, 16); - final int chunkZ = Math.floorDiv(z, 16); - Chunk chunk = getChunk(chunkX, chunkZ); - if (chunk == null) { - chunk = createChunk(Biome.VOID, chunkX, chunkZ); - } + Chunk chunk = getChunkAt(x, z); synchronized (chunk) { - chunk.setBlock(x % 16, y, z % 16, blockId); + chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); sendChunkUpdate(chunk); } } + public synchronized void setBlock(int x, int y, int z, String blockId) { + Chunk chunk = getChunkAt(x, z); + synchronized (chunk) { + chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); + sendChunkUpdate(chunk); + } + } + + public short getBlockId(int x, int y, int z) { + Chunk chunk = getChunkAt(x, z); + return chunk.getBlockId((byte) (x % 16), (byte) y, (byte) (z % 16)); + } + + public String getCustomBlockId(int x, int y, int z) { + Chunk chunk = getChunkAt(x, z); + return chunk.getCustomBlockId((byte) (x % 16), (byte) y, (byte) (z % 16)); + } + public BlockBatch createBlockBatch() { return new BlockBatch(this); } @@ -48,7 +60,7 @@ public class Instance { if (chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ) return chunk; } - return null; + return createChunk(Biome.VOID, chunkX, chunkZ); // TODO generation API } public Chunk getChunkAt(double x, double z) { diff --git a/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java b/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java new file mode 100644 index 000000000..4aab1a8e1 --- /dev/null +++ b/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java @@ -0,0 +1,16 @@ +package fr.themode.minestom.instance.demo; + +import fr.themode.minestom.entity.Player; +import fr.themode.minestom.instance.CustomBlock; + +public class StoneBlock extends CustomBlock { + + public StoneBlock() { + super((short) 1); + } + + @Override + public int getBreakDelay(Player player) { + return 750; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java index 4af48a6aa..45c2377b9 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java @@ -15,7 +15,10 @@ import fr.themode.minestom.net.ConnectionState; import fr.themode.minestom.net.packet.client.ClientPreplayPacket; import fr.themode.minestom.net.packet.server.login.JoinGamePacket; import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket; -import fr.themode.minestom.net.packet.server.play.*; +import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket; +import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket; +import fr.themode.minestom.net.packet.server.play.SpawnPlayerPacket; +import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket; import fr.themode.minestom.net.player.PlayerConnection; import fr.themode.minestom.utils.Utils; import fr.themode.minestom.world.Dimension; @@ -148,15 +151,6 @@ public class LoginStartPacket implements ClientPreplayPacket { player.openInventory(inv); inv.setItemStack(1, new ItemStack(1, (byte) 2)); inv.updateItems(); - - - EntityEffectPacket entityEffectPacket = new EntityEffectPacket(); - entityEffectPacket.entityId = player.getEntityId(); - entityEffectPacket.effectId = 4; - entityEffectPacket.amplifier = -1; - entityEffectPacket.duration = 3600; - entityEffectPacket.flags = 0; - connection.sendPacket(entityEffectPacket); } @Override diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java index ca682ad5c..9d657c84e 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java @@ -25,7 +25,7 @@ public class ClientPlayerBlockPlacementPacket implements ClientPlayPacket { int offsetY = blockFace == ClientPlayerDiggingPacket.BlockFace.BOTTOM ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.TOP ? 1 : 0; int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0; - instance.setBlock(position.getX() + offsetX, position.getY() + offsetY, position.getZ() + offsetZ, (short) 1); + instance.setBlock(position.getX() + offsetX, position.getY() + offsetY, position.getZ() + offsetZ, "stone"); player.getInventory().refreshSlot(player.getHeldSlot()); // TODO consume block in hand for survival players /*Random random = new Random(); diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerDiggingPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerDiggingPacket.java index 6069d89fc..ab817efb6 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerDiggingPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerDiggingPacket.java @@ -1,10 +1,14 @@ package fr.themode.minestom.net.packet.client.play; import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.Main; import fr.themode.minestom.entity.GameMode; import fr.themode.minestom.entity.Player; +import fr.themode.minestom.instance.CustomBlock; import fr.themode.minestom.instance.Instance; import fr.themode.minestom.net.packet.client.ClientPlayPacket; +import fr.themode.minestom.net.packet.server.play.EntityEffectPacket; +import fr.themode.minestom.net.packet.server.play.RemoveEntityEffectPacket; import fr.themode.minestom.utils.Position; import fr.themode.minestom.utils.Utils; @@ -24,19 +28,56 @@ public class ClientPlayerDiggingPacket implements ClientPlayPacket { instance.setBlock(position.getX(), position.getY(), position.getZ(), (short) 0); } } else if (player.getGameMode() == GameMode.SURVIVAL) { - player.refreshTargetBlock(position); - // TODO survival mining + Instance instance = player.getInstance(); + if (instance != null) { + String customBlockId = instance.getCustomBlockId(position.getX(), position.getY(), position.getZ()); + if (customBlockId != null) { + CustomBlock customBlock = Main.getBlockManager().getBlock(customBlockId); + player.refreshTargetBlock(customBlock, position); + addEffect(player); + } else { + player.resetTargetBlock(); + removeEffect(player); + } + } } break; case CANCELLED_DIGGING: - player.refreshTargetBlock(null); + player.sendBlockBreakAnimation(position, (byte) -1); + player.resetTargetBlock(); + removeEffect(player); break; case FINISHED_DIGGING: - player.refreshTargetBlock(null); + if (player.getCustomBlockTarget() != null) { + player.resetTargetBlock(); + removeEffect(player); + } else { + Instance instance = player.getInstance(); + if (instance != null) { + instance.setBlock(position.getX(), position.getY(), position.getZ(), (short) 0); + } + } break; } } + private void addEffect(Player player) { + EntityEffectPacket entityEffectPacket = new EntityEffectPacket(); + entityEffectPacket.entityId = player.getEntityId(); + entityEffectPacket.effectId = 4; + entityEffectPacket.amplifier = -1; + entityEffectPacket.duration = 0; + entityEffectPacket.flags = 0; + player.getPlayerConnection().sendPacket(entityEffectPacket); + } + + private void removeEffect(Player player) { + RemoveEntityEffectPacket removeEntityEffectPacket = new RemoveEntityEffectPacket(); + removeEntityEffectPacket.entityId = player.getEntityId(); + removeEntityEffectPacket.effectId = 4; + player.getPlayerConnection().sendPacket(removeEntityEffectPacket); + } + @Override public void read(Buffer buffer) { this.status = Status.values()[Utils.readVarInt(buffer)]; diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java index 9f81c3871..9ffda6b3f 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java @@ -74,11 +74,11 @@ public class ChunkDataPacket implements ServerPacket { private Short[] getSection(Chunk chunk, int section) { Short[] blocks = new Short[16 * 16 * 16]; - for (int y = 0; y < 16; y++) { - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { + for (byte y = 0; y < 16; y++) { + for (byte x = 0; x < 16; x++) { + for (byte z = 0; z < 16; z++) { int index = (((y * 16) + x) * 16) + z; - blocks[index] = chunk.getBlockId(x, y + 16 * section, z); + blocks[index] = chunk.getBlockId(x, (byte) (y + 16 * section), z); } } } diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ParticlePacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ParticlePacket.java new file mode 100644 index 000000000..719dba1a0 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ParticlePacket.java @@ -0,0 +1,36 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Utils; + +public class ParticlePacket implements ServerPacket { + + public int particleId; + public boolean longDistance; + public float x, y, z; + public float offsetX, offsetY, offsetZ; + public float particleData; + public int particleCount; + // TODO data + + @Override + public void write(Buffer buffer) { + buffer.putInt(particleId); + buffer.putBoolean(longDistance); + buffer.putFloat(x); + buffer.putFloat(y); + buffer.putFloat(z); + buffer.putFloat(offsetX); + buffer.putFloat(offsetY); + buffer.putFloat(offsetZ); + buffer.putFloat(particleData); + buffer.putInt(particleCount); + Utils.writeVarInt(buffer, 1); + } + + @Override + public int getId() { + return 0x23; + } +}