- * Firstly, closes all cache writers (which adds the END identifier to each and fills the cache byte arrays on this instance) - * If required, creates all missing palettes first (as needed by all remaining data). - * At last writes all missing data (block states, tile entities, biomes, entities). - * - * @param clipboard The clipboard to write into. - * @throws IOException on I/O error. - */ - private void readRemainingDataCache(Clipboard clipboard) throws IOException { - byte identifier; - if (this.paletteCacheWriter != null) { - this.paletteCacheWriter.close(); - } - if (this.dataCacheWriter != null) { - this.dataCacheWriter.close(); - } - if (this.paletteCache != null) { - try (final DataInputStream cacheStream = new DataInputStream(new FastBufferedInputStream( - new LZ4BlockInputStream(new FastBufferedInputStream(new ByteArrayInputStream(this.paletteCache)))))) { - while ((identifier = cacheStream.readByte()) != CACHE_IDENTIFIER_END) { - if (identifier == CACHE_IDENTIFIER_BLOCK) { - this.readPaletteMap(cacheStream, this.provideBlockPaletteInitializer()); - continue; - } - if (identifier == CACHE_IDENTIFIER_BIOMES) { - this.readPaletteMap(cacheStream, this.provideBiomePaletteInitializer()); - continue; - } - throw new IOException("invalid cache state - got identifier: 0x" + identifier); - } - } - } - try (final DataInputStream cacheStream = new DataInputStream(new FastBufferedInputStream( - new LZ4BlockInputStream(new FastBufferedInputStream(new ByteArrayInputStream(this.dataCache))))); - final NBTInputStream cacheNbtIn = new NBTInputStream(cacheStream)) { - while ((identifier = cacheStream.readByte()) != CACHE_IDENTIFIER_END) { - switch (identifier) { - case CACHE_IDENTIFIER_BLOCK -> this.readPaletteData(cacheStream, this.getBlockWriter(clipboard)); - case CACHE_IDENTIFIER_BIOMES -> this.readPaletteData(cacheStream, this.getBiomeWriter(clipboard)); - case CACHE_IDENTIFIER_ENTITIES -> { - cacheStream.skipNBytes(1); // list child type (TAG_Compound) - this.readEntityContainers( - cacheStream, - cacheNbtIn, - DataFixer.FixTypes.ENTITY, - this.provideEntityTransformer(clipboard) - ); - } - case CACHE_IDENTIFIER_BLOCK_TILE_ENTITIES -> { - cacheStream.skipNBytes(1); // list child type (TAG_Compound) - this.readEntityContainers( - cacheStream, - cacheNbtIn, - DataFixer.FixTypes.BLOCK_ENTITY, - this.provideTileEntityTransformer(clipboard) - ); - } - default -> throw new IOException("invalid cache state - got identifier: 0x" + identifier); - } - } - } - } - - /** - * Reset the main stream of this clipboard and reads all remaining data that could not be read or fixed yet. - * Might need two iterations if the DataVersion tag is after the Blocks tag while the Palette inside the Blocks tag is not - * at the first position. - * - * @param clipboard The clipboard to write into. - * @throws IOException on I/O error. - */ - private void readRemainingDataReset(Clipboard clipboard) throws IOException { - byte type; - String tag; - outer: - while (!this.remainingTags.isEmpty()) { - this.reset(); - skipHeader(this.dataInputStream); - while ((type = dataInputStream.readByte()) != NBTConstants.TYPE_END) { - tag = dataInputStream.readUTF(); - byte b = tag.equals("Blocks") ? CACHE_IDENTIFIER_BLOCK : - tag.equals("Biomes") ? CACHE_IDENTIFIER_BIOMES : - tag.equals("Entities") ? CACHE_IDENTIFIER_ENTITIES : - CACHE_IDENTIFIER_END; - if (!this.remainingTags.remove(b)) { - this.nbtInputStream.readTagPayloadLazy(type, 0); - continue; - } - switch (tag) { - case "Blocks" -> readBlocks(clipboard); - case "Biomes" -> readBiomes(clipboard); - case "Entities" -> readEntities(clipboard); - default -> this.nbtInputStream.readTagPayloadLazy(type, 0); // Should never happen, but just in case - } - if (this.remainingTags.isEmpty()) { - break outer; - } - } - } - } - - /** - * {@inheritDoc} - *
- * Requires {@link #read()}, {@link #read(UUID)} or {@link #read(UUID, Function)} to be called before.
- */
- @Override
- public OptionalInt getDataVersion() {
- return this.dataVersion > -1 ? OptionalInt.of(this.dataVersion) : OptionalInt.empty();
- }
-
- private void readBlocks(Clipboard target) throws IOException {
- this.blockPalette = new BlockState[BlockTypesCache.states.length];
- readPalette(
- target != null,
- CACHE_IDENTIFIER_BLOCK,
- () -> this.blockPalette[0] != null,
- this.provideBlockPaletteInitializer(),
- this.getBlockWriter(target),
- (type, tag) -> {
- if (!tag.equals("BlockEntities")) {
- try {
- this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
- } catch (IOException e) {
- LOGGER.error("Failed to skip additional tag", e);
- }
- return;
- }
- try {
- this.readTileEntities(target);
- } catch (IOException e) {
- LOGGER.warn("Failed to read tile entities", e);
- }
- }
- );
- }
-
- private void readBiomes(Clipboard target) throws IOException {
- this.biomePalette = new BiomeType[BiomeType.REGISTRY.size()];
- readPalette(
- target != null,
- CACHE_IDENTIFIER_BIOMES,
- () -> this.biomePalette[0] != null,
- this.provideBiomePaletteInitializer(),
- this.getBiomeWriter(target),
- (type, tag) -> {
- try {
- this.nbtInputStream.readTagPayloadLazy(type, 0);
- } catch (IOException e) {
- LOGGER.error("Failed to skip additional tag in biome container: {}", tag, e);
- }
- }
- );
- }
-
- private void readEntities(@Nullable Clipboard target) throws IOException {
- if (target == null || this.dataFixer == null) {
- if (supportsReset()) {
- this.remainingTags.add(CACHE_IDENTIFIER_ENTITIES);
- this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
- return;
- }
- // Easier than streaming for now
- final NBTOutputStream cacheStream = new NBTOutputStream(this.getDataCacheWriter());
- cacheStream.writeByte(CACHE_IDENTIFIER_ENTITIES);
- cacheStream.writeTagPayload(this.nbtInputStream.readTagPayload(NBTConstants.TYPE_LIST, 0));
- return;
- }
- if (this.dataInputStream.read() != NBTConstants.TYPE_COMPOUND) {
- throw new IOException("Expected a compound block for entity");
- }
- this.readEntityContainers(
- this.dataInputStream, this.nbtInputStream, DataFixer.FixTypes.ENTITY, this.provideEntityTransformer(target)
- );
- }
-
- private void readTileEntities(Clipboard target) throws IOException {
- if (target == null || this.dataFixer == null) {
- if (supportsReset()) {
- this.remainingTags.add(CACHE_IDENTIFIER_BLOCK); // use block identifier, as this method will be called by
- // readBlocks again
- this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
- return;
- }
- // Easier than streaming for now
- final NBTOutputStream cacheStream = new NBTOutputStream(this.getDataCacheWriter());
- cacheStream.writeByte(CACHE_IDENTIFIER_BLOCK_TILE_ENTITIES);
- cacheStream.writeTagPayload(this.nbtInputStream.readTagPayload(NBTConstants.TYPE_LIST, 0));
- return;
- }
- if (this.dataInputStream.read() != NBTConstants.TYPE_COMPOUND) {
- throw new IOException("Expected a compound block for tile entity");
- }
- this.readEntityContainers(
- this.dataInputStream,
- this.nbtInputStream,
- DataFixer.FixTypes.BLOCK_ENTITY,
- this.provideTileEntityTransformer(target)
- );
- }
-
- private void readEntityContainers(
- DataInputStream stream,
- NBTInputStream nbtStream,
- DataFixer.FixType
- * This method expects that the identifier ({@link NBTConstants#TYPE_COMPOUND}) is already consumed from the stream.
- *
- * @param stream The stream to read the data from.
- * @param initializer The initializer called for each entry with its index and backed value.
- * @return {@code true} if the mapping could be read, {@code false} otherwise (e.g. DataFixer is not yet available).
- * @throws IOException on I/O error.
- */
- private boolean readPaletteMap(DataInputStream stream, PaletteInitializer initializer) throws IOException {
- if (this.dataFixer == null) {
- return false;
- }
- while (stream.readByte() != NBTConstants.TYPE_END) {
- String value = stream.readUTF();
- char index = (char) stream.readInt();
- initializer.initialize(index, value);
- }
- return true;
- }
-
- private void indexToPosition(int index, PositionConsumer supplier) {
- int y = index / (dimensions.x() * dimensions.z());
- int remainder = index - (y * dimensions.x() * dimensions.z());
- int z = remainder / dimensions.x();
- int x = remainder - z * dimensions.x();
- supplier.accept(x, y, z);
- }
-
- private PaletteDataApplier getBlockWriter(Clipboard target) {
- if (target instanceof LinearClipboard linearClipboard) {
- return (index, ordinal) -> linearClipboard.setBlock(index, this.blockPalette[ordinal]);
- }
- return (index, ordinal) -> indexToPosition(index, (x, y, z) -> target.setBlock(x, y, z, this.blockPalette[ordinal]));
- }
-
- private PaletteDataApplier getBiomeWriter(Clipboard target) {
- return (index, ordinal) -> indexToPosition(index, (x, y, z) -> target.setBiome(x, y, z, this.biomePalette[ordinal]));
- }
-
- private PaletteInitializer provideBlockPaletteInitializer() {
- return (index, value) -> {
- if (this.dataFixer == null) {
- throw new IllegalStateException("Can't read block palette map if DataFixer is not yet available");
- }
- value = dataFixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, value);
- try {
- this.blockPalette[index] = BlockState.get(value);
- } catch (InputParseException e) {
- LOGGER.warn("Invalid BlockState in palette: {}. Block will be replaced with air.", value);
- this.blockPalette[index] = BlockTypes.AIR.getDefaultState();
- }
- };
- }
-
- private PaletteInitializer provideBiomePaletteInitializer() {
- return (index, value) -> {
- if (this.dataFixer == null) {
- throw new IllegalStateException("Can't read biome palette map if DataFixer is not yet available");
- }
- value = dataFixer.fixUp(DataFixer.FixTypes.BIOME, value);
- BiomeType biomeType = BiomeTypes.get(value);
- if (biomeType == null) {
- biomeType = BiomeTypes.PLAINS;
- LOGGER.warn("Invalid biome type in palette: {}. Biome will be replaced with plains.", value);
- }
- this.biomePalette[index] = biomeType;
- };
- }
-
- private EntityTransformer provideEntityTransformer(Clipboard clipboard) {
- return (x, y, z, id, tag) -> {
- EntityType type = EntityType.REGISTRY.get(id);
- if (type == null) {
- LOGGER.warn("Invalid entity id: {} - skipping", id);
- return;
- }
- clipboard.createEntity(
- new Location(clipboard, Location.at(x, y, z).add(clipboard.getMinimumPoint().toVector3())),
- new BaseEntity(type, LazyReference.computed(tag))
- );
- };
- }
-
- private EntityTransformer provideTileEntityTransformer(Clipboard clipboard) {
- return (x, y, z, id, tag) -> clipboard.tile(
- MathMan.roundInt(x + clipboard.getMinimumPoint().x()),
- MathMan.roundInt(y + clipboard.getMinimumPoint().y()),
- MathMan.roundInt(z + clipboard.getMinimumPoint().z()),
- FaweCompoundTag.of(tag)
- );
- }
-
- /**
- * @return {@code true} if {@code Width}, {@code Length} and {@code Height} are already read from the stream
- */
- private boolean areDimensionsAvailable() {
- return this.dimensions.x() != 0 && this.dimensions.y() != 0 && this.dimensions.z() != 0;
- }
-
- /**
- * Closes this reader instance and all underlying resources.
- *
- * @throws IOException on I/O error.
- */
- @Override
- public void close() throws IOException {
- parentStream.close(); // closes all underlying resources implicitly
- }
-
- /**
- * Resets the main stream to the previously marked position ({@code 0}), if supported (see {@link #supportsReset()}).
- * If the stream is reset, the sub streams (for DataInput and NBT) are re-created to respect the new position.
- *
- * @throws IOException on I/O error.
- */
- private void reset() throws IOException {
- if (this.supportsReset()) {
- this.parentStream.reset();
- this.parentStream.mark(Integer.MAX_VALUE);
- this.setSubStreams();
- }
- }
-
- /**
- * @return {@code true} if the stream used while instantiating the reader supports resets (without memory overhead).
- */
- private boolean supportsReset() {
- return this.remainingTags != null;
- }
-
- /**
- * Overwrites the DataInput- and NBT-InputStreams (e.g. when the marker of the backed stream updated).
- *
- * @throws IOException on I/O error.
- */
- private void setSubStreams() throws IOException {
- this.dataInputStream = new DataInputStream(this.parentStream);
- this.nbtInputStream = new NBTInputStream(this.parentStream);
- }
-
- /**
- * Creates a new cache writer for non-palette data, if none exists yet.
- * Returns either the already created or new one.
- *
- * @return the output stream for non-palette cache data.
- */
- private OutputStream getDataCacheWriter() {
- if (this.dataCacheWriter == null) {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(512);
- this.dataCacheWriter = new FastBufferedOutputStream(new LZ4BlockOutputStream(byteArrayOutputStream)) {
- @Override
- public void close() throws IOException {
- this.write(CACHE_IDENTIFIER_END);
- super.close();
- FaWeSchematicReaderV3.this.dataCache = byteArrayOutputStream.toByteArray();
- }
- };
- }
- return this.dataCacheWriter;
- }
-
- /**
- * Creates a new cache writer for palette data, if none exists yet.
- * Returns either the already created or new one.
- *
- * @return the output stream for palette cache data.
- */
- private OutputStream getPaletteCacheWriter() {
- if (this.paletteCacheWriter == null) {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(256);
- this.paletteCacheWriter = new FastBufferedOutputStream(new LZ4BlockOutputStream(byteArrayOutputStream)) {
- @Override
- public void close() throws IOException {
- this.write(CACHE_IDENTIFIER_END);
- super.close();
- FaWeSchematicReaderV3.this.paletteCache = byteArrayOutputStream.toByteArray();
- }
- };
- }
- return this.paletteCacheWriter;
- }
-
- private boolean needsVarIntReading(int byteArrayLength) {
- return byteArrayLength > this.dimensions.x() * this.dimensions.y() * this.dimensions.z();
- }
-
- /**
- * Skips the schematic header including the root compound (empty name) and the root's child compound ("Schematic")
- *
- * @param dataInputStream The stream containing the schematic data to skip
- * @throws IOException on I/O error
- */
- private static void skipHeader(DataInputStream dataInputStream) throws IOException {
- dataInputStream.skipNBytes(1 + 2); // 1 Byte = TAG_Compound, 2 Bytes = Short (Length of tag name = "")
- dataInputStream.skipNBytes(1 + 2 + 9); // as above + 9 bytes = "Schematic"
- }
-
- @ApiStatus.Internal
- @FunctionalInterface
- private interface PositionConsumer {
-
- /**
- * Called with block location coordinates.
- *
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @param z the z coordinate.
- */
- void accept(int x, int y, int z);
-
- }
-
- @ApiStatus.Internal
- @FunctionalInterface
- private interface EntityTransformer {
-
- /**
- * Called for each entity from the Schematics {@code Entities} compound list.
- *
- * @param x the relative x coordinate of the entity.
- * @param y the relative y coordinate of the entity.
- * @param z the relative z coordinate of the entity.
- * @param id the entity id as a resource location (e.g. {@code minecraft:sheep}).
- * @param tag the - already fixed, if required - nbt data of the entity.
- */
- void transform(double x, double y, double z, String id, LinCompoundTag tag);
-
- }
-
- @ApiStatus.Internal
- @FunctionalInterface
- private interface PaletteInitializer {
-
- /**
- * Called for each palette entry (the mapping part, not data).
- *
- * @param index the index of the entry, as used in the Data byte array.
- * @param value the value for this entry (either biome type as resource location or the block state as a string).
- */
- void initialize(char index, String value);
-
- }
-
- @ApiStatus.Internal
- @FunctionalInterface
- private interface PaletteDataApplier {
-
- /**
- * Called for each palette data entry (not the mapping part, but the var-int byte array).
- *
- * @param index The index of this data entry (due to var-int behaviour not necessarily the index in the data byte array).
- * @param ordinal The ordinal of this entry as defined in the palette mapping.
- */
- void apply(int index, char ordinal);
-
- }
-
- @ApiStatus.Internal
- @FunctionalInterface
- private interface AdditionalTagConsumer {
-
- /**
- * Called for each unknown nbt tag.
- *
- * @param type The type of the tag (as defined by the constants in {@link NBTConstants}).
- * @param name The name of the tag.
- */
- void accept(byte type, String name);
-
- }
-
-}
diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/core/WorldEditWrapper21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/core/WorldEditWrapper21.java
index 52743f45..d93c095d 100644
--- a/SpigotCore/SpigotCore_21/src/de/steamwar/core/WorldEditWrapper21.java
+++ b/SpigotCore/SpigotCore_21/src/de/steamwar/core/WorldEditWrapper21.java
@@ -26,6 +26,8 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
+import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader;
+import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
@@ -33,6 +35,7 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.sql.NodeData;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
+import org.enginehub.linbus.stream.LinBinaryIO;
import java.io.*;
@@ -67,8 +70,8 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
return switch (schemFormat) {
case MCEDIT -> new MCEditSchematicReader(new NBTInputStream(is)).read();
- case SPONGE_V2 -> new FastSchematicReaderV2(new NBTInputStream(is)).read();
- case SPONGE_V3 -> new FaWeSchematicReaderV3(is).read();
+ case SPONGE_V2 -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(is))).read();
+ case SPONGE_V3 -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(is))).read();
};
}
@@ -93,4 +96,9 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
v = transform.apply(v);
return new org.bukkit.util.Vector(v.x(), v.y(), v.z());
}
+
+ @Override
+ public NodeData.SchematicFormat getNativeFormat() {
+ return NodeData.SchematicFormat.SPONGE_V3;
+ }
}
diff --git a/SpigotCore/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java b/SpigotCore/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java
index a6aad3b5..75957933 100644
--- a/SpigotCore/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java
+++ b/SpigotCore/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java
@@ -105,6 +105,11 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
return new org.bukkit.util.Vector(v.getX(), v.getY(), v.getZ());
}
+ @Override
+ public NodeData.SchematicFormat getNativeFormat() {
+ return NodeData.SchematicFormat.MCEDIT;
+ }
+
private static class SpongeSchematicReader implements ClipboardReader {
private final NBTInputStream inputStream;
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java
index 62a7f2b7..c9585847 100644
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java
+++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java
@@ -77,7 +77,7 @@ public class ErrorHandler extends Handler {
return;
try {
- //SWException.log(message, stacktrace);
+ SWException.log(message, stacktrace);
} catch (SecurityException e) {
Core.getInstance().getLogger().log(Level.INFO, "Could not log error in database", e);
}
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java
index d07bd155..9928b947 100644
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java
+++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java
@@ -46,6 +46,8 @@ public interface WorldEditWrapper {
Vector getMaximum(Region region);
Vector applyTransform(Vector vector, Transform transform);
+ NodeData.SchematicFormat getNativeFormat();
+
static WorldEditPlugin getWorldEditPlugin() {
return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
}
@@ -86,16 +88,6 @@ public interface WorldEditWrapper {
return inputStream;
}
- static NodeData.SchematicFormat getNativeFormat() {
- if (Core.getVersion() <= 12) {
- return NodeData.SchematicFormat.MCEDIT;
- } else if (Core.getVersion() <= 20) {
- return NodeData.SchematicFormat.SPONGE_V2;
- } else {
- return NodeData.SchematicFormat.SPONGE_V3;
- }
- }
-
interface SchematicWriter {
void write(OutputStream outputStream, Clipboard clipboard, ClipboardHolder holder) throws IOException;
}
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java
index 0957bae0..62f00369 100644
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java
+++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java
@@ -61,7 +61,7 @@ public class SchematicData {
}
public void saveFromPlayer(Player player) throws IOException, NoClipboardException {
- data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player), WorldEditWrapper.getNativeFormat());
+ data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player), WorldEditWrapper.impl.getNativeFormat());
}
@Deprecated
diff --git a/VelocityCore/build.gradle.kts b/VelocityCore/build.gradle.kts
index e9d6386e..166df4c4 100644
--- a/VelocityCore/build.gradle.kts
+++ b/VelocityCore/build.gradle.kts
@@ -64,4 +64,6 @@ dependencies {
implementation(libs.apolloapi)
implementation(libs.apollocommon)
+
+ implementation(libs.nbt)
}
\ No newline at end of file
diff --git a/VelocityCore/src/de/steamwar/velocitycore/discord/listeners/DiscordSchemUpload.java b/VelocityCore/src/de/steamwar/velocitycore/discord/listeners/DiscordSchemUpload.java
index a39837f6..68b8d360 100644
--- a/VelocityCore/src/de/steamwar/velocitycore/discord/listeners/DiscordSchemUpload.java
+++ b/VelocityCore/src/de/steamwar/velocitycore/discord/listeners/DiscordSchemUpload.java
@@ -25,10 +25,13 @@ import de.steamwar.sql.NodeData;
import de.steamwar.sql.Punishment;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
+import dev.dewy.nbt.Nbt;
+import dev.dewy.nbt.tags.collection.CompoundTag;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
@@ -38,6 +41,8 @@ import java.util.logging.Level;
public class DiscordSchemUpload extends ListenerAdapter {
+ private static final Nbt NBT = new Nbt();
+
private static final List