diff --git a/SpigotCore_8/src/de/steamwar/sql/Schematic_8.java b/SpigotCore_8/src/de/steamwar/sql/Schematic_8.java index d610b6a..f9f7266 100644 --- a/SpigotCore_8/src/de/steamwar/sql/Schematic_8.java +++ b/SpigotCore_8/src/de/steamwar/sql/Schematic_8.java @@ -1,20 +1,34 @@ package de.steamwar.sql; -import com.sk89q.worldedit.EmptyClipboardException; -import com.sk89q.worldedit.WorldEdit; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.sk89q.jnbt.*; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.world.registry.WorldData; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; class Schematic_8 { private Schematic_8(){} @@ -51,4 +65,190 @@ class Schematic_8 { private static WorldEditPlugin getWorldEditPlugin() { return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); } + + public static class SpongeSchematicReader implements ClipboardReader { + private final NBTInputStream inputStream; + private int schematicVersion = -1; + + public SpongeSchematicReader(NBTInputStream inputStream) { + Preconditions.checkNotNull(inputStream); + this.inputStream = inputStream; + } + + @Override + public Clipboard read(WorldData worldData) throws IOException { + CompoundTag schematicTag = this.getBaseTag(); + if (this.schematicVersion == 1) { + return this.readVersion1(schematicTag); + } else if (this.schematicVersion == 2) { + return this.readVersion1(schematicTag); + } else { + throw new IOException("This schematic version is currently not supported"); + } + } + + private CompoundTag getBaseTag() throws IOException { + NamedTag rootTag = this.inputStream.readNamedTag(); + if (!rootTag.getName().equals("Schematic")) { + throw new IOException("Tag 'Schematic' does not exist or is not first"); + } else { + CompoundTag schematicTag = (CompoundTag)rootTag.getTag(); + Map schematic = schematicTag.getValue(); + this.schematicVersion = (requireTag(schematic, "Version", IntTag.class)).getValue(); + return schematicTag; + } + } + + private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOException { + Map schematic = schematicTag.getValue(); + int width = (requireTag(schematic, "Width", ShortTag.class)).getValue(); + int height = (requireTag(schematic, "Height", ShortTag.class)).getValue(); + int length = (requireTag(schematic, "Length", ShortTag.class)).getValue(); + IntArrayTag offsetTag = getTag(schematic, "Offset", IntArrayTag.class); + int[] offsetParts; + if (offsetTag != null) { + offsetParts = offsetTag.getValue(); + if (offsetParts.length != 3) { + throw new IOException("Invalid offset specified in schematic."); + } + } else { + offsetParts = new int[]{0, 0, 0}; + } + + BlockVector min = new BlockVector(offsetParts[0], offsetParts[1], offsetParts[2]); + CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class); + Vector origin; + CuboidRegion region; + if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) { + Map metadata = metadataTag.getValue(); + int offsetX = (requireTag(metadata, "WEOffsetX", IntTag.class)).getValue(); + int offsetY = (requireTag(metadata, "WEOffsetY", IntTag.class)).getValue(); + int offsetZ = (requireTag(metadata, "WEOffsetZ", IntTag.class)).getValue(); + BlockVector offset = new BlockVector(offsetX, offsetY, offsetZ); + origin = min.subtract(offset); + region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE)); + } else { + origin = min; + region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE)); + } + + IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class); + Map paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue(); + if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) { + throw new IOException("Block palette size does not match expected size."); + } else { + Map palette = new HashMap<>(); //TODO not base blocks + ParserContext parserContext = new ParserContext(); + parserContext.setRestricted(false); + parserContext.setPreferringWildcard(false); + + int id; + BaseBlock state; + for(Iterator var36 = paletteObject.keySet().iterator(); var36.hasNext(); palette.put(id, state)) { + String palettePart = (String)var36.next(); + id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); + + try { + //TODO Parse id to base block + throw new InputParseException("TODO"); + } catch (InputParseException var31) { + state = new BaseBlock(0); //AIR + } + } + + byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue(); + Map> tileEntitiesMap = new HashMap<>(); + ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class); + if (tileEntities == null) { + tileEntities = getTag(schematic, "TileEntities", ListTag.class); + } + + if (tileEntities != null) { + List> tileEntityTags = tileEntities.getValue().stream().map((tag) -> + (CompoundTag)tag + ).map(CompoundTag::getValue).collect(Collectors.toList()); + + BlockVector pt; + Map tileEntity; + for(Iterator> var20 = tileEntityTags.iterator(); var20.hasNext(); tileEntitiesMap.put(pt, tileEntity)) { + tileEntity = var20.next(); + int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); + pt = new BlockVector(pos[0], pos[1], pos[2]); + Map values = Maps.newHashMap(tileEntity); + values.put("x", new IntTag(pt.getBlockX())); + values.put("y", new IntTag(pt.getBlockY())); + values.put("z", new IntTag(pt.getBlockZ())); + values.put("id", values.get("Id")); + values.remove("Id"); + values.remove("Pos"); + tileEntity = values; + } + } + + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(origin); + int index = 0; + + for(int i = 0; i < blocks.length; ++index) { + int value = 0; + int varintLength = 0; + + while(true) { + value |= (blocks[i] & 127) << varintLength++ * 7; + if (varintLength > 5) { + throw new IOException("VarInt too big (probably corrupted data)"); + } + + if ((blocks[i] & 128) != 128) { + ++i; + int y = index / (width * length); + int z = index % (width * length) / width; + int x = index % (width * length) % width; + BaseBlock block = palette.get(value); + BlockVector pt = new BlockVector(x, y, z); + + try { + if (tileEntitiesMap.containsKey(pt)) { + block.setNbtData(new CompoundTag(tileEntitiesMap.get(pt))); + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), block); + } else { + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), block); + } + break; + } catch (WorldEditException var30) { + throw new IOException("Failed to load a block in the schematic"); + } + } + + ++i; + } + } + + return clipboard; + } + } + + private static T requireTag(Map items, String key, Class expected) throws IOException { + if (!items.containsKey(key)) { + throw new IOException("Schematic file is missing a \"" + key + "\" tag"); + } else { + Tag tag = items.get(key); + if (!expected.isInstance(tag)) { + throw new IOException(key + " tag is not of tag type " + expected.getName()); + } else { + return expected.cast(tag); + } + } + } + + @Nullable + private static T getTag(Map items, String key, Class expected) { + if (!items.containsKey(key)) { + return null; + } else { + Tag test = items.get(key); + return !expected.isInstance(test) ? null : expected.cast(test); + } + } + } }