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;
}
}