From c6b1ded750af4df52b1d0dcb49e637df4a276055 Mon Sep 17 00:00:00 2001 From: mworzala Date: Wed, 10 Apr 2024 08:34:12 -0400 Subject: [PATCH] chore: basic nbt reader/writer for protocol while waiting for adventure --- .../server/network/NetworkBuffer.java | 8 +-- .../server/network/NetworkBufferTypeImpl.java | 55 ++++++++----------- .../minestom/server/tag/TagNbtSeparator.java | 6 +- .../server/utils/nbt/BinaryTagReader.java | 37 +++++++++++++ .../{NBTUtils.java => nbt/BinaryTagUtil.java} | 6 +- .../server/utils/nbt/BinaryTagWriter.java | 40 ++++++++++++++ 6 files changed, 111 insertions(+), 41 deletions(-) create mode 100644 src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java rename src/main/java/net/minestom/server/utils/{NBTUtils.java => nbt/BinaryTagUtil.java} (95%) create mode 100644 src/main/java/net/minestom/server/utils/nbt/BinaryTagWriter.java diff --git a/src/main/java/net/minestom/server/network/NetworkBuffer.java b/src/main/java/net/minestom/server/network/NetworkBuffer.java index cc8b5a3b2..047bb40b3 100644 --- a/src/main/java/net/minestom/server/network/NetworkBuffer.java +++ b/src/main/java/net/minestom/server/network/NetworkBuffer.java @@ -12,13 +12,13 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.network.packet.server.play.data.WorldPos; import net.minestom.server.particle.Particle; import net.minestom.server.utils.Direction; +import net.minestom.server.utils.nbt.BinaryTagReader; +import net.minestom.server.utils.nbt.BinaryTagWriter; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.DataInput; -import java.io.DataOutput; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.*; @@ -81,8 +81,8 @@ public final class NetworkBuffer { int writeIndex; int readIndex; - DataOutput nbtWriter; - DataInput nbtReader; + BinaryTagWriter nbtWriter; + BinaryTagReader nbtReader; public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) { this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN); diff --git a/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java b/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java index fe6b4068f..3a2e2e4d0 100644 --- a/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java +++ b/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java @@ -1,5 +1,8 @@ package net.minestom.server.network; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.EndBinaryTag; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer; @@ -10,13 +13,12 @@ import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.data.WorldPos; import net.minestom.server.particle.Particle; import net.minestom.server.particle.data.ParticleData; +import net.minestom.server.utils.nbt.BinaryTagReader; +import net.minestom.server.utils.nbt.BinaryTagWriter; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.*; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.nio.charset.StandardCharsets; import java.util.UUID; @@ -283,37 +285,31 @@ interface NetworkBufferTypeImpl extends NetworkBuffer.Type { } } - record NbtType() implements NetworkBufferTypeImpl { + record NbtType() implements NetworkBufferTypeImpl { @Override - public void write(@NotNull NetworkBuffer buffer, org.jglrxavpok.hephaistos.nbt.NBT value) { - NBTWriter nbtWriter = buffer.nbtWriter; + public void write(@NotNull NetworkBuffer buffer, BinaryTag value) { + BinaryTagWriter nbtWriter = buffer.nbtWriter; if (nbtWriter == null) { - nbtWriter = new NBTWriter(new OutputStream() { + nbtWriter = new BinaryTagWriter(new DataOutputStream(new OutputStream() { @Override public void write(int b) { buffer.write(BYTE, (byte) b); } - }, CompressedProcesser.NONE); + })); buffer.nbtWriter = nbtWriter; } try { - if (value == NBTEnd.INSTANCE) { - // Kotlin - https://discord.com/channels/706185253441634317/706186227493109860/1163703658341478462 - buffer.write(BYTE, (byte) NBTType.TAG_End.getOrdinal()); - } else { - buffer.write(BYTE, (byte) value.getID().getOrdinal()); - nbtWriter.writeRaw(value); - } + nbtWriter.writeNameless(value); } catch (IOException e) { throw new RuntimeException(e); } } @Override - public org.jglrxavpok.hephaistos.nbt.NBT read(@NotNull NetworkBuffer buffer) { - NBTReader nbtReader = buffer.nbtReader; + public BinaryTag read(@NotNull NetworkBuffer buffer) { + BinaryTagReader nbtReader = buffer.nbtReader; if (nbtReader == null) { - nbtReader = new NBTReader(new InputStream() { + nbtReader = new BinaryTagReader(new DataInputStream(new InputStream() { @Override public int read() { return buffer.read(BYTE) & 0xFF; @@ -323,15 +319,12 @@ interface NetworkBufferTypeImpl extends NetworkBuffer.Type { public int available() { return buffer.readableBytes(); } - }, CompressedProcesser.NONE); + })); buffer.nbtReader = nbtReader; } try { - byte tagId = buffer.read(BYTE); - if (tagId == NBTType.TAG_End.getOrdinal()) - return NBTEnd.INSTANCE; - return nbtReader.readRaw(tagId); - } catch (IOException | NBTException e) { + return nbtReader.readNameless(); + } catch (IOException e) { throw new RuntimeException(e); } } @@ -362,13 +355,13 @@ interface NetworkBufferTypeImpl extends NetworkBuffer.Type { record ComponentType() implements NetworkBufferTypeImpl { @Override public void write(@NotNull NetworkBuffer buffer, Component value) { - final NBT nbt = NbtComponentSerializer.nbt().serialize(value); + final BinaryTag nbt = NbtComponentSerializer.nbt().serialize(value); buffer.write(NBT, nbt); } @Override public Component read(@NotNull NetworkBuffer buffer) { - final NBT nbt = buffer.read(NBT); + final BinaryTag nbt = buffer.read(NBT); return NbtComponentSerializer.nbt().deserialize(nbt); } } @@ -413,8 +406,8 @@ interface NetworkBufferTypeImpl extends NetworkBuffer.Type { buffer.write(VAR_INT, value.material().id()); buffer.write(BYTE, (byte) value.amount()); // Vanilla does not write an empty object, just an end tag. - NBTCompound nbt = value.meta().toNBT(); - buffer.write(NBT, nbt.isEmpty() ? NBTEnd.INSTANCE : nbt); + CompoundBinaryTag nbt = value.meta().toNBT(); + buffer.write(NBT, nbt.size() == 0 ? EndBinaryTag.endBinaryTag() : nbt); } @Override @@ -427,8 +420,8 @@ interface NetworkBufferTypeImpl extends NetworkBuffer.Type { if (material == null) throw new RuntimeException("Unknown material id: " + id); final int amount = buffer.read(BYTE); - final NBT nbt = buffer.read(NBT); - if (!(nbt instanceof NBTCompound compound)) { + final BinaryTag nbt = buffer.read(NBT); + if (!(nbt instanceof CompoundBinaryTag compound)) { return ItemStack.of(material, amount); } return ItemStack.fromNBT(material, compound, amount); diff --git a/src/main/java/net/minestom/server/tag/TagNbtSeparator.java b/src/main/java/net/minestom/server/tag/TagNbtSeparator.java index f6e43cfd6..83ea125f1 100644 --- a/src/main/java/net/minestom/server/tag/TagNbtSeparator.java +++ b/src/main/java/net/minestom/server/tag/TagNbtSeparator.java @@ -1,7 +1,7 @@ package net.minestom.server.tag; import net.kyori.adventure.nbt.*; -import net.minestom.server.utils.NBTUtils; +import net.minestom.server.utils.nbt.BinaryTagUtil; import java.util.ArrayList; import java.util.List; @@ -51,7 +51,7 @@ final class TagNbtSeparator { var tagFunction = SUPPORTED_TYPES.get(nbt.type()); if (tagFunction != null) { Tag tag = tagFunction.apply(key); - consumer.accept(makeEntry(path, tag, NBTUtils.nbtValueFromTag(nbt))); + consumer.accept(makeEntry(path, tag, BinaryTagUtil.nbtValueFromTag(nbt))); } else if (nbt instanceof CompoundBinaryTag nbtCompound) { for (var ent : nbtCompound) { var newPath = new ArrayList<>(path); @@ -68,7 +68,7 @@ final class TagNbtSeparator { var tag = tagFunction.apply(key).list(); Object[] values = new Object[nbtList.size()]; for (int i = 0; i < values.length; i++) { - values[i] = NBTUtils.nbtValueFromTag(nbtList.get(i)); + values[i] = BinaryTagUtil.nbtValueFromTag(nbtList.get(i)); } consumer.accept(makeEntry(path, Tag.class.cast(tag), List.of(values))); } catch (Exception e) { diff --git a/src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java b/src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java new file mode 100644 index 000000000..8a68021ad --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java @@ -0,0 +1,37 @@ +package net.minestom.server.utils.nbt; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagType; +import net.kyori.adventure.nbt.BinaryTagTypes; +import org.jetbrains.annotations.NotNull; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Map; + +// Based on net.kyori.adventure.nbt.BinaryTagReaderImpl licensed under the MIT license. +// https://github.com/KyoriPowered/adventure/blob/main/4/nbt/src/main/java/net/kyori/adventure/nbt/BinaryTagReaderImpl.java +public class BinaryTagReader { + + static { + BinaryTagTypes.COMPOUND.id(); // Force initialization + } + + private final DataInput input; + + public BinaryTagReader(@NotNull DataInput input) { + this.input = input; + } + + public @NotNull BinaryTag readNameless() throws IOException { + BinaryTagType type = BinaryTagUtil.nbtTypeFromId(input.readByte()); + return type.read(input); + } + + public @NotNull Map.Entry readNamed() throws IOException { + BinaryTagType type = BinaryTagUtil.nbtTypeFromId(input.readByte()); + String name = input.readUTF(); + return Map.entry(name, type.read(input)); + } + +} diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/nbt/BinaryTagUtil.java similarity index 95% rename from src/main/java/net/minestom/server/utils/NBTUtils.java rename to src/main/java/net/minestom/server/utils/nbt/BinaryTagUtil.java index fd07f7dc7..d61c1074f 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/nbt/BinaryTagUtil.java @@ -1,4 +1,4 @@ -package net.minestom.server.utils; +package net.minestom.server.utils.nbt; import net.kyori.adventure.nbt.*; import net.minestom.server.utils.validate.Check; @@ -6,7 +6,7 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @ApiStatus.Internal -public final class NBTUtils { +public final class BinaryTagUtil { private static final BinaryTagType[] TYPES = new BinaryTagType[]{ BinaryTagTypes.END, BinaryTagTypes.BYTE, @@ -54,6 +54,6 @@ public final class NBTUtils { } } - private NBTUtils() { + private BinaryTagUtil() { } } diff --git a/src/main/java/net/minestom/server/utils/nbt/BinaryTagWriter.java b/src/main/java/net/minestom/server/utils/nbt/BinaryTagWriter.java new file mode 100644 index 000000000..7690d4de0 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/BinaryTagWriter.java @@ -0,0 +1,40 @@ +package net.minestom.server.utils.nbt; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagType; +import net.kyori.adventure.nbt.BinaryTagTypes; +import org.jetbrains.annotations.NotNull; + +import java.io.DataOutput; +import java.io.IOException; + +// Based on net.kyori.adventure.nbt.BinaryTagWriterImpl licensed under the MIT license. +// https://github.com/KyoriPowered/adventure/blob/main/4/nbt/src/main/java/net/kyori/adventure/nbt/BinaryTagWriterImpl.java +public class BinaryTagWriter { + + static { + BinaryTagTypes.COMPOUND.id(); // Force initialization + } + + private final DataOutput output; + + public BinaryTagWriter(@NotNull DataOutput output) { + this.output = output; + } + + public void writeNameless(@NotNull BinaryTag tag) throws IOException { + //noinspection unchecked + BinaryTagType type = (BinaryTagType) tag.type(); + output.writeByte(type.id()); + type.write(tag, output); + } + + public void readNamed(@NotNull String name, @NotNull BinaryTag tag) throws IOException { + //noinspection unchecked + BinaryTagType type = (BinaryTagType) tag.type(); + output.writeByte(type.id()); + output.writeUTF(name); + type.write(tag, output); + } + +}