From 1186e84020906c529579a78785ef534af4e293be Mon Sep 17 00:00:00 2001 From: Lixfel Date: Thu, 16 Sep 2021 12:51:43 +0200 Subject: [PATCH] Hotfix separate WEWrapping from Flattening to allow WE-Less SpigotCore --- .../de/steamwar/core/FlatteningWrapper14.java | 600 +----------------- .../de/steamwar/core/WorldEditWrapper14.java | 599 +++++++++++++++++ .../de/steamwar/core/FlatteningWrapper8.java | 267 -------- .../de/steamwar/core/WorldEditWrapper8.java | 266 ++++++++ .../de/steamwar/core/FlatteningWrapper.java | 10 - .../de/steamwar/core/WorldEditWrapper.java | 26 + .../src/de/steamwar/sql/Schematic.java | 8 +- 7 files changed, 897 insertions(+), 879 deletions(-) create mode 100644 SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java create mode 100644 SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java create mode 100644 SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java diff --git a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java index af35928..3a28106 100644 --- a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java +++ b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java @@ -20,48 +20,14 @@ package de.steamwar.core; import com.comphenix.tinyprotocol.Reflection; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.EmptyClipboardException; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; -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.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; -import com.sk89q.worldedit.extent.clipboard.io.NBTSchematicReader; -import com.sk89q.worldedit.extent.clipboard.io.legacycompat.*; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.world.DataFixer; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.registry.LegacyMapper; import de.steamwar.scoreboard.SWScoreboard; -import de.steamwar.sql.NoClipboardException; import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkNotNull; +import java.util.HashMap; +import java.util.Map; public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper { @@ -331,566 +297,4 @@ public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper head.setItemMeta(headmeta); return head; } - - private static final ClipboardFormat SCHEMATIC = BuiltInClipboardFormat.MCEDIT_SCHEMATIC; - private static final ClipboardFormat SCHEM = BuiltInClipboardFormat.SPONGE_SCHEMATIC; - - @Override - public byte[] getPlayerClipboard(Player player, boolean schemFormat) { - ClipboardHolder clipboardHolder; - try { - clipboardHolder = getWorldEditPlugin().getSession(player).getClipboard(); - } catch (EmptyClipboardException e) { - throw new NoClipboardException(); - } - - Clipboard clipboard = clipboardHolder.getClipboard(); - if(clipboard == null) - throw new NoClipboardException(); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try{ - if(schemFormat){ - ClipboardWriter writer = SCHEM.getWriter(outputStream); - writer.write(clipboard); - writer.close(); - }else{ - SCHEMATIC.getWriter(outputStream).write(clipboard); - } - }catch(NullPointerException e){ - throw new RuntimeException(e.getMessage(), new IOException(e)); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - return outputStream.toByteArray(); - } - - @Override - public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { - Clipboard clipboard = null; - try { - clipboard = getClipboard(is, schemFormat); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - - if (clipboard == null) - throw new NoClipboardException(); - - Actor actor = getWorldEditPlugin().wrapCommandSender(player); - getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard)); - } - - @Override - public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { - try { - if(schemFormat){ - return new SpongeSchematicReader(new NBTInputStream(is)).read(); - }else{ - return new MCEditSchematicReader(new NBTInputStream(is)).read(); - } - } catch (NullPointerException e) { - throw new NoClipboardException(); - } - } - - private static WorldEditPlugin getWorldEditPlugin() { - return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); - } - - private static class MCEditSchematicReader extends NBTSchematicReader { - - private final NBTInputStream inputStream; - private final DataFixer fixer; - private boolean faweSchem = false; - private static final ImmutableList COMPATIBILITY_HANDLERS - = ImmutableList.of( - new SignCompatibilityHandler(), - new FlowerPotCompatibilityHandler(), - new NoteBlockCompatibilityHandler(), - new SkullBlockCompatibilityHandler() - ); - - /** - * Create a new instance. - * - * @param inputStream the input stream to read from - */ - MCEditSchematicReader(NBTInputStream inputStream) { - checkNotNull(inputStream); - this.inputStream = inputStream; - this.fixer = null; - } - - @Override - public Clipboard read() throws IOException { - // Schematic tag - NamedTag rootTag = inputStream.readNamedTag(); - if (!rootTag.getName().equals("Schematic")) { - throw new IOException("Tag 'Schematic' does not exist or is not first"); - } - CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); - - // Check - Map schematic = schematicTag.getValue(); - if (!schematic.containsKey("Blocks")) { - throw new IOException("Schematic file is missing a 'Blocks' tag"); - } - - // Check type of Schematic - String materials = requireTag(schematic, "Materials", StringTag.class).getValue(); - if (!materials.equals("Alpha")) { - throw new IOException("Schematic file is not an Alpha schematic"); - } - - // ==================================================================== - // Metadata - // ==================================================================== - - BlockVector3 origin; - Region region; - - // Get information - short width = requireTag(schematic, "Width", ShortTag.class).getValue(); - short height = requireTag(schematic, "Height", ShortTag.class).getValue(); - short length = requireTag(schematic, "Length", ShortTag.class).getValue(); - - int originX = 0; - int originY = 0; - int originZ = 0; - try { - originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue(); - originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue(); - originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue(); - BlockVector3 min = BlockVector3.at(originX, originY, originZ); - - int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue(); - int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue(); - int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue(); - BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); - - origin = min.subtract(offset); - region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); - } catch (IOException ignored) { - origin = BlockVector3.ZERO; - region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); - } - - // ==================================================================== - // Blocks - // ==================================================================== - - // Get blocks - byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue(); - byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue(); - byte[] addId = new byte[0]; - short[] blocks = new short[blockId.length]; // Have to later combine IDs - - // We support 4096 block IDs using the same method as vanilla Minecraft, where - // the highest 4 bits are stored in a separate byte array. - if (schematic.containsKey("AddBlocks")) { - addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue(); - } - - // Combine the AddBlocks data with the first 8-bit block ID - for (int index = 0; index < blockId.length; index++) { - if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index - blocks[index] = (short) (blockId[index] & 0xFF); - } else { - if ((index & 1) == 0) { - blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF)); - } else { - blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF)); - } - } - } - - // Need to pull out tile entities - final ListTag tileEntityTag = getTag(schematic, "TileEntities", ListTag.class); - List tileEntities = tileEntityTag == null ? new ArrayList<>() : tileEntityTag.getValue(); - Map> tileEntitiesMap = new HashMap<>(); - Map blockStates = new HashMap<>(); - - for (Tag tag : tileEntities) { - if (!(tag instanceof CompoundTag)) continue; - CompoundTag t = (CompoundTag) tag; - int x = t.getInt("x"); - int y = t.getInt("y"); - int z = t.getInt("z"); - int index = y * width * length + z * width + x; - if(index < 0 || index >= blocks.length) - faweSchem = true; - } - - for (Tag tag : tileEntities) { - if (!(tag instanceof CompoundTag)) continue; - CompoundTag t = (CompoundTag) tag; - Map values = new HashMap<>(t.getValue()); - String id = t.getString("id"); - values.put("id", new StringTag(convertBlockEntityId(id))); - int x = t.getInt("x"); - int y = t.getInt("y"); - int z = t.getInt("z"); - if(faweSchem){ - x -= originX; - y -= originY; - z -= originZ; - } - - int index = y * width * length + z * width + x; - - try{ - BlockState block = getBlockState(blocks[index], blockData[index]); - BlockState newBlock = block; - if (newBlock != null) { - for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { - if (handler.isAffectedBlock(newBlock)) { - newBlock = handler.updateNBT(block, values); - if (newBlock == null || values.isEmpty()) { - break; - } - } - } - } - if (values.isEmpty()) { - t = null; - } else { - t = new CompoundTag(values); - } - - if (fixer != null && t != null) { - t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1); - } - - BlockVector3 vec = BlockVector3.at(x, y, z); - if (t != null) { - tileEntitiesMap.put(vec, t.getValue()); - } - blockStates.put(vec, newBlock); - }catch(ArrayIndexOutOfBoundsException e){ - //ignored - } - } - - BlockArrayClipboard clipboard = new BlockArrayClipboard(region); - clipboard.setOrigin(origin); - - - for (int x = 0; x < width; ++x) { - for (int y = 0; y < height; ++y) { - for (int z = 0; z < length; ++z) { - int index = y * width * length + z * width + x; - BlockVector3 pt = BlockVector3.at(x, y, z); - BlockState state = blockStates.computeIfAbsent(pt, p -> getBlockState(blocks[index], blockData[index])); - - try { - if (state != null) { - if (tileEntitiesMap.containsKey(pt)) { - clipboard.setBlock(region.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt)))); - } else { - clipboard.setBlock(region.getMinimumPoint().add(pt), state); - } - } - } catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this - } - } - } - } - - return clipboard; - } - - private String convertBlockEntityId(String id) { - switch (id) { - case "Cauldron": - return "brewing_stand"; - case "Control": - return "command_block"; - case "DLDetector": - return "daylight_detector"; - case "Trap": - return "dispenser"; - case "EnchantTable": - return "enchanting_table"; - case "EndGateway": - return "end_gateway"; - case "AirPortal": - return "end_portal"; - case "EnderChest": - return "ender_chest"; - case "FlowerPot": - return "flower_pot"; - case "RecordPlayer": - return "jukebox"; - case "MobSpawner": - return "mob_spawner"; - case "Music": - case "noteblock": - return "note_block"; - case "Structure": - return "structure_block"; - case "Chest": - return "chest"; - case "Sign": - return "sign"; - case "Banner": - return "banner"; - case "Beacon": - return "beacon"; - case "Comparator": - return "comparator"; - case "Dropper": - return "dropper"; - case "Furnace": - return "furnace"; - case "Hopper": - return "hopper"; - case "Skull": - return "skull"; - default: - return id; - } - } - - private BlockState getBlockState(int id, int data) { - return LegacyMapper.getInstance().getBlockFromLegacy(id, data); - } - - @Override - public void close() throws IOException { - inputStream.close(); - } - } - private static class SpongeSchematicReader extends NBTSchematicReader { - - private final NBTInputStream inputStream; - private DataFixer fixer = null; - private int schematicVersion = -1; - private int dataVersion = -1; - private boolean faweSchem = false; - - /** - * Create a new instance. - * - * @param inputStream the input stream to read from - */ - public SpongeSchematicReader(NBTInputStream inputStream) { - checkNotNull(inputStream); - this.inputStream = inputStream; - } - - @Override - public Clipboard read() throws IOException { - CompoundTag schematicTag = getBaseTag(); - Map schematic = schematicTag.getValue(); - - final Platform platform = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.WORLD_EDITING); - int liveDataVersion = platform.getDataVersion(); - - if (schematicVersion == 1) { - dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1- - fixer = platform.getDataFixer(); - return readVersion1(schematicTag); - } else if (schematicVersion == 2) { - dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); - if (dataVersion < liveDataVersion) { - fixer = platform.getDataFixer(); - } - - return readVersion1(schematicTag); - } - throw new IOException("This schematic version is currently not supported"); - } - - @Override - public OptionalInt getDataVersion() { - try { - CompoundTag schematicTag = getBaseTag(); - Map schematic = schematicTag.getValue(); - if (schematicVersion == 1) { - return OptionalInt.of(1631); - } else if (schematicVersion == 2) { - return OptionalInt.of(requireTag(schematic, "DataVersion", IntTag.class).getValue()); - } - return OptionalInt.empty(); - } catch (IOException e) { - return OptionalInt.empty(); - } - } - - private CompoundTag getBaseTag() throws IOException { - NamedTag rootTag = inputStream.readNamedTag(); - if (!rootTag.getName().equals("Schematic")) { - throw new IOException("Tag 'Schematic' does not exist or is not first"); - } - CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); - - // Check - Map schematic = schematicTag.getValue(); - - schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue(); - return schematicTag; - } - - private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOException { - BlockVector3 origin; - Region region; - 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}; - } - - BlockVector3 min = BlockVector3.at(offsetParts[0], offsetParts[1], offsetParts[2]); - - CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class); - int offsetX = 0; - int offsetY = 0; - int offsetZ = 0; - if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) { - // We appear to have WorldEdit Metadata - Map metadata = metadataTag.getValue(); - offsetX = requireTag(metadata, "WEOffsetX", IntTag.class).getValue(); - offsetY = requireTag(metadata, "WEOffsetY", IntTag.class).getValue(); - offsetZ = requireTag(metadata, "WEOffsetZ", IntTag.class).getValue(); - BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); - origin = min.subtract(offset); - region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); - } else { - origin = min; - region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.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."); - } - - Map palette = new HashMap<>(); - - ParserContext parserContext = new ParserContext(); - parserContext.setRestricted(false); - parserContext.setTryLegacy(false); - parserContext.setPreferringWildcard(false); - - for (String palettePart : paletteObject.keySet()) { - int id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); - if (fixer != null) { - palettePart = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); - } - BlockState state; - try { - state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState(); - } catch (InputParseException e) { - state = BlockTypes.AIR.getDefaultState(); - } - palette.put(id, state); - } - - 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()); - - for (Map tileEntity : tileEntityTags) { - int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); - if(pos[0] < 0 || pos[0] >= width || pos[1] < 0 || pos[1] >= height || pos[2] < 0 || pos[2] >= length) - faweSchem = true; - } - - for (Map tileEntity : tileEntityTags) { - int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); - final BlockVector3 pt = BlockVector3.at(pos[0], pos[1], pos[2]); - Map values = Maps.newHashMap(tileEntity); - if(faweSchem){ - values.put("x", new IntTag(pt.getBlockX() - offsetX)); - values.put("y", new IntTag(pt.getBlockY() - offsetY)); - values.put("z", new IntTag(pt.getBlockZ() - offsetZ)); - }else{ - 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"); - if (fixer != null) { - tileEntity = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, new CompoundTag(values), dataVersion).getValue(); - } else { - tileEntity = values; - } - tileEntitiesMap.put(pt, tileEntity); - } - } - - BlockArrayClipboard clipboard = new BlockArrayClipboard(region); - clipboard.setOrigin(origin); - - int index = 0; - int i = 0; - int value; - int varintLength; - while (i < blocks.length) { - value = 0; - 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++; - break; - } - i++; - } - // index = (y * length * width) + (z * width) + x - int y = index / (width * length); - int z = (index % (width * length)) / width; - int x = (index % (width * length)) % width; - BlockState state = palette.get(value); - BlockVector3 pt = BlockVector3.at(x, y, z); - try { - if (tileEntitiesMap.containsKey(pt)) { - clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt)))); - } else { - clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state); - } - } catch (WorldEditException e) { - throw new IOException("Failed to load a block in the schematic"); - } - - index++; - } - - return clipboard; - } - - @Override - public void close() throws IOException { - inputStream.close(); - } - } } diff --git a/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java b/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java new file mode 100644 index 0000000..96b135b --- /dev/null +++ b/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java @@ -0,0 +1,599 @@ +package de.steamwar.core; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.sk89q.jnbt.*; +import com.sk89q.worldedit.EmptyClipboardException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +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.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.extent.clipboard.io.NBTSchematicReader; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.*; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.registry.LegacyMapper; +import de.steamwar.sql.NoClipboardException; +import org.bukkit.entity.Player; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class WorldEditWrapper14 implements WorldEditWrapper.IWorldEditWrapper { + + private static final ClipboardFormat SCHEMATIC = BuiltInClipboardFormat.MCEDIT_SCHEMATIC; + private static final ClipboardFormat SCHEM = BuiltInClipboardFormat.SPONGE_SCHEMATIC; + + @Override + public byte[] getPlayerClipboard(Player player, boolean schemFormat) { + ClipboardHolder clipboardHolder; + try { + clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard(); + } catch (EmptyClipboardException e) { + throw new NoClipboardException(); + } + + Clipboard clipboard = clipboardHolder.getClipboard(); + if(clipboard == null) + throw new NoClipboardException(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try{ + if(schemFormat){ + ClipboardWriter writer = SCHEM.getWriter(outputStream); + writer.write(clipboard); + writer.close(); + }else{ + SCHEMATIC.getWriter(outputStream).write(clipboard); + } + }catch(NullPointerException e){ + throw new RuntimeException(e.getMessage(), new IOException(e)); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + return outputStream.toByteArray(); + } + + @Override + public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { + Clipboard clipboard = null; + try { + clipboard = getClipboard(is, schemFormat); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + + if (clipboard == null) + throw new NoClipboardException(); + + Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player); + WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard)); + } + + @Override + public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { + try { + if(schemFormat){ + return new SpongeSchematicReader(new NBTInputStream(is)).read(); + }else{ + return new MCEditSchematicReader(new NBTInputStream(is)).read(); + } + } catch (NullPointerException e) { + throw new NoClipboardException(); + } + } + + private static class MCEditSchematicReader extends NBTSchematicReader { + + private final NBTInputStream inputStream; + private final DataFixer fixer; + private boolean faweSchem = false; + private static final ImmutableList COMPATIBILITY_HANDLERS + = ImmutableList.of( + new SignCompatibilityHandler(), + new FlowerPotCompatibilityHandler(), + new NoteBlockCompatibilityHandler(), + new SkullBlockCompatibilityHandler() + ); + + /** + * Create a new instance. + * + * @param inputStream the input stream to read from + */ + MCEditSchematicReader(NBTInputStream inputStream) { + checkNotNull(inputStream); + this.inputStream = inputStream; + this.fixer = null; + } + + @Override + public Clipboard read() throws IOException { + // Schematic tag + NamedTag rootTag = inputStream.readNamedTag(); + if (!rootTag.getName().equals("Schematic")) { + throw new IOException("Tag 'Schematic' does not exist or is not first"); + } + CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); + + // Check + Map schematic = schematicTag.getValue(); + if (!schematic.containsKey("Blocks")) { + throw new IOException("Schematic file is missing a 'Blocks' tag"); + } + + // Check type of Schematic + String materials = requireTag(schematic, "Materials", StringTag.class).getValue(); + if (!materials.equals("Alpha")) { + throw new IOException("Schematic file is not an Alpha schematic"); + } + + // ==================================================================== + // Metadata + // ==================================================================== + + BlockVector3 origin; + Region region; + + // Get information + short width = requireTag(schematic, "Width", ShortTag.class).getValue(); + short height = requireTag(schematic, "Height", ShortTag.class).getValue(); + short length = requireTag(schematic, "Length", ShortTag.class).getValue(); + + int originX = 0; + int originY = 0; + int originZ = 0; + try { + originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue(); + originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue(); + originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue(); + BlockVector3 min = BlockVector3.at(originX, originY, originZ); + + int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue(); + int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue(); + int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue(); + BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); + + origin = min.subtract(offset); + region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); + } catch (IOException ignored) { + origin = BlockVector3.ZERO; + region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); + } + + // ==================================================================== + // Blocks + // ==================================================================== + + // Get blocks + byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue(); + byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue(); + byte[] addId = new byte[0]; + short[] blocks = new short[blockId.length]; // Have to later combine IDs + + // We support 4096 block IDs using the same method as vanilla Minecraft, where + // the highest 4 bits are stored in a separate byte array. + if (schematic.containsKey("AddBlocks")) { + addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue(); + } + + // Combine the AddBlocks data with the first 8-bit block ID + for (int index = 0; index < blockId.length; index++) { + if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index + blocks[index] = (short) (blockId[index] & 0xFF); + } else { + if ((index & 1) == 0) { + blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF)); + } else { + blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF)); + } + } + } + + // Need to pull out tile entities + final ListTag tileEntityTag = getTag(schematic, "TileEntities", ListTag.class); + List tileEntities = tileEntityTag == null ? new ArrayList<>() : tileEntityTag.getValue(); + Map> tileEntitiesMap = new HashMap<>(); + Map blockStates = new HashMap<>(); + + for (Tag tag : tileEntities) { + if (!(tag instanceof CompoundTag)) continue; + CompoundTag t = (CompoundTag) tag; + int x = t.getInt("x"); + int y = t.getInt("y"); + int z = t.getInt("z"); + int index = y * width * length + z * width + x; + if(index < 0 || index >= blocks.length) + faweSchem = true; + } + + for (Tag tag : tileEntities) { + if (!(tag instanceof CompoundTag)) continue; + CompoundTag t = (CompoundTag) tag; + Map values = new HashMap<>(t.getValue()); + String id = t.getString("id"); + values.put("id", new StringTag(convertBlockEntityId(id))); + int x = t.getInt("x"); + int y = t.getInt("y"); + int z = t.getInt("z"); + if(faweSchem){ + x -= originX; + y -= originY; + z -= originZ; + } + + int index = y * width * length + z * width + x; + + try{ + BlockState block = getBlockState(blocks[index], blockData[index]); + BlockState newBlock = block; + if (newBlock != null) { + for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { + if (handler.isAffectedBlock(newBlock)) { + newBlock = handler.updateNBT(block, values); + if (newBlock == null || values.isEmpty()) { + break; + } + } + } + } + if (values.isEmpty()) { + t = null; + } else { + t = new CompoundTag(values); + } + + if (fixer != null && t != null) { + t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1); + } + + BlockVector3 vec = BlockVector3.at(x, y, z); + if (t != null) { + tileEntitiesMap.put(vec, t.getValue()); + } + blockStates.put(vec, newBlock); + }catch(ArrayIndexOutOfBoundsException e){ + //ignored + } + } + + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(origin); + + + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + for (int z = 0; z < length; ++z) { + int index = y * width * length + z * width + x; + BlockVector3 pt = BlockVector3.at(x, y, z); + BlockState state = blockStates.computeIfAbsent(pt, p -> getBlockState(blocks[index], blockData[index])); + + try { + if (state != null) { + if (tileEntitiesMap.containsKey(pt)) { + clipboard.setBlock(region.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt)))); + } else { + clipboard.setBlock(region.getMinimumPoint().add(pt), state); + } + } + } catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this + } + } + } + } + + return clipboard; + } + + private String convertBlockEntityId(String id) { + switch (id) { + case "Cauldron": + return "brewing_stand"; + case "Control": + return "command_block"; + case "DLDetector": + return "daylight_detector"; + case "Trap": + return "dispenser"; + case "EnchantTable": + return "enchanting_table"; + case "EndGateway": + return "end_gateway"; + case "AirPortal": + return "end_portal"; + case "EnderChest": + return "ender_chest"; + case "FlowerPot": + return "flower_pot"; + case "RecordPlayer": + return "jukebox"; + case "MobSpawner": + return "mob_spawner"; + case "Music": + case "noteblock": + return "note_block"; + case "Structure": + return "structure_block"; + case "Chest": + return "chest"; + case "Sign": + return "sign"; + case "Banner": + return "banner"; + case "Beacon": + return "beacon"; + case "Comparator": + return "comparator"; + case "Dropper": + return "dropper"; + case "Furnace": + return "furnace"; + case "Hopper": + return "hopper"; + case "Skull": + return "skull"; + default: + return id; + } + } + + private BlockState getBlockState(int id, int data) { + return LegacyMapper.getInstance().getBlockFromLegacy(id, data); + } + + @Override + public void close() throws IOException { + inputStream.close(); + } + } + private static class SpongeSchematicReader extends NBTSchematicReader { + + private final NBTInputStream inputStream; + private DataFixer fixer = null; + private int schematicVersion = -1; + private int dataVersion = -1; + private boolean faweSchem = false; + + /** + * Create a new instance. + * + * @param inputStream the input stream to read from + */ + public SpongeSchematicReader(NBTInputStream inputStream) { + checkNotNull(inputStream); + this.inputStream = inputStream; + } + + @Override + public Clipboard read() throws IOException { + CompoundTag schematicTag = getBaseTag(); + Map schematic = schematicTag.getValue(); + + final Platform platform = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.WORLD_EDITING); + int liveDataVersion = platform.getDataVersion(); + + if (schematicVersion == 1) { + dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1- + fixer = platform.getDataFixer(); + return readVersion1(schematicTag); + } else if (schematicVersion == 2) { + dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); + if (dataVersion < liveDataVersion) { + fixer = platform.getDataFixer(); + } + + return readVersion1(schematicTag); + } + throw new IOException("This schematic version is currently not supported"); + } + + @Override + public OptionalInt getDataVersion() { + try { + CompoundTag schematicTag = getBaseTag(); + Map schematic = schematicTag.getValue(); + if (schematicVersion == 1) { + return OptionalInt.of(1631); + } else if (schematicVersion == 2) { + return OptionalInt.of(requireTag(schematic, "DataVersion", IntTag.class).getValue()); + } + return OptionalInt.empty(); + } catch (IOException e) { + return OptionalInt.empty(); + } + } + + private CompoundTag getBaseTag() throws IOException { + NamedTag rootTag = inputStream.readNamedTag(); + if (!rootTag.getName().equals("Schematic")) { + throw new IOException("Tag 'Schematic' does not exist or is not first"); + } + CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); + + // Check + Map schematic = schematicTag.getValue(); + + schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue(); + return schematicTag; + } + + private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOException { + BlockVector3 origin; + Region region; + 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}; + } + + BlockVector3 min = BlockVector3.at(offsetParts[0], offsetParts[1], offsetParts[2]); + + CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class); + int offsetX = 0; + int offsetY = 0; + int offsetZ = 0; + if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) { + // We appear to have WorldEdit Metadata + Map metadata = metadataTag.getValue(); + offsetX = requireTag(metadata, "WEOffsetX", IntTag.class).getValue(); + offsetY = requireTag(metadata, "WEOffsetY", IntTag.class).getValue(); + offsetZ = requireTag(metadata, "WEOffsetZ", IntTag.class).getValue(); + BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); + origin = min.subtract(offset); + region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); + } else { + origin = min; + region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.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."); + } + + Map palette = new HashMap<>(); + + ParserContext parserContext = new ParserContext(); + parserContext.setRestricted(false); + parserContext.setTryLegacy(false); + parserContext.setPreferringWildcard(false); + + for (String palettePart : paletteObject.keySet()) { + int id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); + if (fixer != null) { + palettePart = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); + } + BlockState state; + try { + state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState(); + } catch (InputParseException e) { + state = BlockTypes.AIR.getDefaultState(); + } + palette.put(id, state); + } + + 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()); + + for (Map tileEntity : tileEntityTags) { + int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); + if(pos[0] < 0 || pos[0] >= width || pos[1] < 0 || pos[1] >= height || pos[2] < 0 || pos[2] >= length) + faweSchem = true; + } + + for (Map tileEntity : tileEntityTags) { + int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); + final BlockVector3 pt = BlockVector3.at(pos[0], pos[1], pos[2]); + Map values = Maps.newHashMap(tileEntity); + if(faweSchem){ + values.put("x", new IntTag(pt.getBlockX() - offsetX)); + values.put("y", new IntTag(pt.getBlockY() - offsetY)); + values.put("z", new IntTag(pt.getBlockZ() - offsetZ)); + }else{ + 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"); + if (fixer != null) { + tileEntity = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, new CompoundTag(values), dataVersion).getValue(); + } else { + tileEntity = values; + } + tileEntitiesMap.put(pt, tileEntity); + } + } + + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(origin); + + int index = 0; + int i = 0; + int value; + int varintLength; + while (i < blocks.length) { + value = 0; + 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++; + break; + } + i++; + } + // index = (y * length * width) + (z * width) + x + int y = index / (width * length); + int z = (index % (width * length)) / width; + int x = (index % (width * length)) % width; + BlockState state = palette.get(value); + BlockVector3 pt = BlockVector3.at(x, y, z); + try { + if (tileEntitiesMap.containsKey(pt)) { + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt)))); + } else { + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state); + } + } catch (WorldEditException e) { + throw new IOException("Failed to load a block in the schematic"); + } + + index++; + } + + return clipboard; + } + + @Override + public void close() throws IOException { + inputStream.close(); + } + } +} diff --git a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java index e6dcdb8..371c799 100644 --- a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java @@ -20,40 +20,11 @@ package de.steamwar.core; import com.comphenix.tinyprotocol.Reflection; -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.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.extent.clipboard.io.SchematicReader; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.world.registry.WorldData; import de.steamwar.scoreboard.SWScoreboard; -import de.steamwar.sql.NoClipboardException; -import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; -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; - public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper { private static final Reflection.FieldAccessor scoreboardName = Reflection.getField(SWScoreboard.scoreboardObjective, String.class, 1); @@ -94,242 +65,4 @@ public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper head.setItemMeta(headmeta); return head; } - - @Override - public byte[] getPlayerClipboard(Player player, boolean schemFormat) { - ClipboardHolder clipboardHolder; - try { - clipboardHolder = getWorldEditPlugin().getSession(player).getClipboard(); - } catch (EmptyClipboardException e) { - throw new NoClipboardException(); - } - - Clipboard clipboard = clipboardHolder.getClipboard(); - if(clipboard == null) - throw new NoClipboardException(); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData()); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - return outputStream.toByteArray(); - } - - @Override - public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { - WorldData world = new BukkitWorld(player.getWorld()).getWorldData(); - Clipboard clipboard; - try { - clipboard = getClipboard(is, schemFormat); - } catch (IOException e) { - throw new RuntimeException(e); - } - - Actor actor = getWorldEditPlugin().wrapCommandSender(player); - getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard, world)); - } - - @Override - public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { - if(schemFormat) - return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData()); - else - return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData()); - } - - private static WorldEditPlugin getWorldEditPlugin() { - return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); - } - - private static class SpongeSchematicReader implements ClipboardReader { - - private final NBTInputStream inputStream; - private int schematicVersion = -1; - - 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.readSchematic(schematicTag); - } else if (this.schematicVersion == 2) { - return this.readSchematic(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 readSchematic(CompoundTag schematicTag) throws IOException { - final Map ids = IDConverter8.getMap(); - - 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."); - - Map palette = new HashMap<>(); - ParserContext parserContext = new ParserContext(); - parserContext.setRestricted(false); - parserContext.setPreferringWildcard(false); - - int id; - BaseBlock state; - for(Iterator iterator = paletteObject.keySet().iterator(); iterator.hasNext(); palette.put(id, state)) { - String palettePart = iterator.next(); - id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); - - IDConverter8.BlockTypeID blockID = ids.get(palettePart); - if(blockID == null){ - blockID = ids.get(palettePart.split("\\[")[0]); - } - - if(blockID == null){ - state = new BaseBlock(0); //AIR - }else{ - state = new BaseBlock(blockID.getBlockId(), blockID.getDataId()); - } - } - - 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); - } - } - } - - 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); - } - } - } } diff --git a/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java b/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java new file mode 100644 index 0000000..3e243bd --- /dev/null +++ b/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java @@ -0,0 +1,266 @@ +package de.steamwar.core; + +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.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.extent.clipboard.io.SchematicReader; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.world.registry.WorldData; +import de.steamwar.sql.NoClipboardException; +import org.bukkit.entity.Player; + +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; + +public class WorldEditWrapper8 implements WorldEditWrapper.IWorldEditWrapper { + + @Override + public byte[] getPlayerClipboard(Player player, boolean schemFormat) { + ClipboardHolder clipboardHolder; + try { + clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard(); + } catch (EmptyClipboardException e) { + throw new NoClipboardException(); + } + + Clipboard clipboard = clipboardHolder.getClipboard(); + if(clipboard == null) + throw new NoClipboardException(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData()); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + return outputStream.toByteArray(); + } + + @Override + public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { + WorldData world = new BukkitWorld(player.getWorld()).getWorldData(); + Clipboard clipboard; + try { + clipboard = getClipboard(is, schemFormat); + } catch (IOException e) { + throw new RuntimeException(e); + } + + Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player); + WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard, world)); + } + + @Override + public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { + if(schemFormat) + return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData()); + else + return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData()); + } + + private static class SpongeSchematicReader implements ClipboardReader { + + private final NBTInputStream inputStream; + private int schematicVersion = -1; + + 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.readSchematic(schematicTag); + } else if (this.schematicVersion == 2) { + return this.readSchematic(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 readSchematic(CompoundTag schematicTag) throws IOException { + final Map ids = IDConverter8.getMap(); + + 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."); + + Map palette = new HashMap<>(); + ParserContext parserContext = new ParserContext(); + parserContext.setRestricted(false); + parserContext.setPreferringWildcard(false); + + int id; + BaseBlock state; + for(Iterator iterator = paletteObject.keySet().iterator(); iterator.hasNext(); palette.put(id, state)) { + String palettePart = iterator.next(); + id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); + + IDConverter8.BlockTypeID blockID = ids.get(palettePart); + if(blockID == null){ + blockID = ids.get(palettePart.split("\\[")[0]); + } + + if(blockID == null){ + state = new BaseBlock(0); //AIR + }else{ + state = new BaseBlock(blockID.getBlockId(), blockID.getDataId()); + } + } + + 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); + } + } + } + + 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); + } + } + } +} diff --git a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java index ad75bce..ca673c0 100644 --- a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java @@ -19,14 +19,9 @@ package de.steamwar.core; -import com.sk89q.worldedit.extent.clipboard.Clipboard; import org.bukkit.Material; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import java.io.IOException; -import java.io.InputStream; - public class FlatteningWrapper { private FlatteningWrapper() {} @@ -39,10 +34,5 @@ public class FlatteningWrapper { Material getMaterial(String material); Material getDye(int colorCode); ItemStack setSkullOwner(String player); - - byte[] getPlayerClipboard(Player player, boolean schemFormat); - void setPlayerClipboard(Player player, InputStream is, boolean schemFormat); - Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException; - } } diff --git a/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java b/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java new file mode 100644 index 0000000..920bc9f --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java @@ -0,0 +1,26 @@ +package de.steamwar.core; + +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.io.InputStream; + +public class WorldEditWrapper { + private WorldEditWrapper() {} + + public static final IWorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance()); + + public interface IWorldEditWrapper { + byte[] getPlayerClipboard(Player player, boolean schemFormat); + void setPlayerClipboard(Player player, InputStream is, boolean schemFormat); + Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException; + + } + + static WorldEditPlugin getWorldEditPlugin() { + return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); + } +} diff --git a/SpigotCore_Main/src/de/steamwar/sql/Schematic.java b/SpigotCore_Main/src/de/steamwar/sql/Schematic.java index d1e326b..400fc89 100644 --- a/SpigotCore_Main/src/de/steamwar/sql/Schematic.java +++ b/SpigotCore_Main/src/de/steamwar/sql/Schematic.java @@ -20,7 +20,7 @@ package de.steamwar.sql; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import de.steamwar.core.FlatteningWrapper; +import de.steamwar.core.WorldEditWrapper; import org.bukkit.entity.Player; import java.io.IOException; @@ -197,7 +197,7 @@ public class Schematic { public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) { try { - return FlatteningWrapper.impl.getClipboard(is, schemFormat); + return WorldEditWrapper.impl.getClipboard(is, schemFormat); } catch (IOException e) { throw new SecurityException("Could not read schem", e); } @@ -209,7 +209,7 @@ public class Schematic { public void loadToPlayer(Player player) throws IOException, NoClipboardException { InputStream is = schemData(); - FlatteningWrapper.impl.setPlayerClipboard(player, is, schemFormat); + WorldEditWrapper.impl.setPlayerClipboard(player, is, schemFormat); } public void saveOldFormatFromPlayer(Player player) throws IOException, NoClipboardException { @@ -233,7 +233,7 @@ public class Schematic { private void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException { Blob blob = SQL.blob(); try { - blob.setBytes(1, FlatteningWrapper.impl.getPlayerClipboard(player, newFormat)); + blob.setBytes(1, WorldEditWrapper.impl.getPlayerClipboard(player, newFormat)); } catch (SQLException e) { throw new SecurityException(e.getMessage(), e); }