import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.bukkit.util.Vector; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * A basic multi block structure *

* For the sake of simplicity, I am not optimizing symmetrical * structures even further than needed (I could indicate a single corner * and build from the rest). *

* If any future maintainers really want to, they can make symmetrical * detection on schematic printing. * * @author Illusion */ public class BasicMultiBlockStructure { /** * Represents an altar definition */ private final Map definition = new HashMap<>(); /** * Skips half the checks by indicating all 4 sides are the same */ private boolean symmetrical; /** * Register a block to be put into the definition map * * @param relative the relative location * @param material the material */ public void registerBlockAt(Vector relative, Material material) { definition.put(relative, material.createBlockData()); } /** * Register a block to be put into the definition map * * @param relative the relative location * @param data The block data (example: sign) */ public void registerBlockAt(Vector relative, BlockData data) { definition.put(relative, data); } /** * Indicates the schematic is symmectrical, and will skip rotation checks * * @param symmetrical boolean value */ public void setSymmetrical(boolean symmetrical) { this.symmetrical = symmetrical; } /** * Registers a schematic * * @param file The .schem file */ public void registerSchematic(File file) { if (!file.exists()) { System.out.println("File " + file.getName() + " (" + file.getParentFile().getAbsolutePath() + ") does not exist."); file.getParentFile().mkdirs(); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } return; } Clipboard clipboard = getClipboard(file); Vector min = convert(clipboard.getMinimumPoint()); Vector max = convert(clipboard.getMaximumPoint()); Vector origin = convert(clipboard.getOrigin()); for (int x = min.getBlockX(); x <= max.getBlockX(); x++) for (int y = min.getBlockY(); y <= max.getBlockY(); y++) for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { int relX = x - origin.getBlockX(); int relY = y - origin.getBlockY(); int relZ = z - origin.getBlockZ(); BlockState state = clipboard.getBlock(BlockVector3.at(x, y, z)); BlockData data = BukkitAdapter.adapt(state); Vector relPos = new Vector(relX, relY, relZ); if (data.getMaterial() == Material.AIR) continue; definition.put(relPos, data); } } /** * Check if the provided origin matches this structure * * @return {@code true} if so */ public boolean matches(Location origin) { if (origin == null || definition.isEmpty()) { return false; } boolean originalMatch = matches(origin, definition); // Check original rotation if (originalMatch) // If original rotation matches, return true return true; if (symmetrical) // If all 4 sides are the same, there's no need to rotate and re-check return false; return matches(origin, rotate(90)) // Otherwise, check all rotations || matches(origin, rotate(180)) || matches(origin, rotate(270)); } /** * Check if position matches a reference area * * @param origin The origin point * @param blocks The reference area, consisted of Relative Vector - Block data link * @return */ private boolean matches(Location origin, Map blocks) { Map snapshots = new HashMap<>(); for (Map.Entry entry : blocks.entrySet()) { Vector relativePos = entry.getKey(); BlockData matchingData = entry.getValue(); Location newLoc = origin.clone().add(relativePos); ChunkPosition chunkPos = new ChunkPosition(newLoc); ChunkSnapshot snapshot = snapshots.get(chunkPos); if (snapshot == null) { snapshot = newLoc.getChunk().getChunkSnapshot(); snapshots.put(chunkPos, snapshot); } if (snapshot. getBlockData( newLoc.getBlockX() & 0xF, // 0xF = 16, this basically obtains relative position in chunk newLoc.getBlockY(), newLoc.getBlockZ() & 0xF).getMaterial() != matchingData.getMaterial()) return false; } return true; } private Map rotate(int deg) { Map map = new HashMap<>(); for (Map.Entry entry : definition.entrySet()) { Vector key = entry.getKey().clone(); BlockData value = entry.getValue(); key = key.rotateAroundY(deg); map.put(key, value); } return map; } private Vector convert(BlockVector3 vector3) { return new Vector(vector3.getBlockX(), vector3.getBlockY(), vector3.getBlockZ()); } private Clipboard getClipboard(File file) { ClipboardFormat format = ClipboardFormats.findByFile(file); try (ClipboardReader reader = format.getReader(new FileInputStream(file))) { return reader.read(); } catch (IOException e) { e.printStackTrace(); } return null; } }