package me.lethunderhawk.dungeon.placement; import com.google.gson.Gson; import com.google.gson.JsonParseException; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; 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.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.bukkit.BukkitAdapter; import me.lethunderhawk.world.util.schematic.Rotation; import me.lethunderhawk.world.util.schematic.Vector3i; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.util.BoundingBox; import java.io.File; import java.io.FileReader; import java.io.IOException; public final class SchematicRoomPlacer { private static final Gson GSON = new Gson(); private SchematicRoomPlacer() {} /** * Places a room schematic at a given anchor. * * @param schematicFile .schem file * @param world Bukkit world * @param anchor anchor location (world-space origin) * @param rotation rotation (NONE, CLOCKWISE_90, CLOCKWISE_180, CLOCKWISE_270) * @return true if successfully validated and placed */ public static boolean placeRoom( File schematicFile, World world, Location anchor, Rotation rotation ) { if(schematicFile == null) return false; if (!schematicFile.exists()) return false; RoomMetadata meta = loadMetadata(schematicFile); if (meta == null) return false; Clipboard clipboard = loadClipboard(schematicFile); if (clipboard == null) return false; BoundingBox box = meta.boundingBox.rotated(rotation).shift(anchor.toVector()); //if (!isAreaClear(world, box)) return false; return pasteClipboard(clipboard, world, anchor, meta.anchor, rotation); } // ---------- Internal ---------- private static Clipboard loadClipboard(File file) { ClipboardFormat format = ClipboardFormats.findByPath(file.getAbsoluteFile().toPath()); if (format == null) return null; try (ClipboardReader reader = format.getReader(new java.io.FileInputStream(file))) { return reader.read(); } catch (IOException e) { return null; } } private static RoomMetadata loadMetadata(File schematic) { File metaFile = new File(schematic.getParent(), schematic.getName().replace(".schem", ".json")); if (!metaFile.exists()) return null; try (FileReader reader = new FileReader(metaFile)) { return GSON.fromJson(reader, RoomMetadata.class); } catch (IOException | JsonParseException e) { return null; } } private static boolean isAreaClear(World world, BoundingBox box) { int minX = (int) box.getMinX(); int minY = (int) box.getMinY(); int minZ = (int) box.getMinZ(); int maxX = (int) box.getMaxX(); int maxY = (int) box.getMaxY(); int maxZ = (int) box.getMaxZ(); for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { if (!world.getBlockAt(x, y, z).isEmpty()) { return false; } } } } return true; } private static boolean pasteClipboard( Clipboard clipboard, World world, Location anchor, Vector3i schematicAnchor, Rotation rotation ) { com.sk89q.worldedit.world.World weWorld = BukkitAdapter.adapt(world); BlockVector3 pastePos = BlockVector3.at( anchor.getBlockX() - schematicAnchor.x(), anchor.getBlockY() - schematicAnchor.y(), anchor.getBlockZ() - schematicAnchor.z() ); try (EditSession session = WorldEdit.getInstance() .newEditSessionBuilder() .world(weWorld) .build()) { ClipboardHolder holder = new ClipboardHolder(clipboard); holder.setTransform(holder.getTransform().combine(rotation.toTransform())); Operation op = holder .createPaste(session) .to(pastePos) .ignoreAirBlocks(true) .build(); Operations.complete(op); session.close(); return true; } catch (Exception e) { return false; } } }