chore: basic nbt reader/writer for protocol while waiting for adventure

This commit is contained in:
mworzala 2024-04-10 08:34:12 -04:00
parent 687968eea0
commit c6b1ded750
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
6 changed files with 111 additions and 41 deletions

View File

@ -12,13 +12,13 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.play.data.WorldPos; import net.minestom.server.network.packet.server.play.data.WorldPos;
import net.minestom.server.particle.Particle; import net.minestom.server.particle.Particle;
import net.minestom.server.utils.Direction; 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 net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.*; import java.util.*;
@ -81,8 +81,8 @@ public final class NetworkBuffer {
int writeIndex; int writeIndex;
int readIndex; int readIndex;
DataOutput nbtWriter; BinaryTagWriter nbtWriter;
DataInput nbtReader; BinaryTagReader nbtReader;
public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) { public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) {
this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN); this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN);

View File

@ -1,5 +1,8 @@
package net.minestom.server.network; 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.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer; 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.network.packet.server.play.data.WorldPos;
import net.minestom.server.particle.Particle; import net.minestom.server.particle.Particle;
import net.minestom.server.particle.data.ParticleData; 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 net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.*;
import java.io.IOException; import java.io.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.UUID; import java.util.UUID;
@ -283,37 +285,31 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
} }
} }
record NbtType() implements NetworkBufferTypeImpl<NBT> { record NbtType() implements NetworkBufferTypeImpl<BinaryTag> {
@Override @Override
public void write(@NotNull NetworkBuffer buffer, org.jglrxavpok.hephaistos.nbt.NBT value) { public void write(@NotNull NetworkBuffer buffer, BinaryTag value) {
NBTWriter nbtWriter = buffer.nbtWriter; BinaryTagWriter nbtWriter = buffer.nbtWriter;
if (nbtWriter == null) { if (nbtWriter == null) {
nbtWriter = new NBTWriter(new OutputStream() { nbtWriter = new BinaryTagWriter(new DataOutputStream(new OutputStream() {
@Override @Override
public void write(int b) { public void write(int b) {
buffer.write(BYTE, (byte) b); buffer.write(BYTE, (byte) b);
} }
}, CompressedProcesser.NONE); }));
buffer.nbtWriter = nbtWriter; buffer.nbtWriter = nbtWriter;
} }
try { try {
if (value == NBTEnd.INSTANCE) { nbtWriter.writeNameless(value);
// 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);
}
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public org.jglrxavpok.hephaistos.nbt.NBT read(@NotNull NetworkBuffer buffer) { public BinaryTag read(@NotNull NetworkBuffer buffer) {
NBTReader nbtReader = buffer.nbtReader; BinaryTagReader nbtReader = buffer.nbtReader;
if (nbtReader == null) { if (nbtReader == null) {
nbtReader = new NBTReader(new InputStream() { nbtReader = new BinaryTagReader(new DataInputStream(new InputStream() {
@Override @Override
public int read() { public int read() {
return buffer.read(BYTE) & 0xFF; return buffer.read(BYTE) & 0xFF;
@ -323,15 +319,12 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
public int available() { public int available() {
return buffer.readableBytes(); return buffer.readableBytes();
} }
}, CompressedProcesser.NONE); }));
buffer.nbtReader = nbtReader; buffer.nbtReader = nbtReader;
} }
try { try {
byte tagId = buffer.read(BYTE); return nbtReader.readNameless();
if (tagId == NBTType.TAG_End.getOrdinal()) } catch (IOException e) {
return NBTEnd.INSTANCE;
return nbtReader.readRaw(tagId);
} catch (IOException | NBTException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@ -362,13 +355,13 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
record ComponentType() implements NetworkBufferTypeImpl<Component> { record ComponentType() implements NetworkBufferTypeImpl<Component> {
@Override @Override
public void write(@NotNull NetworkBuffer buffer, Component value) { 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); buffer.write(NBT, nbt);
} }
@Override @Override
public Component read(@NotNull NetworkBuffer buffer) { public Component read(@NotNull NetworkBuffer buffer) {
final NBT nbt = buffer.read(NBT); final BinaryTag nbt = buffer.read(NBT);
return NbtComponentSerializer.nbt().deserialize(nbt); return NbtComponentSerializer.nbt().deserialize(nbt);
} }
} }
@ -413,8 +406,8 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
buffer.write(VAR_INT, value.material().id()); buffer.write(VAR_INT, value.material().id());
buffer.write(BYTE, (byte) value.amount()); buffer.write(BYTE, (byte) value.amount());
// Vanilla does not write an empty object, just an end tag. // Vanilla does not write an empty object, just an end tag.
NBTCompound nbt = value.meta().toNBT(); CompoundBinaryTag nbt = value.meta().toNBT();
buffer.write(NBT, nbt.isEmpty() ? NBTEnd.INSTANCE : nbt); buffer.write(NBT, nbt.size() == 0 ? EndBinaryTag.endBinaryTag() : nbt);
} }
@Override @Override
@ -427,8 +420,8 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
if (material == null) throw new RuntimeException("Unknown material id: " + id); if (material == null) throw new RuntimeException("Unknown material id: " + id);
final int amount = buffer.read(BYTE); final int amount = buffer.read(BYTE);
final NBT nbt = buffer.read(NBT); final BinaryTag nbt = buffer.read(NBT);
if (!(nbt instanceof NBTCompound compound)) { if (!(nbt instanceof CompoundBinaryTag compound)) {
return ItemStack.of(material, amount); return ItemStack.of(material, amount);
} }
return ItemStack.fromNBT(material, compound, amount); return ItemStack.fromNBT(material, compound, amount);

View File

@ -1,7 +1,7 @@
package net.minestom.server.tag; package net.minestom.server.tag;
import net.kyori.adventure.nbt.*; 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.ArrayList;
import java.util.List; import java.util.List;
@ -51,7 +51,7 @@ final class TagNbtSeparator {
var tagFunction = SUPPORTED_TYPES.get(nbt.type()); var tagFunction = SUPPORTED_TYPES.get(nbt.type());
if (tagFunction != null) { if (tagFunction != null) {
Tag tag = tagFunction.apply(key); 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) { } else if (nbt instanceof CompoundBinaryTag nbtCompound) {
for (var ent : nbtCompound) { for (var ent : nbtCompound) {
var newPath = new ArrayList<>(path); var newPath = new ArrayList<>(path);
@ -68,7 +68,7 @@ final class TagNbtSeparator {
var tag = tagFunction.apply(key).list(); var tag = tagFunction.apply(key).list();
Object[] values = new Object[nbtList.size()]; Object[] values = new Object[nbtList.size()];
for (int i = 0; i < values.length; i++) { 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))); consumer.accept(makeEntry(path, Tag.class.cast(tag), List.of(values)));
} catch (Exception e) { } catch (Exception e) {

View File

@ -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<? extends BinaryTag> type = BinaryTagUtil.nbtTypeFromId(input.readByte());
return type.read(input);
}
public @NotNull Map.Entry<String, BinaryTag> readNamed() throws IOException {
BinaryTagType<? extends BinaryTag> type = BinaryTagUtil.nbtTypeFromId(input.readByte());
String name = input.readUTF();
return Map.entry(name, type.read(input));
}
}

View File

@ -1,4 +1,4 @@
package net.minestom.server.utils; package net.minestom.server.utils.nbt;
import net.kyori.adventure.nbt.*; import net.kyori.adventure.nbt.*;
import net.minestom.server.utils.validate.Check; import net.minestom.server.utils.validate.Check;
@ -6,7 +6,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ApiStatus.Internal @ApiStatus.Internal
public final class NBTUtils { public final class BinaryTagUtil {
private static final BinaryTagType<?>[] TYPES = new BinaryTagType[]{ private static final BinaryTagType<?>[] TYPES = new BinaryTagType[]{
BinaryTagTypes.END, BinaryTagTypes.END,
BinaryTagTypes.BYTE, BinaryTagTypes.BYTE,
@ -54,6 +54,6 @@ public final class NBTUtils {
} }
} }
private NBTUtils() { private BinaryTagUtil() {
} }
} }

View File

@ -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<BinaryTag> type = (BinaryTagType<BinaryTag>) tag.type();
output.writeByte(type.id());
type.write(tag, output);
}
public void readNamed(@NotNull String name, @NotNull BinaryTag tag) throws IOException {
//noinspection unchecked
BinaryTagType<BinaryTag> type = (BinaryTagType<BinaryTag>) tag.type();
output.writeByte(type.id());
output.writeUTF(name);
type.write(tag, output);
}
}