mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-12 10:38:34 +00:00
finish overhaul of NBT stream api
This commit is contained in:
@ -1,90 +0,0 @@
|
||||
package com.boydti.fawe.jnbt;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class NBTStreamer {
|
||||
private final NBTInputStream is;
|
||||
private final HashMap<String, BiConsumer> readers;
|
||||
|
||||
public NBTStreamer(NBTInputStream stream) {
|
||||
this.is = stream;
|
||||
readers = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire stream and runs the applicable readers
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readFully() throws IOException {
|
||||
is.readNamedTagLazy(readers::get);
|
||||
is.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the stream until all readers have been used<br>
|
||||
* - Use readFully if you expect a reader to appear more than once
|
||||
* - Can exit early without having reading the entire file
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readQuick() throws IOException {
|
||||
try {
|
||||
is.readNamedTagLazy(node -> {
|
||||
if (readers.isEmpty()) {
|
||||
throw FaweCache.MANUAL;
|
||||
}
|
||||
return readers.remove(node);
|
||||
});
|
||||
} catch (FaweException ignore) {}
|
||||
is.close();
|
||||
}
|
||||
|
||||
public <T, V> void addReader(String node, BiConsumer<T, V> run) {
|
||||
if (run instanceof NBTStreamReader) {
|
||||
((NBTStreamReader) run).init(node);
|
||||
}
|
||||
readers.put(node, run);
|
||||
}
|
||||
|
||||
public static abstract class NBTStreamReader<T, V> implements BiConsumer<T, V> {
|
||||
private String node;
|
||||
|
||||
public void init(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class ByteReader implements BiConsumer<Integer, Integer> {
|
||||
@Override
|
||||
public void accept(Integer index, Integer value) {
|
||||
run(index, value);
|
||||
}
|
||||
|
||||
public abstract void run(int index, int byteValue);
|
||||
}
|
||||
|
||||
public interface LazyReader extends BiConsumer<Integer, DataInputStream> {}
|
||||
|
||||
public static abstract class LongReader implements BiConsumer<Integer, Long> {
|
||||
@Override
|
||||
public void accept(Integer index, Long value) {
|
||||
run(index, value);
|
||||
}
|
||||
|
||||
public abstract void run(int index, long byteValue);
|
||||
}
|
||||
}
|
@ -81,10 +81,10 @@ public class SchematicStreamer extends NBTStreamer {
|
||||
}
|
||||
};
|
||||
|
||||
addReader("Schematic.Blocks.?", idInit);
|
||||
addReader("Schematic.Data.?", dataInit);
|
||||
addReader("Schematic.AddBlocks.?", addInit);
|
||||
addReader("Schematic.Blocks.#", new ByteReader() {
|
||||
addReader("Schematic.Blocks", NBTStreamer.ReadType.INFO, idInit);
|
||||
addReader("Schematic.Data", NBTStreamer.ReadType.INFO, dataInit);
|
||||
addReader("Schematic.AddBlocks", NBTStreamer.ReadType.INFO, addInit);
|
||||
addReader("Schematic.Blocks", NBTStreamer.ReadType.ELEM, new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
try {
|
||||
@ -94,7 +94,7 @@ public class SchematicStreamer extends NBTStreamer {
|
||||
}
|
||||
}
|
||||
});
|
||||
addReader("Schematic.Data.#", new ByteReader() {
|
||||
addReader("Schematic.Data", NBTStreamer.ReadType.ELEM, new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
try {
|
||||
@ -104,7 +104,7 @@ public class SchematicStreamer extends NBTStreamer {
|
||||
}
|
||||
}
|
||||
});
|
||||
addReader("Schematic.AddBlocks.#", new ByteReader() {
|
||||
addReader("Schematic.AddBlocks", NBTStreamer.ReadType.ELEM, new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
if (value != 0) {
|
||||
@ -134,13 +134,13 @@ public class SchematicStreamer extends NBTStreamer {
|
||||
if (fc == null) setupClipboard(length * width * height);
|
||||
}
|
||||
};
|
||||
addReader("Schematic.AWEBiomes.?", initializer23);
|
||||
addReader("Schematic.Biomes.?", initializer23);
|
||||
addReader("Schematic.AWEBiomes.#", biomeReader); // AWE stores as an int[]
|
||||
addReader("Schematic.Biomes.#", biomeReader); // FAWE stores as a byte[] (4x smaller)
|
||||
addReader("Schematic.AWEBiomes", NBTStreamer.ReadType.INFO,initializer23);
|
||||
addReader("Schematic.Biomes", NBTStreamer.ReadType.INFO,initializer23);
|
||||
addReader("Schematic.AWEBiomes", NBTStreamer.ReadType.ELEM,biomeReader); // AWE stores as an int[]
|
||||
addReader("Schematic.Biomes", NBTStreamer.ReadType.ELEM,biomeReader); // FAWE stores as a byte[] (4x smaller)
|
||||
|
||||
// Tiles
|
||||
addReader("Schematic.TileEntities.#", (BiConsumer<Integer, CompoundTag>) (index, value) -> {
|
||||
addReader("Schematic.TileEntities", NBTStreamer.ReadType.ELEM,(BiConsumer<Integer, CompoundTag>) (index, value) -> {
|
||||
if (fc == null) {
|
||||
setupClipboard(0);
|
||||
}
|
||||
@ -150,7 +150,7 @@ public class SchematicStreamer extends NBTStreamer {
|
||||
fc.setTile(x, y, z, value);
|
||||
});
|
||||
// Entities
|
||||
addReader("Schematic.Entities.#", (BiConsumer<Integer, CompoundTag>) (index, compound) -> {
|
||||
addReader("Schematic.Entities", NBTStreamer.ReadType.ELEM,(BiConsumer<Integer, CompoundTag>) (index, compound) -> {
|
||||
if (fc == null) {
|
||||
setupClipboard(0);
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public interface ElemReader<T> extends StreamReader<T> {
|
||||
void apply(int index, T value);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public interface InfoReader extends StreamReader<Integer> {
|
||||
void apply(int length, int type);
|
||||
|
||||
@Override
|
||||
default void apply(int i, Integer value) {
|
||||
apply(i, value.intValue());
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public interface IntValueReader extends ValueReader<Integer> {
|
||||
void applyInt(int index, int value);
|
||||
|
||||
@Override
|
||||
default void apply(int index, Integer value) {
|
||||
applyInt(index, value);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
|
||||
public interface LazyReader extends StreamReader<DataInputStream> {
|
||||
void apply(int index, NBTInputStream stream);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public interface LongValueReader extends ValueReader<Long> {
|
||||
void applyLong(int index, long value);
|
||||
|
||||
@Override
|
||||
default void apply(int index, Long value) {
|
||||
applyLong(index, value);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public enum ReaderType {
|
||||
VALUE,
|
||||
INFO,
|
||||
ELEM,
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class StreamDelegate {
|
||||
private static final byte[][] ZERO_KEYS = new byte[0][];
|
||||
private static final StreamDelegate[] ZERO_VALUES = new StreamDelegate[0];
|
||||
|
||||
private byte[] buffer;
|
||||
private byte[][] keys;
|
||||
private StreamDelegate[] values;
|
||||
|
||||
private LazyReader lazyReader;
|
||||
private ElemReader elemReader;
|
||||
private InfoReader infoReader;
|
||||
private ValueReader valueReader;
|
||||
|
||||
public StreamDelegate() {
|
||||
keys = ZERO_KEYS;
|
||||
values = ZERO_VALUES;
|
||||
}
|
||||
|
||||
public StreamDelegate addAndGetParent(String name) {
|
||||
add(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate add() {
|
||||
return add("");
|
||||
}
|
||||
|
||||
public StreamDelegate add(String name) {
|
||||
return add(name, new StreamDelegate());
|
||||
}
|
||||
|
||||
private StreamDelegate add(String name, StreamDelegate scope) {
|
||||
if (valueReader != null) {
|
||||
System.out.println("Scope " + name + " | " + scope + " may not run, as the stream is only read once, and a value reader is already set");
|
||||
}
|
||||
byte[] bytes = name.getBytes(NBTConstants.CHARSET);
|
||||
int maxSize = bytes.length;
|
||||
|
||||
byte[][] tmpKeys = new byte[keys.length + 1][];
|
||||
StreamDelegate[] tmpValues = new StreamDelegate[keys.length + 1];
|
||||
tmpKeys[keys.length] = bytes;
|
||||
tmpValues[keys.length] = scope;
|
||||
|
||||
int i = 0;
|
||||
for (; i < keys.length; i++) {
|
||||
byte[] key = keys[i];
|
||||
if (key.length >= bytes.length) {
|
||||
tmpKeys[i] = bytes;
|
||||
tmpValues[i] = scope;
|
||||
break;
|
||||
}
|
||||
tmpKeys[i] = key;
|
||||
tmpValues[i] = values[i];
|
||||
maxSize = Math.max(maxSize, key.length);
|
||||
}
|
||||
for (; i < keys.length; i++) {
|
||||
byte[] key = keys[i];
|
||||
tmpKeys[i + 1] = key;
|
||||
tmpValues[i + 1] = values[i];
|
||||
maxSize = Math.max(maxSize, key.length);
|
||||
}
|
||||
|
||||
this.keys = tmpKeys;
|
||||
this.values = tmpValues;
|
||||
if (this.buffer == null || buffer.length < maxSize) {
|
||||
buffer = new byte[maxSize];
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
public StreamDelegate get0() {
|
||||
if (keys.length > 0 && keys[0].length == 0) {
|
||||
return values[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public StreamDelegate get(DataInputStream is) throws IOException {
|
||||
int nameLength = is.readShort() & 0xFFFF;
|
||||
if (nameLength == 0 && keys.length > 0 && keys[0].length == 0) {
|
||||
return values[0];
|
||||
}
|
||||
if (nameLength > buffer.length) {
|
||||
is.skipBytes(nameLength);
|
||||
return null;
|
||||
}
|
||||
int index = 0;
|
||||
outer:
|
||||
switch (keys.length) {
|
||||
case 0:
|
||||
break;
|
||||
default: {
|
||||
for (; index < keys.length; index++) {
|
||||
byte[] key = keys[index];
|
||||
if (key.length < nameLength) continue;
|
||||
if (key.length == nameLength) {
|
||||
break;
|
||||
} else {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
if (index != keys.length - 1) {
|
||||
int max;
|
||||
for (max = index + 1; max < keys.length;) {
|
||||
byte[] key = keys[max];
|
||||
if (key.length == nameLength) {
|
||||
max++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (index != max) {
|
||||
is.readFully(buffer, 0, nameLength);
|
||||
middle:
|
||||
for (int i = index; i < max; i++) {
|
||||
byte[] key = keys[i];
|
||||
for (int j = 0; j < nameLength; j++) {
|
||||
if (buffer[j] != key[j]) {
|
||||
continue middle;
|
||||
}
|
||||
}
|
||||
return values[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 1: {
|
||||
byte[] key = keys[index];
|
||||
if (key.length == nameLength) {
|
||||
int i = 0;
|
||||
for (; nameLength > 0; nameLength--, i++) {
|
||||
byte b = is.readByte();
|
||||
buffer[i] = b;
|
||||
if (b != key[i]) {
|
||||
nameLength--;
|
||||
break outer;
|
||||
}
|
||||
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
is.skipBytes(nameLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
public StreamDelegate withLong(LongValueReader valueReader) {
|
||||
return withValue(valueReader);
|
||||
}
|
||||
|
||||
public StreamDelegate withInt(IntValueReader valueReader) {
|
||||
return withValue(valueReader);
|
||||
}
|
||||
|
||||
public StreamDelegate withValue(ValueReader valueReader) {
|
||||
if (keys.length != 0) {
|
||||
System.out.println("Reader " + valueReader + " may not run, as the stream is only read once, and a value reader is already set");
|
||||
}
|
||||
this.valueReader = valueReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate withStream(LazyReader lazyReader) {
|
||||
this.lazyReader = lazyReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate withElem(ElemReader elemReader) {
|
||||
this.elemReader = elemReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate withInfo(InfoReader infoReader) {
|
||||
this.infoReader = infoReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void acceptRoot(NBTInputStream is, int type, int depth) throws IOException {
|
||||
if (lazyReader != null) {
|
||||
lazyReader.apply(0, is);
|
||||
} else if (elemReader != null) {
|
||||
Object raw = is.readTagPaylodRaw(type, depth);
|
||||
elemReader.apply(0, raw);
|
||||
} else if (valueReader != null) {
|
||||
Object raw = is.readTagPaylodRaw(type, depth);
|
||||
valueReader.apply(0, raw);
|
||||
} else {
|
||||
is.readTagPaylodLazy(type, depth + 1, this);
|
||||
}
|
||||
}
|
||||
|
||||
public ValueReader getValueReader() {
|
||||
return valueReader;
|
||||
}
|
||||
|
||||
public ElemReader getElemReader() {
|
||||
return elemReader;
|
||||
}
|
||||
|
||||
public void acceptInfo(int length, int type) {
|
||||
if (infoReader != null) {
|
||||
infoReader.apply(length, type);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean acceptLazy(int length, NBTInputStream is) {
|
||||
if (lazyReader != null) {
|
||||
lazyReader.apply(length, is);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public interface StreamReader<T> {
|
||||
void apply(int i, T value);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
public interface ValueReader<T> extends StreamReader<T> {
|
||||
void apply(int index, T value);
|
||||
|
||||
default void applyInt(int index, int value) {
|
||||
apply(index, (T) (Integer) value);
|
||||
}
|
||||
|
||||
default void applyLong(int index, long value) {
|
||||
apply(index, (T) (Long) value);
|
||||
}
|
||||
|
||||
default void applyFloat(int index, float value) {
|
||||
apply(index, (T) (Float) value);
|
||||
}
|
||||
|
||||
default void applyDouble(int index, double value) {
|
||||
apply(index, (T) (Double) value);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user