feat: SequentialNBTReader
This commit is contained in:
3 files changed
+273
-3
No files matched your search
@@ -21,6 +21,7 @@ java {
|
||||
|
||||
dependencies {
|
||||
compileOnlyApi(libs.bundles.adventure)
|
||||
compileOnlyApi(libs.bundles.adventure.serializers)
|
||||
implementation(libs.adventure.api)
|
||||
api(project(":patch:adventure-text-serializer-gson", "shadow")) {
|
||||
excludeAdventure()
|
||||
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
package com.github.retrooper.packetevents.protocol.nbt.serializer;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBT;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public final class SequentialNBTReader implements Iterator<NBT> {
|
||||
|
||||
private static final NBTLimiter DUMMY_LIMITER = NBTLimiter.noop();
|
||||
private static final Map<NBTType<?>, TagSkip> TAG_SKIPS = new HashMap<>(16, 1);
|
||||
|
||||
private final DataInput stream;
|
||||
private NBTType<?> nextType = null;
|
||||
private boolean hasReadType = false;
|
||||
private NBT lastRead = null;
|
||||
|
||||
public SequentialNBTReader(DataInput stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
checkRead();
|
||||
if (!hasReadType) {
|
||||
try {
|
||||
nextType = DefaultNBTSerializer.INSTANCE.readTagType(DUMMY_LIMITER, stream);
|
||||
hasReadType = true;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return nextType != NBTType.END;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBT next() {
|
||||
checkRead();
|
||||
if (!hasReadType) {
|
||||
hasNext();
|
||||
}
|
||||
|
||||
try {
|
||||
hasReadType = false;
|
||||
DefaultNBTSerializer.INSTANCE.readTagName(DUMMY_LIMITER, stream);
|
||||
if (nextType == NBTType.COMPOUND) {
|
||||
lastRead = new Compound(stream);
|
||||
} else if (nextType == NBTType.LIST) {
|
||||
lastRead = new List(stream);
|
||||
} else {
|
||||
lastRead = DefaultNBTSerializer.INSTANCE.readTag(DUMMY_LIMITER, stream, nextType);
|
||||
}
|
||||
|
||||
return lastRead;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRead() {
|
||||
if (lastRead == null) return;
|
||||
if (lastRead instanceof Iterator && ((Iterator<NBT>) lastRead).hasNext()) {
|
||||
throw new IllegalStateException("Previous nbt has not been read completely");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Compound extends NBT implements Iterator<Map.Entry<String, NBT>>, Skippable {
|
||||
|
||||
private boolean hasReadCompletely = false;
|
||||
private final SequentialNBTReader reader;
|
||||
|
||||
private Compound(DataInput stream) {
|
||||
this.reader = new SequentialNBTReader(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTType<?> getType() {
|
||||
return NBTType.COMPOUND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return this == other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBT copy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
hasReadCompletely = !reader.hasNext();
|
||||
return hasReadCompletely;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<String, NBT> next() {
|
||||
if (!reader.hasReadType) {
|
||||
hasNext();
|
||||
}
|
||||
|
||||
if (hasReadCompletely) {
|
||||
throw new IllegalStateException("No more elements in compound");
|
||||
}
|
||||
|
||||
try {
|
||||
reader.hasReadType = false;
|
||||
String name = DefaultNBTSerializer.INSTANCE.readTagName(DUMMY_LIMITER, reader.stream);
|
||||
if (reader.nextType == NBTType.COMPOUND) {
|
||||
reader.lastRead = new Compound(reader.stream);
|
||||
} else if (reader.nextType == NBTType.LIST) {
|
||||
reader.lastRead = new List(reader.stream);
|
||||
} else {
|
||||
reader.lastRead = DefaultNBTSerializer.INSTANCE.readTag(DUMMY_LIMITER, reader.stream, reader.nextType);
|
||||
}
|
||||
|
||||
return new AbstractMap.SimpleEntry<>(name, reader.lastRead);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip() {
|
||||
try {
|
||||
if (reader.lastRead instanceof Skippable) {
|
||||
((Skippable) reader.lastRead).skip();
|
||||
}
|
||||
|
||||
TAG_SKIPS.get(NBTType.COMPOUND).skip(reader.stream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
hasReadCompletely = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class List extends NBT implements Iterator<NBT>, Skippable {
|
||||
|
||||
private final SequentialNBTReader reader;
|
||||
private final NBTType<?> listType;
|
||||
private int remaining;
|
||||
|
||||
private List(DataInput stream) {
|
||||
this.reader = new SequentialNBTReader(stream);
|
||||
try {
|
||||
this.listType = DefaultNBTSerializer.INSTANCE.readTagType(DUMMY_LIMITER, stream);
|
||||
this.remaining = stream.readInt();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTType<?> getType() {
|
||||
return NBTType.LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return this == other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBT copy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return remaining > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBT next() {
|
||||
if (!hasNext()) {
|
||||
throw new IllegalStateException("No more elements in list");
|
||||
}
|
||||
remaining--;
|
||||
return reader.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip() {
|
||||
try {
|
||||
if (reader.lastRead instanceof Skippable) {
|
||||
((Skippable) reader.lastRead).skip();
|
||||
}
|
||||
|
||||
for (int i = 0; i < remaining; i++) {
|
||||
TAG_SKIPS.get(listType).skip(reader.stream);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface Skippable {
|
||||
void skip();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface TagSkip {
|
||||
void skip(DataInput in) throws IOException;
|
||||
}
|
||||
|
||||
static {
|
||||
TAG_SKIPS.put(NBTType.BYTE, in -> in.skipBytes(1));
|
||||
TAG_SKIPS.put(NBTType.SHORT, in -> in.skipBytes(2));
|
||||
TAG_SKIPS.put(NBTType.INT, in -> in.skipBytes(4));
|
||||
TAG_SKIPS.put(NBTType.LONG, in -> in.skipBytes(8));
|
||||
TAG_SKIPS.put(NBTType.FLOAT, in -> in.skipBytes(4));
|
||||
TAG_SKIPS.put(NBTType.DOUBLE, in -> in.skipBytes(8));
|
||||
TAG_SKIPS.put(NBTType.BYTE_ARRAY, in -> {
|
||||
int length = in.readInt();
|
||||
in.skipBytes(length);
|
||||
});
|
||||
TAG_SKIPS.put(NBTType.STRING, in -> {
|
||||
int length = in.readUnsignedShort();
|
||||
in.skipBytes(length);
|
||||
});
|
||||
TAG_SKIPS.put(NBTType.LIST, in -> {
|
||||
NBTType<?> listType = DefaultNBTSerializer.INSTANCE.readTagType(DUMMY_LIMITER, in);
|
||||
int length = in.readInt();
|
||||
for (int i = 0; i < length; i++) {
|
||||
TAG_SKIPS.get(listType).skip(in);
|
||||
}
|
||||
});
|
||||
TAG_SKIPS.put(NBTType.COMPOUND, in -> {
|
||||
NBTType<?> valueType;
|
||||
while ((valueType = DefaultNBTSerializer.INSTANCE.readTagType(DUMMY_LIMITER, in)) != NBTType.END) {
|
||||
|
||||
int utfLen = in.readUnsignedShort();
|
||||
in.skipBytes(utfLen);
|
||||
|
||||
TAG_SKIPS.get(valueType).skip(in);
|
||||
}
|
||||
});
|
||||
TAG_SKIPS.put(NBTType.INT_ARRAY, in -> {
|
||||
int length = in.readInt();
|
||||
in.skipBytes(length * 4);
|
||||
});
|
||||
TAG_SKIPS.put(NBTType.LONG_ARRAY, in -> {
|
||||
int length = in.readInt();
|
||||
in.skipBytes(length * 8);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -22,6 +22,7 @@ import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.*;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.serializer.DefaultNBTSerializer;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.serializer.SequentialNBTReader;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -35,11 +36,10 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class MappingHelper {
|
||||
|
||||
public static NBTCompound decompress(final String path) {
|
||||
NBTLimiter limiter = new NBTLimiter();
|
||||
public static SequentialNBTReader decompress(final String path) {
|
||||
try (final DataInputStream dataInput = new DataInputStream(new GZIPInputStream(new BufferedInputStream(
|
||||
PacketEvents.getAPI().getSettings().getResourceProvider().apply( "assets/" + path + ".nbt"))))) {
|
||||
return (NBTCompound) DefaultNBTSerializer.INSTANCE.deserializeTag(limiter, dataInput);
|
||||
return new SequentialNBTReader(dataInput);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot find resource file " + path + ".nbt", e);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user