Skip to content

Commit

Permalink
Merge pull request #968 from booky10/feat/nameless-nbt
Browse files Browse the repository at this point in the history
feat(nbt): Implement nameless binary serialization
  • Loading branch information
zml2008 committed Dec 18, 2023
2 parents 6fd5262 + 3a3a0e2 commit 75e29ac
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 3 deletions.
170 changes: 170 additions & 0 deletions nbt/src/main/java/net/kyori/adventure/nbt/BinaryTagIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ public interface Reader {
*
* <p>This is the equivalent of passing {@code Compression#NONE} as the second parameter to {@link #read(Path, Compression)}.</p>
*
* <p>The root name field is discarded.</p>
*
* @param path the path
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
Expand All @@ -262,6 +264,8 @@ public interface Reader {
/**
* Reads a binary tag from {@code path} with a {@code compression} type.
*
* <p>The root name field is discarded.</p>
*
* @param path the path
* @param compression the compression type
* @return a binary tag
Expand All @@ -275,6 +279,8 @@ public interface Reader {
*
* <p>This is the equivalent of passing {@code Compression#NONE} as the second parameter to {@link #read(InputStream, Compression)}.</p>
*
* <p>The root name field is discarded.</p>
*
* @param input the input stream
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
Expand All @@ -287,6 +293,8 @@ public interface Reader {
/**
* Reads a binary tag from {@code input} with a {@code compression} type.
*
* <p>The root name field is discarded.</p>
*
* @param input the input stream
* @param compression the compression type
* @return a binary tag
Expand All @@ -298,13 +306,90 @@ public interface Reader {
/**
* Reads a binary tag from {@code input}.
*
* <p>The root name field is discarded.</p>
*
* @param input the input stream
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
* @since 4.4.0
*/
@NotNull CompoundBinaryTag read(final @NotNull DataInput input) throws IOException;

/**
* Reads a binary tag from {@code path}.
*
* <p>This is the equivalent of passing {@code Compression#NONE} as the second parameter to {@link #read(Path, Compression)}.</p>
*
* <p>Doesn't read a root name from the {@link Path} at all, to match the wire protocol in modern game versions.</p>
*
* @param path the path
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
default @NotNull CompoundBinaryTag readNameless(final @NotNull Path path) throws IOException {
return this.readNameless(path, Compression.NONE);
}

/**
* Reads a binary tag from {@code path} with a {@code compression} type.
*
* <p>Doesn't read a root name from the {@link Path} at all, to match the wire protocol in modern game versions.</p>
*
* @param path the path
* @param compression the compression type
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
@NotNull CompoundBinaryTag readNameless(final @NotNull Path path, final @NotNull Compression compression) throws IOException;

/**
* Reads a binary tag from {@code input}.
*
* <p>This is the equivalent of passing {@code Compression#NONE} as the second parameter to {@link #read(InputStream, Compression)}.</p>
*
* <p>Doesn't read a root name from the {@link InputStream} at all, to match the wire protocol in modern game versions.</p>
*
* @param input the input stream
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
default @NotNull CompoundBinaryTag readNameless(final @NotNull InputStream input) throws IOException {
return this.readNameless(input, Compression.NONE);
}

/**
* Reads a binary tag from {@code input} with a {@code compression} type.
*
* <p>Doesn't read a root name from the {@link InputStream} at all, to match the wire protocol in modern game versions.</p>
*
* @param input the input stream
* @param compression the compression type
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
@NotNull CompoundBinaryTag readNameless(final @NotNull InputStream input, final @NotNull Compression compression) throws IOException;

/**
* Reads a binary tag from {@code input}.
*
* <p>Doesn't read a root name from the {@link DataInput} at all, to match the wire protocol in modern game versions.</p>
*
* @param input the input stream
* @return a binary tag
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
@NotNull CompoundBinaryTag readNameless(final @NotNull DataInput input) throws IOException;

/**
* Reads a binary tag, with a name, from {@code path}.
*
Expand Down Expand Up @@ -377,6 +462,8 @@ public interface Writer {
*
* <p>This is the equivalent of passing {@code Compression#NONE} as the second parameter to {@link #write(CompoundBinaryTag, Path, Compression)}.</p>
*
* <p>An empty root name is written.</p>
*
* @param tag the tag to write
* @param path the path
* @throws IOException if an exception was encountered while reading the tag
Expand All @@ -389,6 +476,8 @@ default void write(final @NotNull CompoundBinaryTag tag, final @NotNull Path pat
/**
* Writes a binary tag to {@code path} with a {@code compression} type.
*
* <p>An empty root name is written.</p>
*
* @param tag the tag to write
* @param path the path
* @param compression the compression type
Expand All @@ -402,6 +491,8 @@ default void write(final @NotNull CompoundBinaryTag tag, final @NotNull Path pat
*
* <p>This is the equivalent of passing {@link Compression#NONE} as the second parameter to {@link #write(CompoundBinaryTag, OutputStream, Compression)}.</p>
*
* <p>An empty root name is written.</p>
*
* @param tag the tag to write
* @param output the output stream
* @throws IOException if an exception was encountered while reading the tag
Expand All @@ -414,6 +505,8 @@ default void write(final @NotNull CompoundBinaryTag tag, final @NotNull OutputSt
/**
* Writes a binary tag to {@code output} with a {@code compression} type.
*
* <p>An empty root name is written.</p>
*
* @param tag the tag to write
* @param output the output stream
* @param compression the compression type
Expand All @@ -425,13 +518,90 @@ default void write(final @NotNull CompoundBinaryTag tag, final @NotNull OutputSt
/**
* Writes a binary tag to {@code output}.
*
* <p>An empty root name is written.</p>
*
* @param tag the tag to write
* @param output the output
* @throws IOException if an exception was encountered while reading the tag
* @since 4.4.0
*/
void write(final @NotNull CompoundBinaryTag tag, final @NotNull DataOutput output) throws IOException;

/**
* Writes a binary tag to {@code path} with a {@code compression} type.
*
* <p>This is the equivalent of passing {@code Compression#NONE} as the second parameter to {@link #write(CompoundBinaryTag, Path, Compression)}.</p>
*
* <p>Doesn't write a root name to the {@link Path} at all, to match the wire protocol in modern game versions.</p>
*
* @param tag the tag to write
* @param path the path
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
default void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull Path path) throws IOException {
this.writeNameless(tag, path, Compression.NONE);
}

/**
* Writes a binary tag to {@code path} with a {@code compression} type.
*
* <p>Doesn't write a root name to the {@link Path} at all, to match the wire protocol in modern game versions.</p>
*
* @param tag the tag to write
* @param path the path
* @param compression the compression type
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull Path path, final @NotNull Compression compression) throws IOException;

/**
* Writes a binary tag to {@code output}.
*
* <p>This is the equivalent of passing {@link Compression#NONE} as the second parameter to {@link #write(CompoundBinaryTag, OutputStream, Compression)}.</p>
*
* <p>Doesn't write a root name to the {@link OutputStream} at all, to match the wire protocol in modern game versions.</p>
*
* @param tag the tag to write
* @param output the output stream
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
default void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull OutputStream output) throws IOException {
this.writeNameless(tag, output, Compression.NONE);
}

/**
* Writes a binary tag to {@code output} with a {@code compression} type.
*
* <p>Doesn't write a root name to the {@link OutputStream} at all, to match the wire protocol in modern game versions.</p>
*
* @param tag the tag to write
* @param output the output stream
* @param compression the compression type
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull OutputStream output, final @NotNull Compression compression) throws IOException;

/**
* Writes a binary tag to {@code output}.
*
* <p>Doesn't write a root name to the {@link DataOutput} at all, to match the wire protocol in modern game versions.</p>
*
* @param tag the tag to write
* @param output the output
* @throws IOException if an exception was encountered while reading the tag
* @since 4.15.0
* @sinceMinecraft 1.20.2
*/
void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull DataOutput output) throws IOException;

/**
* Writes a binary tag, with a name, to {@code path}.
*
Expand Down
29 changes: 27 additions & 2 deletions nbt/src/main/java/net/kyori/adventure/nbt/BinaryTagReaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,42 @@ final class BinaryTagReaderImpl implements BinaryTagIO.Reader {
}

@Override
public @NotNull CompoundBinaryTag read(@NotNull DataInput input) throws IOException {
public @NotNull CompoundBinaryTag read(final @NotNull DataInput input) throws IOException {
return this.read(input, true);
}

private @NotNull CompoundBinaryTag read(@NotNull DataInput input, final boolean named) throws IOException {
if (!(input instanceof TrackingDataInput)) {
input = new TrackingDataInput(input, this.maxBytes);
}

final BinaryTagType<? extends BinaryTag> type = BinaryTagType.binaryTagType(input.readByte());
requireCompound(type);
input.skipBytes(input.readUnsignedShort()); // read empty name
if (named) {
input.skipBytes(input.readUnsignedShort()); // read empty name
}
return BinaryTagTypes.COMPOUND.read(input);
}

@Override
public @NotNull CompoundBinaryTag readNameless(final @NotNull Path path, final BinaryTagIO.@NotNull Compression compression) throws IOException {
try (final InputStream is = Files.newInputStream(path)) {
return this.readNameless(is, compression);
}
}

@Override
public @NotNull CompoundBinaryTag readNameless(final @NotNull InputStream input, final BinaryTagIO.@NotNull Compression compression) throws IOException {
try (final DataInputStream dis = new DataInputStream(new BufferedInputStream(compression.decompress(closeShield(input))))) {
return this.readNameless((DataInput) dis);
}
}

@Override
public @NotNull CompoundBinaryTag readNameless(final @NotNull DataInput input) throws IOException {
return this.read(input, false);
}

@Override
public Map.@NotNull Entry<String, CompoundBinaryTag> readNamed(final @NotNull Path path, final BinaryTagIO.@NotNull Compression compression) throws IOException {
try (final InputStream is = Files.newInputStream(path)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,36 @@ public void write(final @NotNull CompoundBinaryTag tag, final @NotNull OutputStr

@Override
public void write(final @NotNull CompoundBinaryTag tag, final @NotNull DataOutput output) throws IOException {
this.write(tag, output, true);
}

private void write(final @NotNull CompoundBinaryTag tag, final @NotNull DataOutput output, final boolean named) throws IOException {
output.writeByte(BinaryTagTypes.COMPOUND.id());
output.writeUTF(""); // write empty name
if (named) {
output.writeUTF(""); // write empty name
}
BinaryTagTypes.COMPOUND.write(tag, output);
}

@Override
public void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull Path path, final BinaryTagIO.@NotNull Compression compression) throws IOException {
try (final OutputStream os = Files.newOutputStream(path)) {
this.writeNameless(tag, os, compression);
}
}

@Override
public void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull OutputStream output, final BinaryTagIO.@NotNull Compression compression) throws IOException {
try (final DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(compression.compress(closeShield(output))))) {
this.writeNameless(tag, (DataOutput) dos);
}
}

@Override
public void writeNameless(final @NotNull CompoundBinaryTag tag, final @NotNull DataOutput output) throws IOException {
this.write(tag, output, false);
}

@Override
public void writeNamed(final Map.@NotNull Entry<String, CompoundBinaryTag> tag, final @NotNull Path path, final BinaryTagIO.@NotNull Compression compression) throws IOException {
try (final OutputStream os = Files.newOutputStream(path)) {
Expand Down
10 changes: 10 additions & 0 deletions nbt/src/test/java/net/kyori/adventure/nbt/BinaryTagIOTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,14 @@ void testWriteAndReadZLIBCompression() throws IOException {
BinaryTagIO.writer().write(tag, output, BinaryTagIO.Compression.ZLIB);
assertEquals(tag, BinaryTagIO.reader().read(new ByteArrayInputStream(output.toByteArray()), BinaryTagIO.Compression.ZLIB));
}

@Test
void testNamelessWriteAndReadNoCompression() throws IOException {
final CompoundBinaryTag tag = CompoundBinaryTag.builder()
.putString("name", "test")
.build();
final ByteArrayOutputStream output = new ByteArrayOutputStream();
BinaryTagIO.writer().writeNameless(tag, output);
assertEquals(tag, BinaryTagIO.reader().readNameless(new ByteArrayInputStream(output.toByteArray())));
}
}

0 comments on commit 75e29ac

Please sign in to comment.