#3803: Add NBT module

This commit is contained in:
Outfluencer 2025-05-31 10:16:29 +10:00 committed by md_5
parent bec329352d
commit 41e49dad6b
No known key found for this signature in database
GPG Key ID: E8E901AC7C617C11
39 changed files with 1514 additions and 154 deletions

28
nbt/LICENSE Normal file
View File

@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2025, SpigotMC Pty. Ltd.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
nbt/README.md Normal file
View File

@ -0,0 +1,4 @@
BungeeCord-NBT
=================
Minimal implementation of NBT for use in BungeeCord

31
nbt/nb-configuration.xml Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

27
nbt/pom.xml Normal file
View File

@ -0,0 +1,27 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-nbt</artifactId>
<version>1.21-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-NBT</name>
<description>Minimal implementation of NBT for use in BungeeCord</description>
<licenses>
<license>
<name>BSD-3-Clause</name>
<url>https://github.com/SpigotMC/BungeeCord/blob/master/dialog/LICENSE</url>
<distribution>repo</distribution>
</license>
</licenses>
</project>

View File

@ -0,0 +1,52 @@
package net.md_5.bungee.nbt;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.limit.NBTLimiter;
import net.md_5.bungee.nbt.type.CompoundTag;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NamedTag implements Tag
{
private String name;
private TypedTag tag;
/**
* Reads the data of the {@link DataInput} and parses it into this
* {@link NamedTag}.
*
* @param input input to read from
* @param limiter limitation of the read data
*/
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
byte type = input.readByte();
name = CompoundTag.readString( input, limiter );
tag = Tag.readById( type, input, limiter );
}
/**
* Write this {@link NamedTag} into a {@link DataOutput}.
*
* @param output the output to write to
*/
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( name, "name cannot be null" );
Preconditions.checkNotNull( tag, "tag cannot be null" );
output.writeByte( tag.getId() );
CompoundTag.writeString( name, output );
tag.write( output );
}
}

View File

@ -0,0 +1,131 @@
package net.md_5.bungee.nbt;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.function.Supplier;
import net.md_5.bungee.nbt.exception.NBTFormatException;
import net.md_5.bungee.nbt.limit.NBTLimiter;
import net.md_5.bungee.nbt.type.ByteArrayTag;
import net.md_5.bungee.nbt.type.ByteTag;
import net.md_5.bungee.nbt.type.CompoundTag;
import net.md_5.bungee.nbt.type.DoubleTag;
import net.md_5.bungee.nbt.type.EndTag;
import net.md_5.bungee.nbt.type.FloatTag;
import net.md_5.bungee.nbt.type.IntArrayTag;
import net.md_5.bungee.nbt.type.IntTag;
import net.md_5.bungee.nbt.type.ListTag;
import net.md_5.bungee.nbt.type.LongArrayTag;
import net.md_5.bungee.nbt.type.LongTag;
import net.md_5.bungee.nbt.type.ShortTag;
import net.md_5.bungee.nbt.type.StringTag;
public interface Tag
{
int OBJECT_HEADER = 8;
int ARRAY_HEADER = 12;
int STRING_SIZE = 28;
int OBJECT_REFERENCE = 4;
Supplier<? extends TypedTag>[] CONSTRUCTORS = new Supplier[]
{
EndTag::new,
ByteTag::new,
ShortTag::new,
IntTag::new,
LongTag::new,
FloatTag::new,
DoubleTag::new,
ByteArrayTag::new,
StringTag::new,
ListTag::new,
CompoundTag::new,
IntArrayTag::new,
LongArrayTag::new
};
byte END = 0;
byte BYTE = 1;
byte SHORT = 2;
byte INT = 3;
byte LONG = 4;
byte FLOAT = 5;
byte DOUBLE = 6;
byte BYTE_ARRAY = 7;
byte STRING = 8;
byte LIST = 9;
byte COMPOUND = 10;
byte INT_ARRAY = 11;
byte LONG_ARRAY = 12;
/**
* Reads the data into this tag.
*
* @param input the input to read from
* @param limiter the limiter for this read operation
* @throws IOException if an exception occurs during io operations
*/
void read(DataInput input, NBTLimiter limiter) throws IOException;
/**
* Writes this tag into a {@link DataOutput}.
*
* @param output the output to write to
* @throws IOException if an exception occurs during io operations
*/
void write(DataOutput output) throws IOException;
/**
* Reads the data of the {@link DataInput} and parses it into a {@link Tag}.
*
* @param id the nbt type
* @param input input to read from
* @param limiter limitation of the read data
* @return the initialized {@link Tag}
* @throws IOException if an exception occurs during io operations
*/
static TypedTag readById(byte id, DataInput input, NBTLimiter limiter) throws IOException
{
if ( id < END || id > LONG_ARRAY )
{
throw new NBTFormatException( "Invalid tag id: " + id );
}
TypedTag tag = CONSTRUCTORS[id].get();
tag.read( input, limiter );
return tag;
}
static NamedTag readNamedTag(DataInput input, NBTLimiter limiter) throws IOException
{
NamedTag namedTag = new NamedTag();
namedTag.read( input, limiter );
return namedTag;
}
static byte[] toByteArray(TypedTag tag) throws IOException
{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream( byteArrayOutputStream );
dataOutputStream.writeByte( tag.getId() );
tag.write( dataOutputStream );
return byteArrayOutputStream.toByteArray();
}
static TypedTag fromByteArray(byte[] data) throws IOException
{
return fromByteArray( data, NBTLimiter.unlimitedSize() );
}
static TypedTag fromByteArray(byte[] data, NBTLimiter limiter) throws IOException
{
DataInputStream stream = new DataInputStream( new ByteArrayInputStream( data ) );
byte type = stream.readByte();
return readById( type, stream, limiter );
}
}

View File

@ -0,0 +1,12 @@
package net.md_5.bungee.nbt;
public interface TypedTag extends Tag
{
/**
* Gets the id of this tag's type.
*
* @return the id related to this tag's type
*/
byte getId();
}

View File

@ -0,0 +1,10 @@
package net.md_5.bungee.nbt.exception;
public class NBTException extends RuntimeException
{
public NBTException(String message)
{
super( message );
}
}

View File

@ -0,0 +1,10 @@
package net.md_5.bungee.nbt.exception;
public class NBTFormatException extends NBTException
{
public NBTFormatException(String message)
{
super( message );
}
}

View File

@ -0,0 +1,10 @@
package net.md_5.bungee.nbt.exception;
public class NBTLimitException extends NBTException
{
public NBTLimitException(String message)
{
super( message );
}
}

View File

