Created
September 24, 2017 16:37
-
-
Save Kage0x3B/b7a0880652dba8695928ff9220d6e87c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.syscy.kagecore.util; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.bukkit.Location; | |
import org.bukkit.configuration.serialization.ConfigurationSerializable; | |
import com.sk89q.worldedit.bukkit.selections.CuboidSelection; | |
import lombok.Getter; | |
import lombok.ToString; | |
@ToString | |
public class BoundingBox implements ConfigurationSerializable { | |
private @Getter Location min; | |
private @Getter Location max; | |
public BoundingBox(Map<String, Object> map) { | |
this((Location) map.get("min"), (Location) map.get("max")); | |
} | |
public BoundingBox(CuboidSelection selection) { | |
this(selection.getMinimumPoint(), selection.getMaximumPoint()); | |
} | |
public BoundingBox(Location min, Location max) { | |
if(min == null || max == null) { | |
throw new IllegalArgumentException("The min/max locations can't be null"); | |
} | |
if(min.getWorld() != max.getWorld()) { | |
throw new IllegalArgumentException("Both provided locations need to be in the same world"); | |
} | |
this.min = new Location(min.getWorld(), Math.min(min.getX(), max.getX()), Math.min(min.getY(), max.getY()), Math.min(min.getZ(), max.getZ())); | |
this.max = new Location(min.getWorld(), Math.max(min.getX(), max.getX()), Math.max(min.getY(), max.getY()), Math.max(min.getZ(), max.getZ())); | |
} | |
public boolean contains(Location loc) { | |
return (min.getWorld() == null || loc.getWorld() == min.getWorld()) && min.getX() <= loc.getX() && max.getX() >= loc.getX() - 1 && min.getY() <= loc.getY() && max.getY() >= loc.getY() - 1 && min.getZ() <= loc.getZ() && max.getZ() >= loc.getZ() - 1; | |
} | |
@Override | |
public Map<String, Object> serialize() { | |
Map<String, Object> map = new HashMap<>(); | |
map.put("min", min); | |
map.put("max", max); | |
return map; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* WorldEdit, a Minecraft world manipulation toolkit | |
* Copyright (C) sk89q <http://www.sk89q.com> | |
* Copyright (C) WorldEdit team and contributors | |
* | |
* This program is free software: you can redistribute it and/or modify it | |
* under the terms of the GNU Lesser General Public License as published by the | |
* Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | |
* for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
package de.syscy.kagecore.worldedit; | |
import static com.google.common.base.Preconditions.checkNotNull; | |
import com.sk89q.worldedit.Vector; | |
import com.sk89q.worldedit.extent.Extent; | |
import com.sk89q.worldedit.extent.clipboard.Clipboard; | |
import com.sk89q.worldedit.extent.transform.BlockTransformExtent; | |
import com.sk89q.worldedit.function.operation.ForwardExtentCopy; | |
import com.sk89q.worldedit.function.operation.Operation; | |
import com.sk89q.worldedit.math.transform.AffineTransform; | |
import com.sk89q.worldedit.math.transform.CombinedTransform; | |
import com.sk89q.worldedit.math.transform.Transform; | |
import com.sk89q.worldedit.regions.CuboidRegion; | |
import com.sk89q.worldedit.regions.Region; | |
import com.sk89q.worldedit.world.registry.WorldData; | |
/** | |
* Helper class to 'bake' a transform into a clipboard. | |
* | |
* <p>This class needs a better name and may need to be made more generic.</p> | |
* | |
* @see Clipboard | |
* @see Transform | |
*/ | |
public class FlattenedClipboardTransform { | |
private final Clipboard original; | |
private final Transform transform; | |
private final WorldData worldData; | |
/** | |
* Create a new instance. | |
* | |
* @param original the original clipboard | |
* @param transform the transform | |
* @param worldData the world data instance | |
*/ | |
private FlattenedClipboardTransform(Clipboard original, Transform transform, WorldData worldData) { | |
checkNotNull(original); | |
checkNotNull(transform); | |
checkNotNull(worldData); | |
this.original = original; | |
this.transform = transform; | |
this.worldData = worldData; | |
} | |
/** | |
* Get the transformed region. | |
* | |
* @return the transformed region | |
*/ | |
public Region getTransformedRegion() { | |
Region region = original.getRegion(); | |
Vector minimum = region.getMinimumPoint(); | |
Vector maximum = region.getMaximumPoint(); | |
Transform transformAround = new CombinedTransform(new AffineTransform().translate(original.getOrigin().multiply(-1)), transform, new AffineTransform().translate(original.getOrigin())); | |
Vector[] corners = new Vector[] { minimum, maximum, minimum.setX(maximum.getX()), minimum.setY(maximum.getY()), minimum.setZ(maximum.getZ()), maximum.setX(minimum.getX()), maximum.setY(minimum.getY()), maximum.setZ(minimum.getZ()) }; | |
for(int i = 0; i < corners.length; i++) { | |
corners[i] = transformAround.apply(corners[i]); | |
} | |
Vector newMinimum = corners[0]; | |
Vector newMaximum = corners[0]; | |
for(int i = 1; i < corners.length; i++) { | |
newMinimum = Vector.getMinimum(newMinimum, corners[i]); | |
newMaximum = Vector.getMaximum(newMaximum, corners[i]); | |
} | |
// After transformation, the points may not really sit on a block, | |
// so we should expand the region for edge cases | |
newMinimum = newMinimum.setX(Math.floor(newMinimum.getX())); | |
newMinimum = newMinimum.setY(Math.floor(newMinimum.getY())); | |
newMinimum = newMinimum.setZ(Math.floor(newMinimum.getZ())); | |
newMaximum = newMaximum.setX(Math.ceil(newMaximum.getX())); | |
newMaximum = newMaximum.setY(Math.ceil(newMaximum.getY())); | |
newMaximum = newMaximum.setZ(Math.ceil(newMaximum.getZ())); | |
return new CuboidRegion(newMinimum, newMaximum); | |
} | |
/** | |
* Create an operation to copy from the original clipboard to the given extent. | |
* | |
* @param target the target | |
* @return the operation | |
*/ | |
public Operation copyTo(Extent target) { | |
BlockTransformExtent extent = new BlockTransformExtent(original, transform, worldData.getBlockRegistry()); | |
ForwardExtentCopy copy = new ForwardExtentCopy(extent, original.getRegion(), original.getOrigin(), target, original.getOrigin()); | |
copy.setTransform(transform); | |
return copy; | |
} | |
/** | |
* Create a new instance to bake the transform with. | |
* | |
* @param original the original clipboard | |
* @param transform the transform | |
* @param worldData the world data instance | |
* @return a builder | |
*/ | |
public static FlattenedClipboardTransform transform(Clipboard original, Transform transform, WorldData worldData) { | |
return new FlattenedClipboardTransform(original, transform, worldData); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.syscy.kagecore.worldedit; | |
import org.bukkit.Location; | |
import com.sk89q.worldedit.EditSession; | |
import com.sk89q.worldedit.MaxChangedBlocksException; | |
import com.sk89q.worldedit.Vector; | |
import com.sk89q.worldedit.WorldEditException; | |
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; | |
import com.sk89q.worldedit.extent.clipboard.Clipboard; | |
import com.sk89q.worldedit.function.operation.Operation; | |
import com.sk89q.worldedit.function.operation.Operations; | |
import com.sk89q.worldedit.math.transform.AffineTransform; | |
import com.sk89q.worldedit.session.ClipboardHolder; | |
import com.sk89q.worldedit.world.registry.WorldData; | |
import lombok.AccessLevel; | |
import lombok.AllArgsConstructor; | |
import lombok.Getter; | |
import lombok.Setter; | |
@AllArgsConstructor(access = AccessLevel.PROTECTED) | |
public class Schematic { | |
private @Getter @Setter EditSession editSession; | |
private @Getter @Setter ClipboardHolder clipboardHolder; | |
public void rotateX(double theta) { | |
rotate(theta, 0, 0); | |
} | |
public void rotateY(double theta) { | |
rotate(0, theta, 0); | |
} | |
public void rotateZ(double theta) { | |
rotate(0, 0, theta); | |
} | |
public void rotate(double x, double y, double z) { | |
AffineTransform transform = new AffineTransform(); | |
if(x != 0) { | |
transform = transform.rotateX(x); | |
} | |
if(y != 0) { | |
transform = transform.rotateY(y); | |
} | |
if(z != 0) { | |
transform = transform.rotateZ(z); | |
} | |
clipboardHolder.setTransform(clipboardHolder.getTransform().combine(transform)); | |
} | |
/** | |
* Applies the current transform to the clipboard. | |
* Usually this only happens immediately before the schematic is pasted. | |
*/ | |
public void bakeTransform() throws WorldEditException, MaxChangedBlocksException { | |
WorldData worldData = editSession.getWorld().getWorldData(); | |
FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboardHolder.getClipboard(), clipboardHolder.getTransform(), worldData); | |
Clipboard newClipboard = new BlockArrayClipboard(result.getTransformedRegion()); | |
newClipboard.setOrigin(clipboardHolder.getClipboard().getOrigin()); | |
Operations.complete(result.copyTo(newClipboard)); | |
clipboardHolder = new ClipboardHolder(newClipboard, worldData); | |
} | |
public void paste(Location location) throws WorldEditException { | |
paste(location, true); | |
} | |
public void paste(Location location, boolean ignoreAirBlocks) throws WorldEditException { | |
editSession.setBlockChangeLimit(Integer.MAX_VALUE); | |
Vector position = new Vector(location.getBlockX(), location.getBlockY(), location.getBlockZ()); | |
Operation pasteOperation = clipboardHolder.createPaste(editSession, clipboardHolder.getWorldData()).to(position).ignoreAirBlocks(ignoreAirBlocks).build(); | |
Operations.complete(pasteOperation); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.syscy.kagecore.worldedit; | |
import java.io.BufferedInputStream; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import de.syscy.kagecore.util.BoundingBox; | |
import org.bukkit.Location; | |
import org.bukkit.World; | |
import com.sk89q.worldedit.EditSession; | |
import com.sk89q.worldedit.MaxChangedBlocksException; | |
import com.sk89q.worldedit.Vector; | |
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.function.operation.ForwardExtentCopy; | |
import com.sk89q.worldedit.function.operation.Operations; | |
import com.sk89q.worldedit.regions.CuboidRegion; | |
import com.sk89q.worldedit.session.ClipboardHolder; | |
import com.sk89q.worldedit.util.io.Closer; | |
import com.sk89q.worldedit.world.registry.WorldData; | |
import lombok.experimental.UtilityClass; | |
@UtilityClass | |
public class SchematicLoader { | |
/** | |
* Loads a schematic from the plugins/WorldEdit/schematics folder | |
* @param world The world in which the schematic will be used | |
* @param schematicName The file name in the schematics folder | |
*/ | |
public static Schematic load(World world, String schematicName) throws IOException { | |
if(!WorldEditUtil.initWorldEdit()) { | |
return null; | |
} | |
return load(world, WorldEditUtil.getWorldEdit().getWorkingDirectoryFile("schematics/" + schematicName)); | |
} | |
/** | |
* Loads a schematic from a file | |
* @param world The world in which the schematic will be used | |
* @param file | |
*/ | |
public static Schematic load(World world, File schematicFile) throws IOException { | |
if(!WorldEditUtil.initWorldEdit()) { | |
return null; | |
} | |
com.sk89q.worldedit.world.World weWorld = WorldEditUtil.getWEWorld(world); | |
WorldData worldData = weWorld.getWorldData(); | |
ClipboardFormat format = ClipboardFormat.findByFile(schematicFile); | |
Closer closer = Closer.create(); | |
BufferedInputStream inputStream = closer.register(new BufferedInputStream(closer.register(new FileInputStream(schematicFile)))); | |
ClipboardReader clipboardReader = format.getReader(inputStream); | |
Clipboard clipboard = clipboardReader.read(worldData); | |
ClipboardHolder clipboardHolder = new ClipboardHolder(clipboard, worldData); | |
closer.close(); | |
return new Schematic(WorldEditUtil.createSession(weWorld), new ClipboardHolder(clipboardHolder.getClipboard(), clipboardHolder.getWorldData())); | |
} | |
/** | |
* Loads an area in the world into a schematic (for saving it) | |
* @param world The world in which the schematic will be used | |
* @param boundingBox The bounding box of the area | |
* @param origin An origin, used for rotating and when pasting the schematic again. Usually set to (0,0) or the (bottom-)center of the area | |
* @return a {@link Schematic} | |
*/ | |
public static Schematic loadArea(World world, BoundingBox boundingBox, org.bukkit.util.Vector origin) { | |
if(!WorldEditUtil.initWorldEdit()) { | |
return null; | |
} | |
com.sk89q.worldedit.world.World weWorld = WorldEditUtil.getWEWorld(world); | |
CuboidRegion region = new CuboidRegion(toVector(boundingBox.getMin()), toVector(boundingBox.getMax())); | |
EditSession editSession = WorldEditUtil.createSession(weWorld); | |
BlockArrayClipboard clipboard = new BlockArrayClipboard(region); | |
clipboard.setOrigin(new Vector(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ())); | |
ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); | |
try { | |
Operations.completeLegacy(copy); | |
} catch(MaxChangedBlocksException ex) { | |
return null; | |
} | |
return new Schematic(editSession, new ClipboardHolder(clipboard, editSession.getWorld().getWorldData())); | |
} | |
private static Vector toVector(Location location) { | |
return new Vector(location.getBlockX(), location.getBlockY(), location.getBlockZ()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.syscy.kagecore.worldedit; | |
import java.io.BufferedOutputStream; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import com.sk89q.worldedit.extent.clipboard.Clipboard; | |
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; | |
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; | |
import com.sk89q.worldedit.session.ClipboardHolder; | |
import com.sk89q.worldedit.util.io.Closer; | |
import lombok.experimental.UtilityClass; | |
@UtilityClass | |
public class SchematicSaver { | |
/** | |
* Saves a schematic to a file (Ignores any transforms right now) | |
* @param schematic The schematic | |
* @param schematicFile The file to save the schematic to | |
*/ | |
public static boolean save(Schematic schematic, File schematicFile) throws IOException { | |
if(!WorldEditUtil.initWorldEdit()) { | |
return false; | |
} | |
ClipboardHolder clipboardHolder = schematic.getClipboardHolder(); | |
Clipboard clipboard = clipboardHolder.getClipboard(); | |
Closer closer = Closer.create(); | |
File parent = schematicFile.getParentFile(); | |
if(parent != null && !parent.exists()) { | |
if(!parent.mkdirs()) { | |
throw new IOException("Could not create folder for the schematic!"); | |
} | |
} | |
FileOutputStream fileOutputStream = closer.register(new FileOutputStream(schematicFile)); | |
BufferedOutputStream bufferedOutputStream = closer.register(new BufferedOutputStream(fileOutputStream)); | |
ClipboardWriter clipboardWriter = closer.register(ClipboardFormat.SCHEMATIC.getWriter(bufferedOutputStream)); | |
clipboardWriter.write(clipboard, clipboardHolder.getWorldData()); | |
try { | |
closer.close(); | |
} catch(IOException ignored) { | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.syscy.kagecore.worldedit; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.logging.Logger; | |
import org.bukkit.Bukkit; | |
import org.bukkit.plugin.Plugin; | |
import com.sk89q.worldedit.EditSession; | |
import com.sk89q.worldedit.WorldEdit; | |
import com.sk89q.worldedit.bukkit.BukkitWorld; | |
import com.sk89q.worldedit.bukkit.WorldEditPlugin; | |
import com.sk89q.worldedit.world.World; | |
import lombok.AccessLevel; | |
import lombok.Getter; | |
import lombok.experimental.UtilityClass; | |
@UtilityClass | |
public class WorldEditUtil { | |
private static @Getter WorldEditPlugin worldEditPlugin; | |
private static @Getter WorldEdit worldEdit; | |
private static @Getter(value = AccessLevel.PROTECTED) Map<org.bukkit.World, World> worldCache = new HashMap<>(); | |
protected static boolean initWorldEdit() { | |
if(worldEditPlugin == null) { | |
Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit"); | |
if(plugin != null) { | |
worldEditPlugin = (WorldEditPlugin) plugin; | |
worldEdit = worldEditPlugin.getWorldEdit(); | |
} else { | |
Logger.getLogger("KageCore").info("WorldEdit is not installed!"); | |
return false; | |
} | |
} | |
return true; | |
} | |
public static World getWEWorld(org.bukkit.World world) { | |
if(!initWorldEdit()) { | |
return null; | |
} | |
if(worldCache.containsKey(world)) { | |
return worldCache.get(world); | |
} | |
World weWorld = new BukkitWorld(world); | |
worldCache.put(world, weWorld); | |
return weWorld; | |
} | |
public static EditSession createSession(World world) { | |
return worldEdit.getEditSessionFactory().getEditSession(world, Integer.MAX_VALUE); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment