package net.aufdemrand.denizen.utilities.blocks; import net.aufdemrand.denizen.nms.NMSHandler; import net.aufdemrand.denizen.nms.interfaces.BlockData; import net.aufdemrand.denizen.nms.util.jnbt.*; import net.aufdemrand.denizen.objects.dCuboid; import net.aufdemrand.denizen.scripts.commands.world.SchematicCommand; import net.aufdemrand.denizen.utilities.DenizenAPI; import net.aufdemrand.denizen.utilities.debugging.dB; import net.aufdemrand.denizencore.utilities.CoreUtilities; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.BlockVector; import org.bukkit.util.Vector; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class CuboidBlockSet implements BlockSet { public CuboidBlockSet() { } public CuboidBlockSet(dCuboid cuboid, Location center) { Location low = cuboid.pairs.get(0).low; Location high = cuboid.pairs.get(0).high; x_width = (high.getX() - low.getX()) + 1; y_length = (high.getY() - low.getY()) + 1; z_height = (high.getZ() - low.getZ()) + 1; center_x = center.getX() - low.getX(); center_y = center.getY() - low.getY(); center_z = center.getZ() - low.getZ(); for (int x = 0; x < x_width; x++) { for (int y = 0; y < y_length; y++) { for (int z = 0; z < z_height; z++) { blocks.add(NMSHandler.getInstance().getBlockHelper().getBlockData(low.clone().add(x, y, z).getBlock())); } } } } public List<BlockData> blocks = new ArrayList<BlockData>(); public double x_width; public double y_length; public double z_height; public double center_x; public double center_y; public double center_z; @Override public List<BlockData> getBlocks() { return blocks; } public dCuboid getCuboid(Location loc) { Location low = loc.clone().subtract(center_x, center_y, center_z); Location high = low.clone().add(x_width, y_length, z_height); return new dCuboid(low, high); } public class IntHolder { public long theInt = 0; } @Override public void setBlocksDelayed(final Location loc, final Runnable runme, final boolean noAir) { final IntHolder index = new IntHolder(); final long goal = (long) (x_width * y_length * z_height); new BukkitRunnable() { @Override public void run() { SchematicCommand.noPhys = true; long start = System.currentTimeMillis(); while (index.theInt < goal) { long z = index.theInt % ((long) (z_height)); long y = ((index.theInt - z) % ((long) (y_length * z_height))) / ((long) z_height); long x = (index.theInt - y - z) / ((long) (y_length * z_height)); if (!noAir || blocks.get((int)index.theInt).getMaterial() != Material.AIR) { blocks.get((int) index.theInt).setBlock(loc.clone().add(x - center_x, y - center_y, z - center_z).getBlock()); } index.theInt++; if (System.currentTimeMillis() - start > 50) { SchematicCommand.noPhys = false; return; } } SchematicCommand.noPhys = false; if (runme != null) { runme.run(); } cancel(); } }.runTaskTimer(DenizenAPI.getCurrentInstance(), 1, 1); } @Override public void setBlocks(Location loc, boolean noAir) { SchematicCommand.noPhys = true; int index = 0; for (int x = 0; x < x_width; x++) { for (int y = 0; y < y_length; y++) { for (int z = 0; z < z_height; z++) { if (!noAir || blocks.get(index).getMaterial() != Material.AIR) { blocks.get(index).setBlock(loc.clone().add(x - center_x, y - center_y, z - center_z).getBlock()); } index++; } } } SchematicCommand.noPhys = false; } public void rotateOne() { List<BlockData> bd = new ArrayList<BlockData>(); double cx = center_x; center_x = center_z; center_z = cx; for (int x = 0; x < z_height; x++) { for (int y = 0; y < y_length; y++) { for (int z = (int)x_width - 1; z >= 0; z--) { bd.add(blockAt(z, y, x)); } } } blocks = bd; } public void flipX() { List<BlockData> bd = new ArrayList<BlockData>(); center_x = x_width - center_x; for (int x = (int)x_width - 1; x >= 0; x--) { for (int y = 0; y < y_length; y++) { for (int z = 0; z < z_height; z++) { bd.add(blockAt(x, y, z)); } } } blocks = bd; } public void flipY() { List<BlockData> bd = new ArrayList<BlockData>(); center_x = x_width - center_x; for (int x = 0; x < x_width; x++) { for (int y = (int)y_length - 1; y >= 0; y--) { for (int z = 0; z < z_height; z++) { bd.add(blockAt(x, y, z)); } } } blocks = bd; } public void flipZ() { List<BlockData> bd = new ArrayList<BlockData>(); center_x = x_width - center_x; for (int x = 0; x < x_width; x++) { for (int y = 0; y < y_length; y++) { for (int z = (int)z_height - 1; z >= 0; z--) { bd.add(blockAt(x, y, z)); } } } blocks = bd; } public BlockData blockAt(double X, double Y, double Z) { return blocks.get((int)(Z + Y * z_height + X * z_height * y_length)); // This calculation should produce the same result as the below nonsense: /* int index = 0; for (int x = 0; x < x_width; x++) { for (int y = 0; y < y_length; y++) { for (int z = 0; z < z_height; z++) { if (x == X && y == Y && z == Z) { return blocks.get(index); } index++; } } } return null; */ } public static CuboidBlockSet fromMCEditStream(InputStream is) { CuboidBlockSet cbs = new CuboidBlockSet(); try { NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(is)); NamedTag rootTag = nbtStream.readNamedTag(); nbtStream.close(); if (!rootTag.getName().equals("Schematic")) { throw new Exception("Tag 'Schematic' does not exist or is not first!"); } CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); Map<String, Tag> schematic = schematicTag.getValue(); short width = (getChildTag(schematic, "Width", ShortTag.class).getValue()); short length = (getChildTag(schematic, "Length", ShortTag.class).getValue()); short height = (getChildTag(schematic, "Height", ShortTag.class).getValue()); int originX = 0; int originY = 0; int originZ = 0; try { originX = getChildTag(schematic, "DenizenOriginX", IntTag.class).getValue(); originY = getChildTag(schematic, "DenizenOriginY", IntTag.class).getValue(); originZ = getChildTag(schematic, "DenizenOriginZ", IntTag.class).getValue(); } catch (Exception e) { // Default origin, why not } cbs.x_width = width; cbs.z_height = length; cbs.y_length = height; cbs.center_x = originX; cbs.center_y = originY; cbs.center_z = originZ; // Disregard Offset String materials = getChildTag(schematic, "Materials", StringTag.class).getValue(); if (!materials.equals("Alpha")) { throw new Exception("Schematic file is not an Alpha schematic!"); } byte[] blockId = getChildTag(schematic, "Blocks", ByteArrayTag.class).getValue(); byte[] blockData = getChildTag(schematic, "Data", ByteArrayTag.class).getValue(); byte[] addId = new byte[0]; short[] blocks = new short[blockId.length]; if (schematic.containsKey("AddBlocks")) { addId = getChildTag(schematic, "AddBlocks", ByteArrayTag.class).getValue(); } for (int index = 0; index < blockId.length; index++) { if ((index >> 1) >= addId.length) { 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)); } } } List<Tag> tileEntities = getChildTag(schematic, "TileEntities", ListTag.class).getValue(); Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<BlockVector, Map<String, Tag>>(); for (Tag tag : tileEntities) { if (!(tag instanceof CompoundTag)) { continue; } CompoundTag t = (CompoundTag) tag; int x = 0; int y = 0; int z = 0; Map<String, Tag> values = new HashMap<String, Tag>(); for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) { if (entry.getKey().equals("x")) { if (entry.getValue() instanceof IntTag) { x = ((IntTag) entry.getValue()).getValue(); } } else if (entry.getKey().equals("y")) { if (entry.getValue() instanceof IntTag) { y = ((IntTag) entry.getValue()).getValue(); } } else if (entry.getKey().equals("z")) { if (entry.getValue() instanceof IntTag) { z = ((IntTag) entry.getValue()).getValue(); } } values.put(entry.getKey(), entry.getValue()); } BlockVector vec = new BlockVector(x, y, z); tileEntitiesMap.put(vec, values); } org.bukkit.util.Vector vec = new Vector(width, height, length); 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; BlockVector pt = new BlockVector(x, y, z); BlockData block = NMSHandler.getInstance().getBlockHelper().getBlockData(blocks[index], blockData[index]); if (tileEntitiesMap.containsKey(pt)) { CompoundTag otag = NMSHandler.getInstance().createCompoundTag(tileEntitiesMap.get(pt)); block.setCompoundTag(otag); } cbs.blocks.add(block); } } } } catch (Exception e) { dB.echoError(e); } return cbs; } private static <T extends Tag> T getChildTag(Map<String, Tag> items, String key, Class<T> expected) throws Exception { if (!items.containsKey(key)) { throw new Exception("Schematic file is missing a '" + key + "' tag"); } Tag tag = items.get(key); if (!expected.isInstance(tag)) { throw new Exception(key + " tag is not of tag type " + expected.getName()); } return expected.cast(tag); } // Thanks to WorldEdit for sample code public void saveMCEditFormatToStream(OutputStream os) { try { HashMap<String, Tag> schematic = new HashMap<String, Tag>(); schematic.put("Width", new ShortTag((short) (x_width))); schematic.put("Length", new ShortTag((short) (z_height))); schematic.put("Height", new ShortTag((short) (y_length))); schematic.put("Materials", new StringTag("Alpha")); schematic.put("DenizenOriginX", new IntTag((int) center_x)); schematic.put("DenizenOriginY", new IntTag((int) center_y)); schematic.put("DenizenOriginZ", new IntTag((int) center_z)); schematic.put("WEOriginX", new IntTag((int) center_x)); schematic.put("WEOriginY", new IntTag((int) center_y)); schematic.put("WEOriginZ", new IntTag((int) center_z)); schematic.put("WEOffsetX", new IntTag(0)); schematic.put("WEOffsetY", new IntTag(0)); schematic.put("WEOffsetZ", new IntTag(0)); byte[] blocks = new byte[(int) ((x_width) * (y_length) * (z_height))]; byte[] addBlocks = null; byte[] blockData = new byte[blocks.length]; ArrayList<Tag> tileEntities = new ArrayList<Tag>(); int indexer = 0; for (int x = 0; x < x_width; x++) { for (int y = 0; y < y_length; y++) { for (int z = 0; z < z_height; z++) { int index = (int) (y * (x_width) * (z_height) + z * (x_width) + x); BlockData bd = this.blocks.get(indexer);//blockAt(x, y, z); indexer++; if (bd.getMaterial().getId() > 255) { if (addBlocks == null) { addBlocks = new byte[(blocks.length >> 1) + 1]; } addBlocks[index >> 1] = (byte) (((index & 1) == 0) ? addBlocks[index >> 1] & 0xF0 | (bd.getMaterial().getId() >> 8) & 0xF : addBlocks[index >> 1] & 0xF | ((bd.getMaterial().getId() >> 8) & 0xF) << 4); } blocks[index] = (byte) bd.getMaterial().getId(); blockData[index] = bd.getData(); CompoundTag rawTag = bd.getCompoundTag(); if (rawTag != null) { HashMap<String, Tag> values = new HashMap<String, Tag>(); for (Map.Entry<String, Tag> entry : rawTag.getValue().entrySet()) { values.put(entry.getKey(), entry.getValue()); } // TODO: ??? -> values.put("id", new StringTag(null)); // block.getNbtId() values.put("x", new IntTag(x)); values.put("y", new IntTag(y)); values.put("z", new IntTag(z)); CompoundTag tileEntityTag = NMSHandler.getInstance().createCompoundTag(values); tileEntities.add(tileEntityTag); } } } } schematic.put("Blocks", new ByteArrayTag(blocks)); schematic.put("Data", new ByteArrayTag(blockData)); schematic.put("Entities", new ListTag(CompoundTag.class, new ArrayList<Tag>())); schematic.put("TileEntities", new ListTag(CompoundTag.class, tileEntities)); if (addBlocks != null) { schematic.put("AddBlocks", new ByteArrayTag(addBlocks)); } CompoundTag schematicTag = NMSHandler.getInstance().createCompoundTag(schematic); NBTOutputStream stream = new NBTOutputStream(new GZIPOutputStream(os)); stream.writeNamedTag("Schematic", schematicTag); os.flush(); stream.close(); } catch (Exception e) { dB.echoError(e); } } }