@ -0,0 +1,66 @@
package net.md_5.bungee.nbt.limit;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.nbt.exception.NBTLimitException;
@RequiredArgsConstructor
public class NBTLimiter
{
private static final int MAX_STACK_DEPTH = 512;
//
private final long maxBytes;
private final int maxDepth;
public static NBTLimiter unlimitedSize()
{
return new NBTLimiter( Long.MAX_VALUE, MAX_STACK_DEPTH );
}
public NBTLimiter(long maxBytes)
{
this( maxBytes, MAX_STACK_DEPTH );
}
private long usedBytes;
private int depth;
public void countBytes(long amount)
{
if ( amount < 0 )
{
throw new NBTLimitException( "NBT limiter tried to count negative byte amount" );
}
if ( ( usedBytes = Math.addExact( usedBytes, amount ) ) > maxBytes )
{
throw new NBTLimitException( "NBT tag is to big, bytes > " + maxBytes );
}
}
public void countBytes(long amount, long factor)
{
if ( amount < 0 || factor < 0 )
{
throw new NBTLimitException( "NBT limiter tried to count negative byte amount" );
}
countBytes( Math.multiplyExact( amount, factor ) );
}
public void push()
{
if ( ( depth = Math.addExact( depth, 1 ) ) > maxDepth )
{
throw new NBTLimitException( "NBT tag is to complex, depth > " + maxDepth );
}
}
public void pop()
{
if ( --depth < 0 )
{
throw new NBTLimitException( "NBT limiter tried to pop depth 0" );
}
}
}

View File

@ -0,0 +1,44 @@
package net.md_5.bungee.nbt.type;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ByteArrayTag implements TypedTag
{
private byte[] value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + ARRAY_HEADER + Integer.BYTES );
int length = input.readInt();
limiter.countBytes( length, Byte.BYTES );
input.readFully( value = new byte[ length ] );
}
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( value, "byte array value cannot be null" );
output.writeInt( value.length );
output.write( value );
}
@Override
public byte getId()
{
return Tag.BYTE_ARRAY;
}
}

View File

@ -0,0 +1,39 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ByteTag implements TypedTag
{
private byte value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + Byte.BYTES );
value = input.readByte();
}
@Override
public void write(DataOutput output) throws IOException
{
output.write( value );
}
@Override
public byte getId()
{
return Tag.BYTE;
}
}

View File

@ -0,0 +1,99 @@
package net.md_5.bungee.nbt.type;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.exception.NBTFormatException;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CompoundTag implements TypedTag
{
private static final int MAP_SIZE_IN_BYTES = 48;
private static final int MAP_ENTRY_SIZE_IN_BYTES = 32;
//
private Map<String, TypedTag> value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.push();
limiter.countBytes( MAP_SIZE_IN_BYTES );
Map<String, TypedTag> map = new LinkedHashMap<>();
for ( byte type; ( type = input.readByte() ) != Tag.END; )
{
String name = readString( input, limiter );
TypedTag tag = Tag.readById( type, input, limiter );
if ( map.put( name, tag ) == null )
{
limiter.countBytes( MAP_ENTRY_SIZE_IN_BYTES + OBJECT_REFERENCE );
}
}
limiter.pop();
value = map;
}
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( value, "compound tag map cannot be null" );
for ( Map.Entry<String, TypedTag> entry : value.entrySet() )
{
String name = entry.getKey();
TypedTag tag = entry.getValue();
output.writeByte( tag.getId() );
if ( tag.getId() == Tag.END )
{
throw new NBTFormatException( "invalid end tag in compound tag" );
}
writeString( name, output );
tag.write( output );
}
output.writeByte( 0 );
}
@Override
public byte getId()
{
return Tag.COMPOUND;
}
public static String readString(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( STRING_SIZE );
String string = input.readUTF();
limiter.countBytes( string.length(), Character.BYTES );
return string;
}
public static void writeString(String string, DataOutput output) throws IOException
{
output.writeUTF( string );
}
public TypedTag get(String key)
{
return value.get( key );
}
public void put(String key, TypedTag tag)
{
value.put( key, tag );
}
public int size()
{
return value.size();
}
}

View File

@ -0,0 +1,39 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DoubleTag implements TypedTag
{
private double value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + Double.BYTES );
value = input.readDouble();
}
@Override
public void write(DataOutput output) throws IOException
{
output.writeDouble( value );
}
@Override
public byte getId()
{
return Tag.DOUBLE;
}
}

View File

@ -0,0 +1,34 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
public class EndTag implements TypedTag
{
public static final EndTag INSTANCE = new EndTag();
@Override
public void read(DataInput input, NBTLimiter limiter)
{
limiter.countBytes( OBJECT_HEADER );
}
@Override
public void write(DataOutput output)
{
}
@Override
public byte getId()
{
return Tag.END;
}
}

View File

@ -0,0 +1,39 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FloatTag implements TypedTag
{
private float value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + Float.BYTES );
value = input.readFloat();
}
@Override
public void write(DataOutput output) throws IOException
{
output.writeFloat( value );
}
@Override
public byte getId()
{
return Tag.FLOAT;
}
}

View File

@ -0,0 +1,52 @@
package net.md_5.bungee.nbt.type;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IntArrayTag implements TypedTag
{
private int[] value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + ARRAY_HEADER + Integer.BYTES );
int length = input.readInt();
limiter.countBytes( length, Integer.BYTES );
int[] data = new int[ length ];
for ( int i = 0; i < length; i++ )
{
data[i] = input.readInt();
}
value = data;
}
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( value, "int array value cannot be null" );
output.writeInt( value.length );
for ( int i : value )
{
output.writeInt( i );
}
}
@Override
public byte getId()
{
return Tag.INT_ARRAY;
}
}

View File

@ -0,0 +1,39 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IntTag implements TypedTag
{
private int value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + Integer.BYTES );
value = input.readInt();
}
@Override
public void write(DataOutput output) throws IOException
{
output.writeInt( value );
}
@Override
public byte getId()
{
return Tag.INT;
}
}

View File

@ -0,0 +1,87 @@
package net.md_5.bungee.nbt.type;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.exception.NBTFormatException;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ListTag implements TypedTag
{
public static final int LIST_HEADER = 12;
//
private List<TypedTag> value;
private byte listType;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.push();
limiter.countBytes( OBJECT_HEADER + LIST_HEADER + ARRAY_HEADER + Byte.BYTES + Integer.BYTES );
listType = input.readByte();
int length = input.readInt();
if ( listType == Tag.END && length > 0 )
{
throw new NBTFormatException( "Missing type in ListTag" );
}
limiter.countBytes( length, OBJECT_REFERENCE );
List<TypedTag> tagList = new ArrayList<>( length );
for ( int i = 0; i < length; i++ )
{
tagList.add( Tag.readById( listType, input, limiter ) );
}
limiter.pop();
value = tagList;
}
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( value, "list value cannot be null" );
if ( listType == Tag.END && !value.isEmpty() )
{
throw new NBTFormatException( "Missing type in ListTag" );
}
output.writeByte( listType );
output.writeInt( value.size() );
for ( TypedTag tag : value )
{
if ( tag.getId() != listType )
{
throw new NBTFormatException( "ListTag type mismatch" );
}
tag.write( output );
}
}
@Override
public byte getId()
{
return Tag.LIST;
}
public TypedTag get(int index)
{
return value.get( index );
}
public int size()
{
return value.size();
}
}

View File

@ -0,0 +1,53 @@
package net.md_5.bungee.nbt.type;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LongArrayTag implements TypedTag
{
private long[] value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + ARRAY_HEADER + Integer.BYTES );
int length = input.readInt();
limiter.countBytes( length, Long.BYTES );
long[] data = new long[ length ];
for ( int i = 0; i < length; i++ )
{
data[i] = input.readLong();
}
value = data;
}
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( value, "long array value cannot be null" );
output.writeInt( value.length );
for ( long i : value )
{
output.writeLong( i );
}
}
@Override
public byte getId()
{
return Tag.LONG_ARRAY;
}
}

View File

@ -0,0 +1,39 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LongTag implements TypedTag
{
private long value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + Long.BYTES );
value = input.readLong();
}
@Override
public void write(DataOutput output) throws IOException
{
output.writeLong( value );
}
@Override
public byte getId()
{
return Tag.LONG;
}
}

View File

@ -0,0 +1,39 @@
package net.md_5.bungee.nbt.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ShortTag implements TypedTag
{
private short value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + Short.BYTES );
value = input.readShort();
}
@Override
public void write(DataOutput output) throws IOException
{
output.writeShort( value );
}
@Override
public byte getId()
{
return Tag.SHORT;
}
}

View File

@ -0,0 +1,44 @@
package net.md_5.bungee.nbt.type;
import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StringTag implements TypedTag
{
private String value;
@Override
public void read(DataInput input, NBTLimiter limiter) throws IOException
{
limiter.countBytes( OBJECT_HEADER + STRING_SIZE );
String string = input.readUTF();
limiter.countBytes( string.length(), Character.BYTES );
value = string;
}
@Override
public void write(DataOutput output) throws IOException
{
Preconditions.checkNotNull( value, "string value cannot be null" );
output.writeUTF( value );
}
@Override
public byte getId()
{
return Tag.STRING;
}
}

View File

@ -0,0 +1,53 @@
package net.md_5.bungee.nbt;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import net.md_5.bungee.nbt.exception.NBTLimitException;
import net.md_5.bungee.nbt.limit.NBTLimiter;
import net.md_5.bungee.nbt.type.ByteArrayTag;
import net.md_5.bungee.nbt.type.ListTag;
import org.junit.jupiter.api.Test;
public class NBTLimiterTest
{
@Test
public void testNbtLimiter()
{
assertThrows( NBTLimitException.class, () ->
{
ByteArrayTag byteArrayTag = new ByteArrayTag( new byte[ 1000 ] );
byte[] arr = Tag.toByteArray( byteArrayTag );
Tag.fromByteArray( arr, new NBTLimiter( 100, 1 ) );
} );
assertDoesNotThrow( () ->
{
ByteArrayTag byteArrayTag = new ByteArrayTag( new byte[ 1000 ] );
byte[] arr = Tag.toByteArray( byteArrayTag );
Tag.fromByteArray( arr, new NBTLimiter( 99999999, 1 ) );
} );
}
@Test
public void testDepth()
{
assertThrows( NBTLimitException.class, () ->
{
ListTag root = new ListTag( new ArrayList<>(), Tag.LIST );
root.getValue().add( new ListTag( new ArrayList<>(), Tag.LIST ) );
byte[] arr = Tag.toByteArray( root );
Tag.fromByteArray( arr, new NBTLimiter( 100, 1 ) );
} );
assertDoesNotThrow( () ->
{
ListTag root = new ListTag( new ArrayList<>(), Tag.LIST );
root.getValue().add( new ListTag( new ArrayList<>(), Tag.LIST ) );
byte[] arr = Tag.toByteArray( root );
Tag.fromByteArray( arr, new NBTLimiter( 100, 2 ) );
} );
}
}

View File

