From 4f4d48df926776e6419de1b603132481890b6e6a Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Mon, 6 Jul 2020 23:20:20 +0200 Subject: [PATCH] Remove old NBT code, use Hephaistos --- build.gradle | 3 + prismarine-minecraft-data | 2 +- .../net/minestom/server/MinecraftServer.java | 25 ++ .../server/data/type/InventoryData.java | 1 + .../server/entity/damage/DamageType.java | 3 +- .../server/instance/block/CustomBlock.java | 8 +- .../server/network/packet/PacketReader.java | 18 +- .../server/network/packet/PacketWriter.java | 20 +- .../packet/server/login/JoinGamePacket.java | 44 +-- .../packet/server/play/ChunkDataPacket.java | 35 +- .../java/net/minestom/server/utils/Utils.java | 342 ++++++++++-------- .../server/utils/item/NbtReaderUtils.java | 246 ------------- .../net/minestom/server/utils/nbt/NBT.java | 18 - .../server/utils/nbt/NbtConsumer.java | 6 - .../minestom/server/utils/nbt/NbtWriter.java | 102 ------ 15 files changed, 306 insertions(+), 567 deletions(-) delete mode 100644 src/main/java/net/minestom/server/utils/item/NbtReaderUtils.java delete mode 100644 src/main/java/net/minestom/server/utils/nbt/NBT.java delete mode 100644 src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java delete mode 100644 src/main/java/net/minestom/server/utils/nbt/NbtWriter.java diff --git a/build.gradle b/build.gradle index ed7f43520..7d6a6d69b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java-library' id 'net.ltgt.apt' version '0.10' + id 'org.jetbrains.kotlin.jvm' version '1.3.72' } allprojects { @@ -71,4 +72,6 @@ dependencies { // Pathfinding implementation 'com.github.MadMartian:hydrazine-path-finding:1.1.0' + api "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + api 'com.github.jglrxavpok:Hephaistos:v1.0.3' } diff --git a/prismarine-minecraft-data b/prismarine-minecraft-data index bf55e7257..6e5241db3 160000 --- a/prismarine-minecraft-data +++ b/prismarine-minecraft-data @@ -1 +1 @@ -Subproject commit bf55e7257113471e1d6a8116e83edb6cf5011cca +Subproject commit 6e5241db3b4546242d2056b27691e9f7c11dc2d5 diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 775f67a57..13e17377f 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -9,12 +9,18 @@ import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.command.CommandManager; import net.minestom.server.data.DataManager; import net.minestom.server.entity.EntityManager; +import net.minestom.server.entity.EntityType; import net.minestom.server.entity.Player; import net.minestom.server.extras.mojangAuth.MojangCrypt; +import net.minestom.server.fluids.Fluid; import net.minestom.server.gamedata.loottables.LootTableManager; import net.minestom.server.gamedata.tags.TagManager; +import net.minestom.server.instance.Biome; import net.minestom.server.instance.InstanceManager; +import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockManager; +import net.minestom.server.item.Enchantment; +import net.minestom.server.item.Material; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.PacketProcessor; @@ -22,10 +28,14 @@ import net.minestom.server.network.PacketWriterUtils; import net.minestom.server.network.netty.NettyServer; import net.minestom.server.network.packet.server.play.PluginMessagePacket; import net.minestom.server.network.packet.server.play.ServerDifficultyPacket; +import net.minestom.server.particle.Particle; import net.minestom.server.ping.ResponseDataConsumer; +import net.minestom.server.potion.PotionType; import net.minestom.server.recipe.RecipeManager; import net.minestom.server.registry.ResourceGatherer; import net.minestom.server.scoreboard.TeamManager; +import net.minestom.server.sound.Sound; +import net.minestom.server.stat.StatisticType; import net.minestom.server.storage.StorageFolder; import net.minestom.server.storage.StorageManager; import net.minestom.server.timer.SchedulerManager; @@ -115,6 +125,21 @@ public class MinecraftServer { private static MinecraftSessionService sessionService = authService.createMinecraftSessionService(); public static MinecraftServer init() { + // warmup/force-init registries + // without this line, registry types that are not loaded explicitly will have an internal empty registry in Registries + // That can happen with PotionType for instance, if no code tries to access a PotionType field + // TODO: automate (probably with code generation) + Block.values(); + Material.values(); + PotionType.values(); + Enchantment.values(); + EntityType.values(); + Sound.values(); + Particle.values(); + StatisticType.values(); + Biome.values(); + Fluid.values(); + connectionManager = new ConnectionManager(); packetProcessor = new PacketProcessor(); packetListenerManager = new PacketListenerManager(); diff --git a/src/main/java/net/minestom/server/data/type/InventoryData.java b/src/main/java/net/minestom/server/data/type/InventoryData.java index bbb48cc5d..c9c873e8e 100644 --- a/src/main/java/net/minestom/server/data/type/InventoryData.java +++ b/src/main/java/net/minestom/server/data/type/InventoryData.java @@ -5,6 +5,7 @@ import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; import net.minestom.server.network.packet.PacketReader; import net.minestom.server.network.packet.PacketWriter; +import org.jglrxavpok.hephaistos.nbt.NBTException; public class InventoryData extends DataType { diff --git a/src/main/java/net/minestom/server/entity/damage/DamageType.java b/src/main/java/net/minestom/server/entity/damage/DamageType.java index ce6a21968..35f9df16b 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageType.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageType.java @@ -35,8 +35,7 @@ public class DamageType { } public RichMessage buildChatMessage(Player killed) { - RichMessage richMessage = RichMessage.of(ColoredText.ofFormat("{@death." + identifier + "}")) - .append(ColoredText.ofFormat(killed.getUsername())); + RichMessage richMessage = RichMessage.of(ColoredText.ofFormat("{@death." + identifier + ","+killed.getUsername()+"}")); return richMessage; } diff --git a/src/main/java/net/minestom/server/instance/block/CustomBlock.java b/src/main/java/net/minestom/server/instance/block/CustomBlock.java index b284f4ef7..f79eb5318 100644 --- a/src/main/java/net/minestom/server/instance/block/CustomBlock.java +++ b/src/main/java/net/minestom/server/instance/block/CustomBlock.java @@ -7,8 +7,8 @@ import net.minestom.server.gamedata.loottables.LootTable; import net.minestom.server.gamedata.loottables.LootTableManager; import net.minestom.server.instance.Instance; import net.minestom.server.utils.BlockPosition; -import net.minestom.server.utils.nbt.NbtWriter; import net.minestom.server.utils.time.UpdateOption; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; /** * TODO @@ -188,12 +188,14 @@ public abstract class CustomBlock { } /** - * Allows custom block to write block entity data to a given NBT compound + * Allows custom block to write block entity data to a given NBT compound. + * Used to send block entity data to the client over the network. + * Can also be used to save block entity data on disk for compatible chunk savers * * @param position position of the block * @param blockData equivalent to
instance.getBlockData(position)
*/ - public void writeBlockEntity(BlockPosition position, Data blockData, NbtWriter nbt) { + public void writeBlockEntity(BlockPosition position, Data blockData, NBTCompound nbt) { } /** diff --git a/src/main/java/net/minestom/server/network/packet/PacketReader.java b/src/main/java/net/minestom/server/network/packet/PacketReader.java index 4ad8e94a4..17fc1f0c5 100644 --- a/src/main/java/net/minestom/server/network/packet/PacketReader.java +++ b/src/main/java/net/minestom/server/network/packet/PacketReader.java @@ -6,13 +6,18 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.SerializerUtils; import net.minestom.server.utils.Utils; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTException; +import org.jglrxavpok.hephaistos.nbt.NBTReader; +import java.io.IOException; +import java.io.InputStream; import java.util.UUID; -// TODO delete -public class PacketReader { +public class PacketReader extends InputStream { private ByteBuf buffer; + private NBTReader nbtReader = new NBTReader(this, false); public PacketReader(ByteBuf buffer) { this.buffer = buffer; @@ -106,4 +111,13 @@ public class PacketReader { public ByteBuf getBuffer() { return buffer; } + + @Override + public int read() { + return readByte(); + } + + public NBT readTag() throws IOException, NBTException { + return nbtReader.read(); + } } diff --git a/src/main/java/net/minestom/server/network/packet/PacketWriter.java b/src/main/java/net/minestom/server/network/packet/PacketWriter.java index 72c2114a3..992172026 100644 --- a/src/main/java/net/minestom/server/network/packet/PacketWriter.java +++ b/src/main/java/net/minestom/server/network/packet/PacketWriter.java @@ -7,15 +7,20 @@ import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.SerializerUtils; import net.minestom.server.utils.Utils; import net.minestom.server.utils.buffer.BufferWrapper; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTWriter; +import java.io.IOException; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.UUID; import java.util.function.Consumer; -public class PacketWriter { +public class PacketWriter extends OutputStream { private ByteBuf buffer = Unpooled.buffer(); + private NBTWriter nbtWriter = new NBTWriter(this, false); public void writeBoolean(boolean b) { buffer.writeBoolean(b); @@ -128,6 +133,15 @@ public class PacketWriter { Utils.writeItemStack(this, itemStack); } + public void writeNBT(String name, NBT tag) { + try { + nbtWriter.writeNamed("", tag); + } catch (IOException e) { + // should not throw, as nbtWriter points to this PacketWriter + e.printStackTrace(); + } + } + public byte[] toByteArray() { byte[] bytes = new byte[buffer.readableBytes()]; int readerIndex = buffer.readerIndex(); @@ -135,4 +149,8 @@ public class PacketWriter { return bytes; } + @Override + public void write(int b) { + writeByte((byte) b); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java b/src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java index 96ed7dd9d..3183e6a76 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java @@ -4,11 +4,11 @@ import net.minestom.server.entity.GameMode; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; -import net.minestom.server.utils.nbt.NbtWriter; import net.minestom.server.world.Dimension; import net.minestom.server.world.LevelType; - -import static net.minestom.server.utils.nbt.NBT.*; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import org.jglrxavpok.hephaistos.nbt.NBTList; +import org.jglrxavpok.hephaistos.nbt.NBTTypes; public class JoinGamePacket implements ServerPacket { @@ -27,7 +27,6 @@ public class JoinGamePacket implements ServerPacket { @Override public void write(PacketWriter writer) { - NbtWriter nbtWriter = new NbtWriter(writer); int gameModeId = gameMode.getId(); if (gameMode.isHardcore()) gameModeId |= 8; @@ -40,23 +39,26 @@ public class JoinGamePacket implements ServerPacket { //array of worlds writer.writeVarInt(1); writer.writeSizedString(identifier); - nbtWriter.writeCompound("", (writer1) -> { - writer1.writeList("dimension", NBT_COMPOUND, 1, () -> { - writer1.writeString("name", "test:normal"); - writer1.writeFloat("ambient_light", 1F); - writer1.writeString("infiniburn", ""); - writer1.writeByte("natural", (byte) 0x01); - writer1.writeByte("has_ceiling", (byte) 0x01); - writer1.writeByte("has_skylight", (byte) 0x01); - writer1.writeByte("shrunk", (byte) 0x00); - writer1.writeByte("ultrawarm", (byte) 0x00); - writer1.writeByte("has_raids", (byte) 0x00); - writer1.writeByte("respawn_anchor_works", (byte) 0x00); - writer1.writeByte("bed_works", (byte) 0x01); - writer1.writeByte("piglin_safe", (byte) 0x01); - writer1.writeInt("logical_height", 255); - }); - }); + // TODO: modifiable + NBTCompound dimension = new NBTCompound() + .setString("name", "test:normal") + .setFloat("ambient_light", 1F) + .setString("infiniburn", "") + .setByte("natural", (byte) 0x01) + .setByte("has_ceiling", (byte) 0x01) + .setByte("has_skylight", (byte) 0x01) + .setByte("shrunk", (byte) 0x00) + .setByte("ultrawarm", (byte) 0x00) + .setByte("has_raids", (byte) 0x00) + .setByte("respawn_anchor_works", (byte) 0x00) + .setByte("bed_works", (byte) 0x01) + .setByte("piglin_safe", (byte) 0x01) + .setInt("logical_height", 255) + ; + NBTList dimensionList = new NBTList<>(NBTTypes.TAG_Compound); + dimensionList.add(dimension); + writer.writeNBT("", new NBTCompound().set("dimension", dimensionList)); + //writer.writeInt(dimension.getId()); writer.writeSizedString("test:normal"); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java index 7a30ad7a0..c6d675e50 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java @@ -15,7 +15,7 @@ import net.minestom.server.utils.Utils; import net.minestom.server.utils.buffer.BufferUtils; import net.minestom.server.utils.buffer.BufferWrapper; import net.minestom.server.utils.chunk.ChunkUtils; -import net.minestom.server.utils.nbt.NbtWriter; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Set; @@ -44,8 +44,6 @@ public class ChunkDataPacket implements ServerPacket { @Override public void write(PacketWriter writer) { - NbtWriter nbtWriter = new NbtWriter(writer); - writer.writeInt(chunkX); writer.writeInt(chunkZ); writer.writeBoolean(fullChunk); @@ -81,10 +79,11 @@ public class ChunkDataPacket implements ServerPacket { } { - nbtWriter.writeCompound("", compound -> { - compound.writeLongArray("MOTION_BLOCKING", Utils.encodeBlocks(motionBlocking, 9)); - compound.writeLongArray("WORLD_SURFACE", Utils.encodeBlocks(worldSurface, 9)); - }); + writer.writeNBT("", + new NBTCompound() + .setLongArray("MOTION_BLOCKING", Utils.encodeBlocks(motionBlocking, 9)) + .setLongArray("WORLD_SURFACE", Utils.encodeBlocks(worldSurface, 9)) + ); } // Biome data @@ -104,18 +103,18 @@ public class ChunkDataPacket implements ServerPacket { for (int index : blockEntities) { final BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ); - nbtWriter.writeCompound("", compound -> { - compound.writeDouble("x", blockPosition.getX()); - compound.writeDouble("y", blockPosition.getY()); - compound.writeDouble("z", blockPosition.getZ()); + NBTCompound nbt = new NBTCompound() + .setDouble("x", blockPosition.getX()) + .setDouble("y", blockPosition.getY()) + .setDouble("z", blockPosition.getZ()); - final short customBlockId = customBlocksId[index]; - final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); - if (customBlock != null) { - Data data = blocksData.get(index); - customBlock.writeBlockEntity(blockPosition, data, compound); - } - }); + final short customBlockId = customBlocksId[index]; + final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); + if (customBlock != null) { + Data data = blocksData.get(index); + customBlock.writeBlockEntity(blockPosition, data, nbt); + } + writer.writeNBT("", nbt); } } diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java index a8ba5be3f..dcbf5cfcd 100644 --- a/src/main/java/net/minestom/server/utils/Utils.java +++ b/src/main/java/net/minestom/server/utils/Utils.java @@ -1,23 +1,30 @@ package net.minestom.server.utils; import io.netty.buffer.ByteBuf; +import net.minestom.server.attribute.Attribute; +import net.minestom.server.attribute.AttributeOperation; import net.minestom.server.chat.ColoredText; import net.minestom.server.instance.Chunk; import net.minestom.server.item.Enchantment; import net.minestom.server.item.ItemStack; +import net.minestom.server.item.attribute.AttributeSlot; import net.minestom.server.item.attribute.ItemAttribute; import net.minestom.server.network.packet.PacketReader; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.potion.PotionType; +import net.minestom.server.registry.Registries; import net.minestom.server.utils.buffer.BufferWrapper; -import net.minestom.server.utils.item.NbtReaderUtils; -import net.minestom.server.utils.nbt.NBT; -import net.minestom.server.utils.nbt.NbtWriter; +import org.jglrxavpok.hephaistos.nbt.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.*; public class Utils { + private final static Logger LOGGER = LoggerFactory.getLogger(Utils.class); + public static int getVarIntSize(int input) { return (input & 0xFFFFFF80) == 0 ? 1 : (input & 0xFFFFC000) == 0 @@ -110,18 +117,18 @@ public class Utils { 70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE, 0, 5}; - private static void writeEnchant(NbtWriter writer, String listName, Map enchantmentMap) { - writer.writeList(listName, NBT.NBT_COMPOUND, enchantmentMap.size(), () -> { - for (Map.Entry entry : enchantmentMap.entrySet()) { - Enchantment enchantment = entry.getKey(); - short level = entry.getValue(); + private static void writeEnchant(NBTCompound nbt, String listName, Map enchantmentMap) { + NBTList enchantList = new NBTList<>(NBTTypes.TAG_Compound); + for (Map.Entry entry : enchantmentMap.entrySet()) { + Enchantment enchantment = entry.getKey(); + short level = entry.getValue(); - writer.writeShort("lvl", level); - - writer.writeString("id", "minecraft:" + enchantment.name().toLowerCase()); - - } - }); + enchantList.add(new NBTCompound() + .setShort("lvl", level) + .setString("id", "minecraft:" + enchantment.name().toLowerCase()) + ); + } + nbt.set(listName, enchantList); } public static ItemStack readItemStack(PacketReader reader) { @@ -141,18 +148,91 @@ public class Utils { ItemStack item = new ItemStack((short) id, count); - byte nbt = reader.readByte(); // Should be compound start (0x0A) or 0 if there isn't NBT data + try { + NBT itemNBT = reader.readTag(); + if(itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data + NBTCompound nbt = (NBTCompound) itemNBT; + if(nbt.containsKey("Damage")) item.setDamage(nbt.getShort("Damage")); + if(nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getInt("Unbreakable") == 1); + if(nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags")); + if(nbt.containsKey("Potion")) item.addPotionType(Registries.getPotionType(nbt.getString("Potion"))); + if(nbt.containsKey("display")) { + NBTCompound display = nbt.getCompound("display"); + if(display.containsKey("Name")) item.setDisplayName(ColoredText.of(display.getString("Name"))); + if(display.containsKey("Lore")) { + NBTList loreList = display.getList("Lore"); + ArrayList lore = new ArrayList<>(); + for(NBTString s : loreList) { + lore.add(ColoredText.of(s.getValue())); + } + item.setLore(lore); + } + } - if (nbt == 0x00) { - return item; - } else if (nbt == 0x0A) { - reader.readShort(); // Ignored, should be empty (main compound name) - NbtReaderUtils.readItemStackNBT(reader, item); + if(nbt.containsKey("Enchantments")) { + loadEnchantments(nbt.getList("Enchantments"), item::setEnchantment); + } + if(nbt.containsKey("StoredEnchantments")) { + loadEnchantments(nbt.getList("StoredEnchantments"), item::setStoredEnchantment); + } + if(nbt.containsKey("AttributeModifiers")) { + NBTList attributes = nbt.getList("AttributeModifiers"); + for (NBTCompound attributeNBT : attributes) { + // TODO: 1.16 changed how UUIDs are stored, is this part affected? + long uuidMost = attributeNBT.getLong("UUIDMost"); + long uuidLeast = attributeNBT.getLong("UUIDLeast"); + UUID uuid = new UUID(uuidMost, uuidLeast); + double value = attributeNBT.getDouble("Amount"); + String slot = attributeNBT.getString("Slot"); + String attributeName = attributeNBT.getString("AttributeName"); + int operation = attributeNBT.getInt("Operation"); + String name = attributeNBT.getString("Name"); + + final Attribute attribute = Attribute.fromKey(attributeName); + // Wrong attribute name, stop here + if (attribute == null) + break; + final AttributeOperation attributeOperation = AttributeOperation.byId(operation); + // Wrong attribute operation, stop here + if (attributeOperation == null) + break; + final AttributeSlot attributeSlot = AttributeSlot.valueOf(slot.toUpperCase()); + // Wrong attribute slot, stop here + if (attributeSlot == null) + break; + + // Add attribute + final ItemAttribute itemAttribute = + new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot); + item.addAttribute(itemAttribute); + } + } + } + } catch (IOException | NBTException e) { + e.printStackTrace(); } return item; } + @FunctionalInterface + private interface EnchantmentSetter { + void applyEnchantment(Enchantment name, short level); + } + + private static void loadEnchantments(NBTList enchantments, EnchantmentSetter setter) { + for(NBTCompound enchantment : enchantments) { + short level = enchantment.getShort("lvl"); + String id = enchantment.getString("id"); + Enchantment enchant = Registries.getEnchantment(id); + if(enchant != null) { + setter.applyEnchantment(enchant, level); + } else { + LOGGER.warn("Unknown enchantment type: "+id); + } + } + } + public static void writeBlocks(BufferWrapper buffer, short[] blocksId, int bitsPerEntry) { short count = 0; for (short id : blocksId) @@ -178,28 +258,6 @@ public class Utils { } } - /*public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) { - long maxEntryValue = (1L << bitsPerEntry) - 1; - - int length = (int) Math.ceil(blocks.length * bitsPerEntry / 64.0); - long[] data = new long[length]; - - for (int index = 0; index < blocks.length; index++) { - int value = blocks[index]; - int bitIndex = index * bitsPerEntry; - int startIndex = bitIndex / 64; - int endIndex = ((index + 1) * bitsPerEntry - 1) / 64; - int startBitSubIndex = bitIndex % 64; - data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex; - if (startIndex != endIndex) { - int endBitSubIndex = 64 - startBitSubIndex; - data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex; - } - } - - return data; - }*/ - public static void writeItemStack(PacketWriter packet, ItemStack itemStack) { if (itemStack == null || itemStack.isAir()) { packet.writeBoolean(false); @@ -209,126 +267,116 @@ public class Utils { packet.writeByte(itemStack.getAmount()); if (!itemStack.hasNbtTag()) { - packet.writeByte((byte) 0x00); // No nbt + packet.writeByte((byte) NBTTypes.TAG_End); // No nbt return; } - NbtWriter mainWriter = new NbtWriter(packet); + NBTCompound itemNBT = new NBTCompound(); - mainWriter.writeCompound("", writer -> { - // Unbreakable - if (itemStack.isUnbreakable()) { - writer.writeInt("Unbreakable", 1); + // Unbreakable + if (itemStack.isUnbreakable()) { + itemNBT.setInt("Unbreakable", 1); + } + + // Start damage + { + itemNBT.setShort("Damage", itemStack.getDamage()); + } + // End damage + + // Display + boolean hasDisplayName = itemStack.hasDisplayName(); + boolean hasLore = itemStack.hasLore(); + + if (hasDisplayName || hasLore) { + NBTCompound displayNBT = new NBTCompound(); + if (hasDisplayName) { + final String name = itemStack.getDisplayName().toString(); + displayNBT.setString("Name", name); } - // Start damage - { - writer.writeShort("Damage", itemStack.getDamage()); - } - // End damage + if (hasLore) { + final ArrayList lore = itemStack.getLore(); - // Display - boolean hasDisplayName = itemStack.hasDisplayName(); - boolean hasLore = itemStack.hasLore(); - - if (hasDisplayName || hasLore) { - writer.writeCompound("display", displayWriter -> { - if (hasDisplayName) { - final String name = itemStack.getDisplayName().toString(); - displayWriter.writeString("Name", name); - } - - if (hasLore) { - final ArrayList lore = itemStack.getLore(); - - displayWriter.writeList("Lore", NBT.NBT_STRING, lore.size(), () -> { - for (ColoredText line : lore) { - packet.writeShortSizedString(line.toString()); - } - }); - - } - }); - } - // End display - - // Start enchantment - { - Map enchantmentMap = itemStack.getEnchantmentMap(); - if (!enchantmentMap.isEmpty()) { - writeEnchant(writer, "Enchantments", enchantmentMap); + final NBTList loreNBT = new NBTList<>(NBTTypes.TAG_String); + for (ColoredText line : lore) { + loreNBT.add(new NBTString(line.toString())); } + displayNBT.set("Lore", loreNBT); + } - Map storedEnchantmentMap = itemStack.getStoredEnchantmentMap(); - if (!storedEnchantmentMap.isEmpty()) { - writeEnchant(writer, "StoredEnchantments", storedEnchantmentMap); + itemNBT.set("display", displayNBT); + } + // End display + + // Start enchantment + { + Map enchantmentMap = itemStack.getEnchantmentMap(); + if (!enchantmentMap.isEmpty()) { + writeEnchant(itemNBT, "Enchantments", enchantmentMap); + } + + Map storedEnchantmentMap = itemStack.getStoredEnchantmentMap(); + if (!storedEnchantmentMap.isEmpty()) { + writeEnchant(itemNBT, "StoredEnchantments", storedEnchantmentMap); + } + } + // End enchantment + + // Start attribute + { + List itemAttributes = itemStack.getAttributes(); + if (!itemAttributes.isEmpty()) { + NBTList attributesNBT = new NBTList<>(NBTTypes.TAG_Compound); + + for (ItemAttribute itemAttribute : itemAttributes) { + UUID uuid = itemAttribute.getUuid(); + + attributesNBT.add( + new NBTCompound() + .setLong("UUIDMost", uuid.getMostSignificantBits()) + .setLong("UUIDLeast", uuid.getLeastSignificantBits()) + .setDouble("Amount", itemAttribute.getValue()) + .setString("Slot", itemAttribute.getSlot().name().toLowerCase()) + .setString("itemAttribute", itemAttribute.getAttribute().getKey()) + .setInt("Operation", itemAttribute.getOperation().getId()) + .setString("Name", itemAttribute.getInternalName()) + ); + } + itemNBT.set("AttributeModifiers", attributesNBT); + } + } + // End attribute + + // Start potion + { + Set potionTypes = itemStack.getPotionTypes(); + if (!potionTypes.isEmpty()) { + for (PotionType potionType : potionTypes) { + itemNBT.setString("Potion", potionType.getNamespaceID()); } } - // End enchantment + } + // End potion - // Start attribute - { - List itemAttributes = itemStack.getAttributes(); - if (!itemAttributes.isEmpty()) { - packet.writeByte((byte) 0x09); // Type id (list) - packet.writeShortSizedString("AttributeModifiers"); - - packet.writeByte((byte) 0x0A); // Compound - packet.writeInt(itemAttributes.size()); - - for (ItemAttribute itemAttribute : itemAttributes) { - UUID uuid = itemAttribute.getUuid(); - - writer.writeLong("UUIDMost", uuid.getMostSignificantBits()); - - writer.writeLong("UUIDLeast", uuid.getLeastSignificantBits()); - - writer.writeDouble("Amount", itemAttribute.getValue()); - - writer.writeString("Slot", itemAttribute.getSlot().name().toLowerCase()); - - writer.writeString("itemAttribute", itemAttribute.getAttribute().getKey()); - - writer.writeInt("Operation", itemAttribute.getOperation().getId()); - - writer.writeString("Name", itemAttribute.getInternalName()); - } - packet.writeByte((byte) 0x00); // End compound - } + // Start hide flags + { + int hideFlag = itemStack.getHideFlag(); + if (hideFlag != 0) { + itemNBT.setInt("HideFlags", hideFlag); } - // End attribute + } + // End hide flags - // Start potion - { - Set potionTypes = itemStack.getPotionTypes(); - if (!potionTypes.isEmpty()) { - for (PotionType potionType : potionTypes) { - packet.writeByte((byte) 0x08); // type id (string) - packet.writeShortSizedString("Potion"); - packet.writeShortSizedString("minecraft:" + potionType.name().toLowerCase()); - } - } + // Start custom model data + { + int customModelData = itemStack.getCustomModelData(); + if (customModelData != 0) { + itemNBT.setInt("CustomModelData", customModelData); } - // End potion - - // Start hide flags - { - int hideFlag = itemStack.getHideFlag(); - if (hideFlag != 0) { - writer.writeInt("HideFlags", hideFlag); - } - } - // End hide flags - - // Start custom model data - { - int customModelData = itemStack.getCustomModelData(); - if (customModelData != 0) { - writer.writeInt("CustomModelData", customModelData); - } - } - // End custom model data - }); + } + // End custom model data + packet.writeNBT("", itemNBT); } } diff --git a/src/main/java/net/minestom/server/utils/item/NbtReaderUtils.java b/src/main/java/net/minestom/server/utils/item/NbtReaderUtils.java deleted file mode 100644 index 4ec1e4f83..000000000 --- a/src/main/java/net/minestom/server/utils/item/NbtReaderUtils.java +++ /dev/null @@ -1,246 +0,0 @@ -package net.minestom.server.utils.item; - -import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.AttributeOperation; -import net.minestom.server.chat.ChatParser; -import net.minestom.server.chat.ColoredText; -import net.minestom.server.item.Enchantment; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.attribute.AttributeSlot; -import net.minestom.server.item.attribute.ItemAttribute; -import net.minestom.server.network.packet.PacketReader; -import net.minestom.server.potion.PotionType; -import net.minestom.server.registry.Registries; - -import java.util.ArrayList; -import java.util.UUID; - -public class NbtReaderUtils { - - public static void readItemStackNBT(PacketReader reader, ItemStack item) { - - byte typeId = reader.readByte(); - - //System.out.println("DEBUG TYPE: " + typeId); - switch (typeId) { - case 0x00: // TAG_End - // End of item NBT - return; - case 0x01: // TAG_Byte - - break; - case 0x02: // TAG_Short - String shortName = reader.readShortSizedString(); - - // Damage NBT - if (shortName.equals("Damage")) { - short damage = reader.readShort(); - item.setDamage(damage); - readItemStackNBT(reader, item); - } - break; - case 0x03: // TAG_Int - String intName = reader.readShortSizedString(); - - // Damage - if (intName.equals("Damage")) { - int damage = reader.readInteger(); - //item.setDamage(damage); - // TODO short vs int damage - readItemStackNBT(reader, item); - } - - // Unbreakable - if (intName.equals("Unbreakable")) { - int value = reader.readInteger(); - item.setUnbreakable(value == 1); - readItemStackNBT(reader, item); - } - - if (intName.equals("HideFlags")) { - int flag = reader.readInteger(); - item.setHideFlag(flag); - readItemStackNBT(reader, item); - } - - break; - case 0x04: // TAG_Long - - break; - case 0x05: // TAG_Float - - break; - case 0x06: // TAG_Double - - break; - case 0x07: // TAG_Byte_Array - - break; - case 0x08: // TAG_String - String stringName = reader.readShortSizedString(); - - if (stringName.equals("Potion")) { - String potionId = reader.readShortSizedString(); - PotionType potionType = Registries.getPotionType(potionId); - - item.addPotionType(potionType); - - readItemStackNBT(reader, item); - } - break; - case 0x09: // TAG_List - - String listName = reader.readShortSizedString(); - - final boolean isEnchantment = listName.equals("Enchantments"); - final boolean isStoredEnchantment = listName.equals("StoredEnchantments"); - if (isEnchantment || isStoredEnchantment) { - reader.readByte(); // Should be a compound (0x0A) - int size = reader.readInteger(); // Enchantments count - - for (int i = 0; i < size; i++) { - reader.readByte(); // Type id (short) - reader.readShortSizedString(); // Constant "lvl" - short lvl = reader.readShort(); - - reader.readByte(); // Type id (string) - reader.readShortSizedString(); // Constant "id" - String id = reader.readShortSizedString(); - - // Convert id - Enchantment enchantment = Registries.getEnchantment(id); - - if (isEnchantment) { - item.setEnchantment(enchantment, lvl); - } else if (isStoredEnchantment) { - item.setStoredEnchantment(enchantment, lvl); - } - } - - reader.readByte(); // Compound end - - readItemStackNBT(reader, item); - - } - - if (listName.equals("AttributeModifiers")) { - reader.readByte(); // Should be a compound (0x0A); - int size = reader.readInteger(); // Attributes count - for (int i = 0; i < size; i++) { - reader.readByte(); // Type id (long) - reader.readShortSizedString(); // Constant "UUIDMost" - long uuidMost = reader.readLong(); - - reader.readByte(); // Type id (long) - reader.readShortSizedString(); // Constant "UUIDLeast" - long uuidLeast = reader.readLong(); - - final UUID uuid = new UUID(uuidMost, uuidLeast); - - reader.readByte(); // Type id (double) - reader.readShortSizedString(); // Constant "Amount" - final double value = reader.readDouble(); - - reader.readByte(); // Type id (string) - reader.readShortSizedString(); // Constant "Slot" - final String slot = reader.readShortSizedString(); - - reader.readByte(); // Type id (string) - reader.readShortSizedString(); // Constant "AttributeName" - final String attributeName = reader.readShortSizedString(); - - reader.readByte(); // Type id (int) - reader.readShortSizedString(); // Constant "Operation" - final int operation = reader.readInteger(); - - reader.readByte(); // Type id (string) - reader.readShortSizedString(); // Constant "Name" - final String name = reader.readShortSizedString(); - - final Attribute attribute = Attribute.fromKey(attributeName); - // Wrong attribute name, stop here - if (attribute == null) - break; - final AttributeOperation attributeOperation = AttributeOperation.byId(operation); - // Wrong attribute operation, stop here - if (attributeOperation == null) - break; - final AttributeSlot attributeSlot = AttributeSlot.valueOf(slot.toUpperCase()); - // Wrong attribute slot, stop here - if (attributeSlot == null) - break; - - // Add attribute - final ItemAttribute itemAttribute = - new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot); - item.addAttribute(itemAttribute); - } - - reader.readByte(); // Compound end - - readItemStackNBT(reader, item); - } - - break; - case 0x0A: // TAG_Compound - - String compoundName = reader.readShortSizedString(); - - // Display Compound - if (compoundName.equals("display")) { - readItemStackDisplayNBT(reader, item); - } - - break; - } - } - - public static void readItemStackDisplayNBT(PacketReader reader, ItemStack item) { - byte typeId = reader.readByte(); - - switch (typeId) { - case 0x00: // TAG_End - // End of the display compound - readItemStackNBT(reader, item); - break; - case 0x08: // TAG_String - - String stringName = reader.readShortSizedString(); - - if (stringName.equals("Name")) { - String jsonDisplayName = reader.readShortSizedString(); - ColoredText displayName = ChatParser.toColoredText(jsonDisplayName); - - item.setDisplayName(displayName); - readItemStackDisplayNBT(reader, item); - } - break; - case 0x09: // TAG_List - - String listName = reader.readShortSizedString(); - - if (listName.equals("Lore")) { - reader.readByte(); // lore type, should always be 0x08 (TAG_String) - - int size = reader.readInteger(); - ArrayList lore = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - String string = reader.readShortSizedString(); - ColoredText text = ChatParser.toColoredText(string); - - lore.add(text); - if (lore.size() == size) { - item.setLore(lore); - } - - if (i == size - 1) { // Last iteration - readItemStackDisplayNBT(reader, item); - } - } - } - - break; - } - } - -} diff --git a/src/main/java/net/minestom/server/utils/nbt/NBT.java b/src/main/java/net/minestom/server/utils/nbt/NBT.java deleted file mode 100644 index a5b1b5f91..000000000 --- a/src/main/java/net/minestom/server/utils/nbt/NBT.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.minestom.server.utils.nbt; - -public class NBT { - - public static final byte NBT_BYTE = 0x01; - public static final byte NBT_SHORT = 0x02; - public static final byte NBT_INT = 0x03; - public static final byte NBT_LONG = 0x04; - public static final byte NBT_FLOAT = 0x05; - public static final byte NBT_DOUBLE = 0x06; - public static final byte NBT_BYTE_ARRAY = 0x07; - public static final byte NBT_STRING = 0x08; - public static final byte NBT_LIST = 0x09; - public static final byte NBT_COMPOUND = 0x0A; - public static final byte NBT_INT_ARRAY = 0x0B; - public static final byte NBT_LONG_ARRAY = 0x0C; - -} diff --git a/src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java b/src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java deleted file mode 100644 index 008eaacd0..000000000 --- a/src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.minestom.server.utils.nbt; - -@FunctionalInterface -public interface NbtConsumer { - void accept(NbtWriter writer); -} diff --git a/src/main/java/net/minestom/server/utils/nbt/NbtWriter.java b/src/main/java/net/minestom/server/utils/nbt/NbtWriter.java deleted file mode 100644 index 2c009a6d5..000000000 --- a/src/main/java/net/minestom/server/utils/nbt/NbtWriter.java +++ /dev/null @@ -1,102 +0,0 @@ -package net.minestom.server.utils.nbt; - -import net.minestom.server.network.packet.PacketWriter; -import net.minestom.server.utils.MathUtils; -import net.minestom.server.utils.validate.Check; - -import static net.minestom.server.utils.nbt.NBT.*; - -public class NbtWriter { - - private PacketWriter packet; - - public NbtWriter(PacketWriter packet) { - this.packet = packet; - } - - public void writeByte(String name, byte value) { - writeHeader(NBT_BYTE, name); - packet.writeByte(value); - } - - public void writeShort(String name, short value) { - writeHeader(NBT_SHORT, name); - packet.writeShort(value); - } - - public void writeInt(String name, int value) { - writeHeader(NBT_INT, name); - packet.writeInt(value); - } - - public void writeLong(String name, long value) { - writeHeader(NBT_LONG, name); - packet.writeLong(value); - } - - public void writeFloat(String name, float value) { - writeHeader(NBT_FLOAT, name); - packet.writeFloat(value); - } - - public void writeDouble(String name, double value) { - writeHeader(NBT_DOUBLE, name); - packet.writeDouble(value); - } - - public void writeByteArray(String name, byte[] value) { - writeHeader(NBT_BYTE_ARRAY, name); - packet.writeInt(value.length); - for (byte val : value) { - packet.writeByte(val); - } - } - - public void writeString(String name, String value) { - writeHeader(NBT_STRING, name); - packet.writeShortSizedString(value); - } - - public void writeList(String name, byte type, int size, Runnable callback) { - writeHeader(NBT_LIST, name); - packet.writeByte(type); - packet.writeInt(size); - callback.run(); - if (type == NBT_COMPOUND) - packet.writeByte((byte) 0x00); // End compount - } - - public void writeCompound(String name, NbtConsumer consumer) { - writeHeader(NBT_COMPOUND, name); - consumer.accept(this); - packet.writeByte((byte) 0x00); // End compound - } - - public void writeIntArray(String name, int[] value) { - writeHeader(NBT_INT_ARRAY, name); - packet.writeInt(value.length); - for (int val : value) { - packet.writeInt(val); - } - } - - public void writeLongArray(String name, long[] value) { - writeHeader(NBT_LONG_ARRAY, name); - packet.writeInt(value.length); - for (long val : value) { - packet.writeLong(val); - } - } - - private void writeHeader(byte type, String name) { - Check.argCondition(!MathUtils.isBetween(type, NBT_BYTE, NBT_LONG_ARRAY), - "The NbtTag type " + type + " is not valid"); - Check.notNull(name, "The NbtTag name cannot be null"); - packet.writeByte(type); - packet.writeShortSizedString(name); - } - - public PacketWriter getPacketWriter() { - return packet; - } -}