@ -0,0 +1,247 @@
package net.md_5.bungee.nbt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import net.md_5.bungee.nbt.exception.NBTFormatException;
import net.md_5.bungee.nbt.type.ByteArrayTag;
import net.md_5.bungee.nbt.type.ByteTag;
import net.md_5.bungee.nbt.type.CompoundTag;
import net.md_5.bungee.nbt.type.DoubleTag;
import net.md_5.bungee.nbt.type.EndTag;
import net.md_5.bungee.nbt.type.FloatTag;
import net.md_5.bungee.nbt.type.IntArrayTag;
import net.md_5.bungee.nbt.type.IntTag;
import net.md_5.bungee.nbt.type.ListTag;
import net.md_5.bungee.nbt.type.LongArrayTag;
import net.md_5.bungee.nbt.type.LongTag;
import net.md_5.bungee.nbt.type.ShortTag;
import net.md_5.bungee.nbt.type.StringTag;
import org.junit.jupiter.api.Test;
public class NBTTagTest
{
@Test
public void testByteTag() throws IOException
{
byte[] tests = new byte[]
{
0, Byte.MAX_VALUE, Byte.MIN_VALUE
};
for ( byte value : tests )
{
ByteTag byteTag = new ByteTag( value );
byte[] deserialized = Tag.toByteArray( byteTag );
ByteTag reSerialized = (ByteTag) Tag.fromByteArray( deserialized );
assertEquals( byteTag, reSerialized );
}
}
@Test
public void testShortTag() throws IOException
{
short[] tests = new short[]
{
0, Short.MAX_VALUE, Short.MIN_VALUE
};
for ( short value : tests )
{
ShortTag tag = new ShortTag( value );
byte[] deserialized = Tag.toByteArray( tag );
ShortTag reSerialized = (ShortTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
}
@Test
public void testIntTag() throws IOException
{
int[] tests = new int[]
{
0, Integer.MAX_VALUE, Integer.MIN_VALUE
};
for ( int value : tests )
{
IntTag tag = new IntTag( value );
byte[] deserialized = Tag.toByteArray( tag );
IntTag reSerialized = (IntTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
}
@Test
public void testLongTag() throws IOException
{
long[] tests = new long[]
{
0, Long.MAX_VALUE, Long.MIN_VALUE
};
for ( long value : tests )
{
LongTag tag = new LongTag( value );
byte[] deserialized = Tag.toByteArray( tag );
LongTag reSerialized = (LongTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
}
@Test
public void testDoubleTag() throws IOException
{
double[] tests = new double[]
{
0, Double.MAX_VALUE, Double.MIN_VALUE, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
};
for ( double value : tests )
{
DoubleTag tag = new DoubleTag( value );
byte[] deserialized = Tag.toByteArray( tag );
DoubleTag reSerialized = (DoubleTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
}
@Test
public void testFloatTag() throws IOException
{
float[] tests = new float[]
{
0, Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY
};
for ( float value : tests )
{
FloatTag tag = new FloatTag( value );
byte[] deserialized = Tag.toByteArray( tag );
FloatTag reSerialized = (FloatTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
}
@Test
public void testStringTag() throws IOException
{
String[] tests = new String[]
{
"Outfluencer", "", String.valueOf( System.currentTimeMillis() ), "BungeeCord", new Object().toString()
};
for ( String value : tests )
{
StringTag tag = new StringTag( value );
byte[] deserialized = Tag.toByteArray( tag );
StringTag reSerialized = (StringTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
}
@Test
public void testByteArrayTag() throws IOException
{
byte[] value = new byte[ 1024 ];
ThreadLocalRandom.current().nextBytes( value );
ByteArrayTag tag = new ByteArrayTag( value );
byte[] deserialized = Tag.toByteArray( tag );
ByteArrayTag reSerialized = (ByteArrayTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
value = new byte[ 0 ];
ThreadLocalRandom.current().nextBytes( value );
tag = new ByteArrayTag( value );
deserialized = Tag.toByteArray( tag );
reSerialized = (ByteArrayTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
@Test
public void testIntArrayTag() throws IOException
{
int[] value = new int[ 1024 ];
for ( int i = 0; i < value.length; i++ )
{
value[i] = ThreadLocalRandom.current().nextInt();
}
IntArrayTag tag = new IntArrayTag( value );
byte[] deserialized = Tag.toByteArray( tag );
IntArrayTag reSerialized = (IntArrayTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
value = new int[ 0 ];
tag = new IntArrayTag( value );
deserialized = Tag.toByteArray( tag );
reSerialized = (IntArrayTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
@Test
public void testLongArrayTag() throws IOException
{
long[] value = new long[ 1024 ];
for ( int i = 0; i < value.length; i++ )
{
value[i] = ThreadLocalRandom.current().nextLong();
}
LongArrayTag tag = new LongArrayTag( value );
byte[] deserialized = Tag.toByteArray( tag );
LongArrayTag reSerialized = (LongArrayTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
value = new long[ 0 ];
tag = new LongArrayTag( value );
deserialized = Tag.toByteArray( tag );
reSerialized = (LongArrayTag) Tag.fromByteArray( deserialized );
assertEquals( tag, reSerialized );
}
@Test
public void testListTag() throws IOException
{
List<TypedTag> tags = new ArrayList<>();
for ( int i = 0; i < 100; i++ )
{
tags.add( new IntTag( i ) );
}
ListTag listTag = new ListTag( tags, Tag.INT );
byte[] deserialized = Tag.toByteArray( listTag );
ListTag reSerialized = (ListTag) Tag.fromByteArray( deserialized );
assertEquals( reSerialized.getValue(), tags );
List<TypedTag> tags2 = new ArrayList<>();
for ( int i = 0; i < 100; i++ )
{
tags2.add( new IntTag( i ) );
}
tags2.add( new ByteTag( Byte.MIN_VALUE ) );
assertThrows( NBTFormatException.class, () -> Tag.toByteArray( new ListTag( tags2, Tag.INT ) ) );
assertThrows( NBTFormatException.class, () -> Tag.toByteArray( new ListTag( Collections.singletonList( new EndTag() ), Tag.END ) ) );
}
@Test
public void testCompoundTag() throws IOException
{
Map<String, TypedTag> map = new HashMap<>();
for ( int i = 0; i < 100; i++ )
{
map.put( "" + i, new IntTag( i ) );
map.put( "a" + i, new ByteTag( (byte) i ) );
map.put( "b" + i, new ShortTag( (short) i ) );
map.put( "c" + i, new LongTag( i ) );
map.put( "f" + i, new FloatTag( i ) );
map.put( "d" + i, new DoubleTag( i ) );
}
CompoundTag compoundTag = new CompoundTag( map );
byte[] deserialized = Tag.toByteArray( compoundTag );
CompoundTag reSerialized = (CompoundTag) Tag.fromByteArray( deserialized );
assertEquals( reSerialized, compoundTag );
Map<String, TypedTag> map2 = new LinkedHashMap<>();
map2.put( "", new EndTag() );
CompoundTag compoundTag2 = new CompoundTag( map2 );
assertThrows( NBTFormatException.class, () -> Tag.toByteArray( compoundTag2 ) );
}
}

View File

@ -0,0 +1,30 @@
package net.md_5.bungee.nbt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Objects;
import net.md_5.bungee.nbt.limit.NBTLimiter;
import net.md_5.bungee.nbt.type.CompoundTag;
import org.junit.jupiter.api.Test;
public class PlayerDataTest
{
@Test
public void testPlayerData() throws URISyntaxException, IOException
{
ClassLoader classLoader = PlayerDataTest.class.getClassLoader();
File file = new File( Objects.requireNonNull( classLoader.getResource( "playerdata.nbt" ) ).toURI() );
FileInputStream fileInputStream = new FileInputStream( file );
DataInputStream dis = new DataInputStream( fileInputStream );
NamedTag namedTag = new NamedTag();
namedTag.read( dis, NBTLimiter.unlimitedSize() );
assertInstanceOf( CompoundTag.class, namedTag.getTag() );
assertEquals( namedTag.getName(), "" );
}
}

Binary file not shown.

View File

@ -45,6 +45,7 @@
<module>serializer</module> <module>serializer</module>
<module>slf4j</module> <module>slf4j</module>
<module>native</module> <module>native</module>
<module>nbt</module>
</modules> </modules>
<scm> <scm>

View File

@ -40,6 +40,12 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-nbt</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId> <artifactId>netty-codec</artifactId>
@ -51,11 +57,5 @@
<version>3.1.0</version> <version>3.1.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>se.llbit</groupId>
<artifactId>jo-nbt</artifactId>
<version>1.3.0</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -21,10 +21,11 @@ import java.util.function.BiConsumer;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentStyle; import net.md_5.bungee.api.chat.ComponentStyle;
import se.llbit.nbt.ErrorTag; import net.md_5.bungee.nbt.NamedTag;
import se.llbit.nbt.NamedTag; import net.md_5.bungee.nbt.Tag;
import se.llbit.nbt.SpecificTag; import net.md_5.bungee.nbt.TypedTag;
import se.llbit.nbt.Tag; import net.md_5.bungee.nbt.limit.NBTLimiter;
import net.md_5.bungee.nbt.type.EndTag;
@RequiredArgsConstructor @RequiredArgsConstructor
public abstract class DefinedPacket public abstract class DefinedPacket
@ -116,7 +117,7 @@ public abstract class DefinedPacket
{ {
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 ) if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 )
{ {
SpecificTag nbt = (SpecificTag) readTag( buf, protocolVersion ); TypedTag nbt = (TypedTag) readTag( buf, protocolVersion );
JsonElement json = TagUtil.toJson( nbt ); JsonElement json = TagUtil.toJson( nbt );
return ChatSerializer.forVersion( protocolVersion ).deserialize( json ); return ChatSerializer.forVersion( protocolVersion ).deserialize( json );
@ -130,7 +131,7 @@ public abstract class DefinedPacket
public static ComponentStyle readComponentStyle(ByteBuf buf, int protocolVersion) public static ComponentStyle readComponentStyle(ByteBuf buf, int protocolVersion)
{ {
SpecificTag nbt = (SpecificTag) readTag( buf, protocolVersion ); TypedTag nbt = (TypedTag) readTag( buf, protocolVersion );
JsonElement json = TagUtil.toJson( nbt ); JsonElement json = TagUtil.toJson( nbt );
return ChatSerializer.forVersion( protocolVersion ).deserializeStyle( json ); return ChatSerializer.forVersion( protocolVersion ).deserializeStyle( json );
@ -152,8 +153,7 @@ public abstract class DefinedPacket
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 ) if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 )
{ {
JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson( message ); JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson( message );
SpecificTag nbt = TagUtil.fromJson( json ); TypedTag nbt = TagUtil.fromJson( json );
writeTag( nbt, buf, protocolVersion ); writeTag( nbt, buf, protocolVersion );
} else } else
{ {
@ -166,8 +166,7 @@ public abstract class DefinedPacket
public static void writeComponentStyle(ComponentStyle style, ByteBuf buf, int protocolVersion) public static void writeComponentStyle(ComponentStyle style, ByteBuf buf, int protocolVersion)
{ {
JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson( style ); JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson( style );
SpecificTag nbt = TagUtil.fromJson( json ); TypedTag nbt = TagUtil.fromJson( json );
writeTag( nbt, buf, protocolVersion ); writeTag( nbt, buf, protocolVersion );
} }
@ -456,29 +455,26 @@ public abstract class DefinedPacket
public static Tag readTag(ByteBuf input, int protocolVersion) public static Tag readTag(ByteBuf input, int protocolVersion)
{ {
DataInputStream in = new DataInputStream( new ByteBufInputStream( input ) ); DataInputStream in = new DataInputStream( new ByteBufInputStream( input ) );
Tag tag; try
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_2 )
{ {
try if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_2 )
{ {
byte type = in.readByte(); byte type = in.readByte();
if ( type == 0 ) if ( type == 0 )
{ {
return Tag.END; return EndTag.INSTANCE;
} else } else
{ {
tag = SpecificTag.read( type, in ); return Tag.readById( type, in, new NBTLimiter( 1 << 21 ) );
} }
} catch ( IOException ex )
{
tag = new ErrorTag( "IOException while reading tag type:\n" + ex.getMessage() );
} }
} else NamedTag namedTag = new NamedTag();
namedTag.read( in, new NBTLimiter( 1 << 21 ) );
return namedTag;
} catch ( IOException ex )
{ {
tag = NamedTag.read( in ); throw new RuntimeException( "Exception reading tag", ex );
} }
Preconditions.checkArgument( !tag.isError(), "Error reading tag: %s", tag.error() );
return tag;
} }
public static void writeTag(Tag tag, ByteBuf output, int protocolVersion) public static void writeTag(Tag tag, ByteBuf output, int protocolVersion)
@ -486,11 +482,11 @@ public abstract class DefinedPacket
DataOutputStream out = new DataOutputStream( new ByteBufOutputStream( output ) ); DataOutputStream out = new DataOutputStream( new ByteBufOutputStream( output ) );
try try
{ {
if ( tag instanceof SpecificTag ) if ( tag instanceof TypedTag )
{ {
SpecificTag specificTag = (SpecificTag) tag; TypedTag typedTag = (TypedTag) tag;
specificTag.writeType( out ); out.writeByte( typedTag.getId() );
specificTag.write( out ); typedTag.write( out );
} else } else
{ {
tag.write( out ); tag.write( out );

View File

@ -7,31 +7,32 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import se.llbit.nbt.ByteArrayTag; import net.md_5.bungee.nbt.Tag;
import se.llbit.nbt.ByteTag; import net.md_5.bungee.nbt.TypedTag;
import se.llbit.nbt.CompoundTag; import net.md_5.bungee.nbt.type.ByteArrayTag;
import se.llbit.nbt.DoubleTag; import net.md_5.bungee.nbt.type.ByteTag;
import se.llbit.nbt.FloatTag; import net.md_5.bungee.nbt.type.CompoundTag;
import se.llbit.nbt.IntArrayTag; import net.md_5.bungee.nbt.type.DoubleTag;
import se.llbit.nbt.IntTag; import net.md_5.bungee.nbt.type.EndTag;
import se.llbit.nbt.ListTag; import net.md_5.bungee.nbt.type.FloatTag;
import se.llbit.nbt.LongArrayTag; import net.md_5.bungee.nbt.type.IntArrayTag;
import se.llbit.nbt.LongTag; import net.md_5.bungee.nbt.type.IntTag;
import se.llbit.nbt.NamedTag; import net.md_5.bungee.nbt.type.ListTag;
import se.llbit.nbt.ShortTag; import net.md_5.bungee.nbt.type.LongArrayTag;
import se.llbit.nbt.SpecificTag; import net.md_5.bungee.nbt.type.LongTag;
import se.llbit.nbt.StringTag; import net.md_5.bungee.nbt.type.ShortTag;
import se.llbit.nbt.Tag; import net.md_5.bungee.nbt.type.StringTag;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TagUtil public final class TagUtil
{ {
public static SpecificTag fromJson(JsonElement json) public static TypedTag fromJson(JsonElement json)
{ {
if ( json instanceof JsonPrimitive ) if ( json instanceof JsonPrimitive )
{ {
@ -64,17 +65,17 @@ public final class TagUtil
return new StringTag( jsonPrimitive.getAsString() ); return new StringTag( jsonPrimitive.getAsString() );
} else if ( jsonPrimitive.isBoolean() ) } else if ( jsonPrimitive.isBoolean() )
{ {
return new ByteTag( jsonPrimitive.getAsBoolean() ? 1 : 0 ); return new ByteTag( (byte) ( jsonPrimitive.getAsBoolean() ? 1 : 0 ) );
} else } else
{ {
throw new IllegalArgumentException( "Unknown JSON primitive: " + jsonPrimitive ); throw new IllegalArgumentException( "Unknown JSON primitive: " + jsonPrimitive );
} }
} else if ( json instanceof JsonObject ) } else if ( json instanceof JsonObject )
{ {
CompoundTag compoundTag = new CompoundTag(); CompoundTag compoundTag = new CompoundTag( new LinkedHashMap<>() );
for ( Map.Entry<String, JsonElement> property : ( (JsonObject) json ).entrySet() ) for ( Map.Entry<String, JsonElement> property : ( (JsonObject) json ).entrySet() )
{ {
compoundTag.add( property.getKey(), fromJson( property.getValue() ) ); compoundTag.getValue().put( property.getKey(), fromJson( property.getValue() ) );
} }
return compoundTag; return compoundTag;
@ -82,103 +83,103 @@ public final class TagUtil
{ {
List<JsonElement> jsonArray = ( (JsonArray) json ).asList(); List<JsonElement> jsonArray = ( (JsonArray) json ).asList();
Integer listType = null; Byte listType = null;
for ( JsonElement jsonEl : jsonArray ) for ( JsonElement jsonEl : jsonArray )
{ {
int type = fromJson( jsonEl ).tagType(); byte type = fromJson( jsonEl ).getId();
if ( listType == null ) if ( listType == null )
{ {
listType = type; listType = type;
} else if ( listType != type ) } else if ( listType != type )
{ {
listType = Tag.TAG_COMPOUND; listType = Tag.COMPOUND;
break; break;
} }
} }
if ( listType == null ) if ( listType == null )
{ {
return new ListTag( Tag.TAG_END, Collections.emptyList() ); return new ListTag( Collections.emptyList(), Tag.END );
} }
SpecificTag listTag; TypedTag listTag;
switch ( listType ) switch ( listType )
{ {
case Tag.TAG_BYTE: case Tag.BYTE:
byte[] bytes = new byte[ jsonArray.size() ]; byte[] bytes = new byte[ jsonArray.size() ];
for ( int i = 0; i < bytes.length; i++ ) for ( int i = 0; i < bytes.length; i++ )
{ {
bytes[i] = (Byte) ( (JsonPrimitive) jsonArray.get( i ) ).getAsNumber(); bytes[i] = (Byte) ( jsonArray.get( i ) ).getAsNumber();
} }
listTag = new ByteArrayTag( bytes ); listTag = new ByteArrayTag( bytes );
break; break;
case Tag.TAG_INT: case Tag.INT:
int[] ints = new int[ jsonArray.size() ]; int[] ints = new int[ jsonArray.size() ];
for ( int i = 0; i < ints.length; i++ ) for ( int i = 0; i < ints.length; i++ )
{ {
ints[i] = (Integer) ( (JsonPrimitive) jsonArray.get( i ) ).getAsNumber(); ints[i] = (Integer) ( jsonArray.get( i ) ).getAsNumber();
} }
listTag = new IntArrayTag( ints ); listTag = new IntArrayTag( ints );
break; break;
case Tag.TAG_LONG: case Tag.LONG:
long[] longs = new long[ jsonArray.size() ]; long[] longs = new long[ jsonArray.size() ];
for ( int i = 0; i < longs.length; i++ ) for ( int i = 0; i < longs.length; i++ )
{ {
longs[i] = (Long) ( (JsonPrimitive) jsonArray.get( i ) ).getAsNumber(); longs[i] = (Long) ( jsonArray.get( i ) ).getAsNumber();
} }
listTag = new LongArrayTag( longs ); listTag = new LongArrayTag( longs );
break; break;
default: default:
List<SpecificTag> tagItems = new ArrayList<>( jsonArray.size() ); List<TypedTag> tagItems = new ArrayList<>( jsonArray.size() );
for ( JsonElement jsonEl : jsonArray ) for ( JsonElement jsonEl : jsonArray )
{ {
SpecificTag subTag = fromJson( jsonEl ); TypedTag subTag = fromJson( jsonEl );
if ( listType == Tag.TAG_COMPOUND && !( subTag instanceof CompoundTag ) ) if ( listType == Tag.COMPOUND && !( subTag instanceof CompoundTag ) )
{ {
CompoundTag wrapper = new CompoundTag(); CompoundTag wrapper = new CompoundTag( new LinkedHashMap<>() );
wrapper.add( "", subTag ); wrapper.getValue().put( "", subTag );
subTag = wrapper; subTag = wrapper;
} }
tagItems.add( subTag ); tagItems.add( subTag );
} }
listTag = new ListTag( listType, tagItems ); listTag = new ListTag( tagItems, listType );
break; break;
} }
return listTag; return listTag;
} else if ( json instanceof JsonNull ) } else if ( json instanceof JsonNull )
{ {
return Tag.END; return EndTag.INSTANCE;
} }
throw new IllegalArgumentException( "Unknown JSON element: " + json ); throw new IllegalArgumentException( "Unknown JSON element: " + json );
} }
public static JsonElement toJson(SpecificTag tag) public static JsonElement toJson(TypedTag tag)
{ {
switch ( tag.tagType() ) switch ( tag.getId() )
{ {
case Tag.TAG_BYTE: case Tag.BYTE:
return new JsonPrimitive( (byte) ( (ByteTag) tag ).getData() ); return new JsonPrimitive( ( (ByteTag) tag ).getValue() );
case Tag.TAG_SHORT: case Tag.SHORT:
return new JsonPrimitive( ( (ShortTag) tag ).getData() ); return new JsonPrimitive( ( (ShortTag) tag ).getValue() );
case Tag.TAG_INT: case Tag.INT:
return new JsonPrimitive( ( (IntTag) tag ).getData() ); return new JsonPrimitive( ( (IntTag) tag ).getValue() );
case Tag.TAG_LONG: case Tag.LONG:
return new JsonPrimitive( ( (LongTag) tag ).getData() ); return new JsonPrimitive( ( (LongTag) tag ).getValue() );
case Tag.TAG_FLOAT: case Tag.FLOAT:
return new JsonPrimitive( ( (FloatTag) tag ).getData() ); return new JsonPrimitive( ( (FloatTag) tag ).getValue() );
case Tag.TAG_DOUBLE: case Tag.DOUBLE:
return new JsonPrimitive( ( (DoubleTag) tag ).getData() ); return new JsonPrimitive( ( (DoubleTag) tag ).getValue() );
case Tag.TAG_BYTE_ARRAY: case Tag.BYTE_ARRAY:
byte[] byteArray = ( (ByteArrayTag) tag ).getData(); byte[] byteArray = ( (ByteArrayTag) tag ).getValue();
JsonArray jsonByteArray = new JsonArray( byteArray.length ); JsonArray jsonByteArray = new JsonArray( byteArray.length );
for ( byte b : byteArray ) for ( byte b : byteArray )
@ -187,21 +188,21 @@ public final class TagUtil
} }
return jsonByteArray; return jsonByteArray;
case Tag.TAG_STRING: case Tag.STRING:
return new JsonPrimitive( ( (StringTag) tag ).getData() ); return new JsonPrimitive( ( (StringTag) tag ).getValue() );
case Tag.TAG_LIST: case Tag.LIST:
List<SpecificTag> items = ( (ListTag) tag ).items; List<TypedTag> items = ( (ListTag) tag ).getValue();
JsonArray jsonList = new JsonArray( items.size() ); JsonArray jsonList = new JsonArray( items.size() );
for ( SpecificTag subTag : items ) for ( TypedTag subTag : items )
{ {
if ( subTag instanceof CompoundTag ) if ( subTag instanceof CompoundTag )
{ {
CompoundTag compound = (CompoundTag) subTag; CompoundTag compound = (CompoundTag) subTag;
if ( compound.size() == 1 ) if ( compound.getValue().size() == 1 )
{ {
SpecificTag first = (SpecificTag) compound.get( "" ); TypedTag first = compound.getValue().get( "" );
if ( !first.isError() ) if ( first != null )
{ {
jsonList.add( toJson( first ) ); jsonList.add( toJson( first ) );
continue; continue;
@ -213,16 +214,13 @@ public final class TagUtil
} }
return jsonList; return jsonList;
case Tag.TAG_COMPOUND: case Tag.COMPOUND:
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
for ( NamedTag subTag : (CompoundTag) tag ) CompoundTag compoundTag = (CompoundTag) tag;
{ compoundTag.getValue().forEach( (key, value) -> jsonObject.add( key, toJson( value ) ) );
jsonObject.add( subTag.name(), toJson( subTag.getTag() ) );
}
return jsonObject; return jsonObject;
case Tag.TAG_INT_ARRAY: case Tag.INT_ARRAY:
int[] intArray = ( (IntArrayTag) tag ).getData(); int[] intArray = ( (IntArrayTag) tag ).getValue();
JsonArray jsonIntArray = new JsonArray( intArray.length ); JsonArray jsonIntArray = new JsonArray( intArray.length );
for ( int i : intArray ) for ( int i : intArray )
@ -231,8 +229,8 @@ public final class TagUtil
} }
return jsonIntArray; return jsonIntArray;
case Tag.TAG_LONG_ARRAY: case Tag.LONG_ARRAY:
long[] longArray = ( (LongArrayTag) tag ).getData(); long[] longArray = ( (LongArrayTag) tag ).getValue();
JsonArray jsonLongArray = new JsonArray( longArray.length ); JsonArray jsonLongArray = new JsonArray( longArray.length );
for ( long l : longArray ) for ( long l : longArray )

View File

@ -5,11 +5,11 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.ProtocolConstants;
import se.llbit.nbt.Tag;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -19,13 +19,13 @@ public class CustomClickAction extends DefinedPacket
{ {
private String id; private String id;
private Tag data; private TypedTag data;
@Override @Override
public void read(ByteBuf buf, Protocol protocol, ProtocolConstants.Direction direction, int protocolVersion) public void read(ByteBuf buf, Protocol protocol, ProtocolConstants.Direction direction, int protocolVersion)
{ {
id = readString( buf ); id = readString( buf );
data = readNullable( (buf0) -> readTag( buf0, protocolVersion ), buf ); data = readNullable( (buf0) -> (TypedTag) readTag( buf0, protocolVersion ), buf );
} }
@Override @Override

View File

@ -7,11 +7,11 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Location; import net.md_5.bungee.protocol.Location;
import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.ProtocolConstants;
import se.llbit.nbt.Tag;
@Data @Data
@NoArgsConstructor @NoArgsConstructor

View File

@ -5,11 +5,11 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Location; import net.md_5.bungee.protocol.Location;
import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.ProtocolConstants;
import se.llbit.nbt.Tag;
@Data @Data
@NoArgsConstructor @NoArgsConstructor

View File

@ -7,13 +7,13 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.api.dialog.Dialog; import net.md_5.bungee.api.dialog.Dialog;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.ChatSerializer; import net.md_5.bungee.protocol.ChatSerializer;
import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Either; import net.md_5.bungee.protocol.Either;
import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.TagUtil; import net.md_5.bungee.protocol.TagUtil;
import se.llbit.nbt.SpecificTag;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -39,7 +39,7 @@ public class ShowDialog extends DefinedPacket
protected static Dialog readDialog(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) protected static Dialog readDialog(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{ {
SpecificTag nbt = (SpecificTag) readTag( buf, protocolVersion ); TypedTag nbt = (TypedTag) readTag( buf, protocolVersion );
JsonElement json = TagUtil.toJson( nbt ); JsonElement json = TagUtil.toJson( nbt );
return ChatSerializer.forVersion( protocolVersion ).getDialogSerializer().deserialize( json ); return ChatSerializer.forVersion( protocolVersion ).getDialogSerializer().deserialize( json );
} }
@ -60,7 +60,7 @@ public class ShowDialog extends DefinedPacket
protected static void writeDialog(Dialog dialog, ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) protected static void writeDialog(Dialog dialog, ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{ {
JsonElement json = ChatSerializer.forVersion( protocolVersion ).getDialogSerializer().toJson( dialog ); JsonElement json = ChatSerializer.forVersion( protocolVersion ).getDialogSerializer().toJson( dialog );
SpecificTag nbt = TagUtil.fromJson( json ); TypedTag nbt = TagUtil.fromJson( json );
writeTag( nbt, buf, protocolVersion ); writeTag( nbt, buf, protocolVersion );
} }

View File

@ -6,16 +6,16 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import net.md_5.bungee.nbt.Tag;
import net.md_5.bungee.nbt.TypedTag;
import net.md_5.bungee.nbt.type.ByteArrayTag;
import net.md_5.bungee.nbt.type.CompoundTag;
import net.md_5.bungee.nbt.type.IntArrayTag;
import net.md_5.bungee.nbt.type.IntTag;
import net.md_5.bungee.nbt.type.ListTag;
import net.md_5.bungee.nbt.type.LongArrayTag;
import net.md_5.bungee.nbt.type.StringTag;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import se.llbit.nbt.ByteArrayTag;
import se.llbit.nbt.CompoundTag;
import se.llbit.nbt.IntArrayTag;
import se.llbit.nbt.IntTag;
import se.llbit.nbt.ListTag;
import se.llbit.nbt.LongArrayTag;
import se.llbit.nbt.SpecificTag;
import se.llbit.nbt.StringTag;
import se.llbit.nbt.Tag;
public class TagUtilTest public class TagUtilTest
{ {
@ -25,7 +25,7 @@ public class TagUtilTest
private static void testDissembleReassemble(String json) private static void testDissembleReassemble(String json)
{ {
JsonElement parsedJson = GSON.fromJson( json, JsonElement.class ); JsonElement parsedJson = GSON.fromJson( json, JsonElement.class );
SpecificTag nbt = TagUtil.fromJson( parsedJson ); TypedTag nbt = TagUtil.fromJson( parsedJson );
JsonElement convertedElement = TagUtil.toJson( nbt ); JsonElement convertedElement = TagUtil.toJson( nbt );
String convertedJson = GSON.toJson( convertedElement ); String convertedJson = GSON.toJson( convertedElement );
@ -35,18 +35,19 @@ public class TagUtilTest
@Test @Test
public void testStringLiteral() public void testStringLiteral()
{ {
// this test only passes if the CompoundTags are backed by a LinkedHashMap
testDissembleReassemble( "{\"text\":\"\",\"extra\":[\"hello\",{\"text\":\"there\",\"color\":\"#ff0000\"},{\"text\":\"friend\",\"font\":\"minecraft:default\"}]}" ); testDissembleReassemble( "{\"text\":\"\",\"extra\":[\"hello\",{\"text\":\"there\",\"color\":\"#ff0000\"},{\"text\":\"friend\",\"font\":\"minecraft:default\"}]}" );
} }
public void testCreateMixedList(JsonArray array) public void testCreateMixedList(JsonArray array)
{ {
SpecificTag tag = TagUtil.fromJson( array ); TypedTag tag = TagUtil.fromJson( array );
assertInstanceOf( ListTag.class, tag ); assertInstanceOf( ListTag.class, tag );
ListTag list = (ListTag) tag; ListTag list = (ListTag) tag;
assertEquals( SpecificTag.TAG_COMPOUND, list.getType() ); assertEquals( Tag.COMPOUND, list.getListType() );
assertEquals( array.size(), list.size() ); assertEquals( array.size(), list.getValue().size() );
for ( int i = 0; i < list.size(); i++ ) for ( int i = 0; i < list.getValue().size(); i++ )
{ {
assertTrue( i < array.size() ); assertTrue( i < array.size() );
@ -77,7 +78,7 @@ public class TagUtilTest
assertInstanceOf( IntTag.class, value ); assertInstanceOf( IntTag.class, value );
IntTag integer = (IntTag) value; IntTag integer = (IntTag) value;
assertEquals( array.get( i ).getAsInt(), integer.getData() ); assertEquals( array.get( i ).getAsInt(), integer.getValue() );
} }
} else if ( primitive.isString() ) } else if ( primitive.isString() )
@ -85,7 +86,7 @@ public class TagUtilTest
assertInstanceOf( StringTag.class, value ); assertInstanceOf( StringTag.class, value );
StringTag string = (StringTag) value; StringTag string = (StringTag) value;
assertEquals( array.get( i ).getAsString(), string.getData() ); assertEquals( array.get( i ).getAsString(), string.getValue() );
} }
} }
} }
@ -129,11 +130,11 @@ public class TagUtilTest
public void testCreateEmptyList() public void testCreateEmptyList()
{ {
JsonArray array = new JsonArray(); JsonArray array = new JsonArray();
SpecificTag tag = TagUtil.fromJson( array ); TypedTag tag = TagUtil.fromJson( array );
assertInstanceOf( ListTag.class, tag ); assertInstanceOf( ListTag.class, tag );
ListTag list = (ListTag) tag; ListTag list = (ListTag) tag;
assertEquals( 0, list.size() ); assertEquals( 0, list.size() );
assertEquals( Tag.TAG_END, list.getType() ); assertEquals( Tag.END, list.getListType() );
} }
@Test @Test
@ -143,16 +144,16 @@ public class TagUtilTest
array.add( ( (byte) 1 ) ); array.add( ( (byte) 1 ) );
array.add( ( (byte) 2 ) ); array.add( ( (byte) 2 ) );
SpecificTag tag = TagUtil.fromJson( array ); TypedTag tag = TagUtil.fromJson( array );
assertInstanceOf( ByteArrayTag.class, tag ); assertInstanceOf( ByteArrayTag.class, tag );
ByteArrayTag byteArray = (ByteArrayTag) tag; ByteArrayTag byteArray = (ByteArrayTag) tag;
assertEquals( 2, byteArray.getData().length ); assertEquals( 2, byteArray.getValue().length );
for ( int i = 0; i < byteArray.getData().length; i++ ) for ( int i = 0; i < byteArray.getValue().length; i++ )
{ {
assertTrue( i < array.size() ); assertTrue( i < array.size() );
byte item = byteArray.getData()[i]; byte item = byteArray.getValue()[i];
assertEquals( array.get( i ).getAsByte(), item ); assertEquals( array.get( i ).getAsByte(), item );
} }
} }
@ -164,16 +165,16 @@ public class TagUtilTest
array.add( 1 ); array.add( 1 );
array.add( 2 ); array.add( 2 );
SpecificTag tag = TagUtil.fromJson( array ); TypedTag tag = TagUtil.fromJson( array );
assertInstanceOf( IntArrayTag.class, tag ); assertInstanceOf( IntArrayTag.class, tag );
IntArrayTag intArray = (IntArrayTag) tag; IntArrayTag intArray = (IntArrayTag) tag;
assertEquals( 2, intArray.getData().length ); assertEquals( 2, intArray.getValue().length );
for ( int i = 0; i < intArray.getData().length; i++ ) for ( int i = 0; i < intArray.getValue().length; i++ )
{ {
assertTrue( i < array.size() ); assertTrue( i < array.size() );
int item = intArray.getData()[i]; int item = intArray.getValue()[i];
assertEquals( array.get( i ).getAsInt(), item ); assertEquals( array.get( i ).getAsInt(), item );
} }
} }
@ -185,16 +186,16 @@ public class TagUtilTest
array.add( 1L ); array.add( 1L );
array.add( 2L ); array.add( 2L );
SpecificTag tag = TagUtil.fromJson( array ); TypedTag tag = TagUtil.fromJson( array );
assertInstanceOf( LongArrayTag.class, tag ); assertInstanceOf( LongArrayTag.class, tag );
LongArrayTag intArray = (LongArrayTag) tag; LongArrayTag intArray = (LongArrayTag) tag;
assertEquals( 2, intArray.getData().length ); assertEquals( 2, intArray.getValue().length );
for ( int i = 0; i < intArray.getData().length; i++ ) for ( int i = 0; i < intArray.getValue().length; i++ )
{ {
assertTrue( i < array.size() ); assertTrue( i < array.size() );
long item = intArray.getData()[i]; long item = intArray.getValue()[i];
assertEquals( array.get( i ).getAsLong(), item ); assertEquals( array.get( i ).getAsLong(), item );
} }
} }
@ -206,10 +207,10 @@ public class TagUtilTest
array.add( "a" ); array.add( "a" );
array.add( "b" ); array.add( "b" );
SpecificTag tag = TagUtil.fromJson( array ); TypedTag tag = TagUtil.fromJson( array );
assertInstanceOf( ListTag.class, tag ); assertInstanceOf( ListTag.class, tag );
ListTag list = (ListTag) tag; ListTag list = (ListTag) tag;
assertEquals( SpecificTag.TAG_STRING, list.getType() ); assertEquals( Tag.STRING, list.getListType() );
assertEquals( 2, list.size() ); assertEquals( 2, list.size() );
for ( int i = 0; i < list.size(); i++ ) for ( int i = 0; i < list.size(); i++ )
@ -220,7 +221,7 @@ public class TagUtilTest
assertInstanceOf( StringTag.class, item ); assertInstanceOf( StringTag.class, item );
StringTag string = (StringTag) item; StringTag string = (StringTag) item;
assertEquals( array.get( i ).getAsString(), string.getData() ); assertEquals( array.get( i ).getAsString(), string.getValue() );
} }
} }
} }

View File

@ -47,7 +47,6 @@ import net.md_5.bungee.protocol.packet.TabCompleteRequest;
import net.md_5.bungee.protocol.packet.TabCompleteResponse; import net.md_5.bungee.protocol.packet.TabCompleteResponse;
import net.md_5.bungee.protocol.packet.UnsignedClientCommand; import net.md_5.bungee.protocol.packet.UnsignedClientCommand;
import net.md_5.bungee.util.AllowedCharacters; import net.md_5.bungee.util.AllowedCharacters;
import se.llbit.nbt.SpecificTag;
public class UpstreamBridge extends PacketHandler public class UpstreamBridge extends PacketHandler
{ {
@ -385,7 +384,7 @@ public class UpstreamBridge extends PacketHandler
@Override @Override
public void handle(CustomClickAction customClickAction) throws Exception public void handle(CustomClickAction customClickAction) throws Exception
{ {
CustomClickEvent event = new CustomClickEvent( con, customClickAction.getId(), TagUtil.toJson( (SpecificTag) customClickAction.getData() ) ); CustomClickEvent event = new CustomClickEvent( con, customClickAction.getId(), TagUtil.toJson( customClickAction.getData() ) );
if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
{ {
throw CancelSendSignal.INSTANCE; throw CancelSendSignal.INSTANCE;

View File

@ -4,12 +4,13 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.nbt.NamedTag;
import net.md_5.bungee.nbt.limit.NBTLimiter;
import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.ProtocolConstants;
import se.llbit.nbt.NamedTag;
import se.llbit.nbt.Tag;
/** /**
* Class to rewrite integers within packets. * Class to rewrite integers within packets.
@ -275,10 +276,13 @@ public abstract class EntityMap
DefinedPacket.readVarInt( packet ); DefinedPacket.readVarInt( packet );
break; break;
case 13: case 13:
Tag tag = NamedTag.read( new DataInputStream( new ByteBufInputStream( packet ) ) ); NamedTag tag = new NamedTag();
if ( tag.isError() ) try
{ {
throw new RuntimeException( tag.error() ); tag.read( new DataInputStream( new ByteBufInputStream( packet ) ), NBTLimiter.unlimitedSize() );
} catch ( IOException ioException )
{
throw new RuntimeException( ioException );
} }
break; break;
case 15: case 15:
@ -321,10 +325,13 @@ public abstract class EntityMap
{ {
packet.readerIndex( position ); packet.readerIndex( position );
Tag tag = NamedTag.read( new DataInputStream( new ByteBufInputStream( packet ) ) ); NamedTag tag = new NamedTag();
if ( tag.isError() ) try
{ {
throw new RuntimeException( tag.error() ); tag.read( new DataInputStream( new ByteBufInputStream( packet ) ), NBTLimiter.unlimitedSize() );
} catch ( IOException ioException )
{
throw new RuntimeException( ioException );
} }
} }
} }