diff --git a/.gitignore b/.gitignore index 85714a0d..e6259922 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,4 @@ gradle.log /bukkit/build /bukkit0/build /bukkit19/build -/bukkit18/build -/core/build \ No newline at end of file +/bukkit18/build \ No newline at end of file diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index d203fd43..23c72def 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -248,8 +248,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (final Throwable e) { e.printStackTrace(); } - } else { - Fawe.debug("Plugin 'WorldGuard' not found. Worldguard features disabled."); } final Plugin plotmePlugin = Bukkit.getServer().getPluginManager().getPlugin("PlotMe"); if ((plotmePlugin != null) && plotmePlugin.isEnabled()) { @@ -259,8 +257,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (final Throwable e) { e.printStackTrace(); } - } else { - Fawe.debug("Plugin 'PlotMe' not found. PlotMe features disabled."); } final Plugin townyPlugin = Bukkit.getServer().getPluginManager().getPlugin("Towny"); if ((townyPlugin != null) && townyPlugin.isEnabled()) { @@ -270,8 +266,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (final Throwable e) { e.printStackTrace(); } - } else { - Fawe.debug("Plugin 'Towny' not found. Towny features disabled."); } final Plugin factionsPlugin = Bukkit.getServer().getPluginManager().getPlugin("Factions"); if ((factionsPlugin != null) && factionsPlugin.isEnabled()) { @@ -292,8 +286,6 @@ public class FaweBukkit implements IFawe, Listener { } } - } else { - Fawe.debug("Plugin 'Factions' not found. Factions features disabled."); } final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence"); if ((residencePlugin != null) && residencePlugin.isEnabled()) { @@ -303,8 +295,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (final Throwable e) { e.printStackTrace(); } - } else { - Fawe.debug("Plugin 'Residence' not found. Factions features disabled."); } final Plugin griefpreventionPlugin = Bukkit.getServer().getPluginManager().getPlugin("GriefPrevention"); if ((griefpreventionPlugin != null) && griefpreventionPlugin.isEnabled()) { @@ -314,8 +304,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (final Throwable e) { e.printStackTrace(); } - } else { - Fawe.debug("Plugin 'GriefPrevention' not found. GriefPrevention features disabled."); } final Plugin preciousstonesPlugin = Bukkit.getServer().getPluginManager().getPlugin("PreciousStones"); if ((preciousstonesPlugin != null) && preciousstonesPlugin.isEnabled()) { @@ -325,8 +313,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (final Throwable e) { e.printStackTrace(); } - } else { - Fawe.debug("Plugin 'PreciousStones' not found. PreciousStones features disabled."); } return managers; } diff --git a/bukkit18/build.gradle b/bukkit18/build.gradle index b42b8cf6..c84fbbe5 100644 --- a/bukkit18/build.gradle +++ b/bukkit18/build.gradle @@ -9,6 +9,7 @@ jar.enabled = false shadowJar { dependencies { include(dependency(':bukkit0')) + include(dependency(':core')) } archiveName = "${parent.name}-${project.name}.jar" destinationDir = file '../target' diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java index 06bbaf77..cb94f1eb 100644 --- a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java @@ -4,7 +4,6 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.util.FaweQueue; import java.lang.reflect.Field; -import java.lang.reflect.Method; import net.minecraft.server.v1_9_R1.Block; import net.minecraft.server.v1_9_R1.DataBits; import net.minecraft.server.v1_9_R1.DataPalette; @@ -51,28 +50,29 @@ public class BukkitChunk_1_9 extends CharFaweChunk { if (current == null) { continue; } - DataPaletteBlock paletteBlock = new DataPaletteBlock(); // Clone palette - DataPalette currentPalette = (DataPalette) fieldPalette.get(paletteBlock); + DataPalette currentPalette = (DataPalette) fieldPalette.get(current); if (!(currentPalette instanceof DataPaletteGlobal)) { - try { - Method resize = DataPaletteBlock.class.getDeclaredMethod("b", int.class); - resize.setAccessible(true); - resize.invoke(paletteBlock, 128); - } catch (Throwable e) { - e.printStackTrace(); - } + current.a(128, null); + } + DataPaletteBlock paletteBlock = new DataPaletteBlock(); + currentPalette = (DataPalette) fieldPalette.get(current); + if (!(currentPalette instanceof DataPaletteGlobal)) { + throw new RuntimeException("Palette must be global!"); } - currentPalette = (DataPalette) fieldPalette.get(paletteBlock); fieldPalette.set(paletteBlock, currentPalette); // Clone size fieldSize.set(paletteBlock, fieldSize.get(current)); - // Clone pallete + // Clone palette DataBits currentBits = (DataBits) fieldBits.get(current); DataBits newBits = new DataBits(1, 0); for (Field field : DataBits.class.getDeclaredFields()) { field.setAccessible(true); - field.set(newBits, field.get(currentBits)); + Object currentValue = field.get(currentBits); + if (currentValue instanceof long[]) { + currentValue = ((long[]) currentValue).clone(); + } + field.set(newBits, currentValue); } fieldBits.set(paletteBlock, newBits); value.sectionPalettes[i] = paletteBlock; diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java index 565a0900..0eb1de5a 100644 --- a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java @@ -43,8 +43,17 @@ import org.bukkit.generator.ChunkGenerator; public class BukkitQueue_1_9_R1 extends BukkitQueue_0 { + private IBlockData air; + public BukkitQueue_1_9_R1(final String world) { super(world); + try { + Field fieldAir = DataPaletteBlock.class.getDeclaredField("a"); + fieldAir.setAccessible(true); + air = (IBlockData) fieldAir.get(null); + } catch (Throwable e) { + e.printStackTrace(); + } } @Override @@ -315,9 +324,12 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0>() { + rollback(player, true, Arrays.copyOfRange(args, 1, args.length), new RunnableVal>() { @Override public void run(List edits) { long total = 0; @@ -94,7 +94,7 @@ public class Rollback extends FaweCommand { @Override public void run() { if (edits.size() == 0) { - player.sendMessage("&d" + BBC.PREFIX.s() + " Rollback complete!"); + player.sendMessage("Rollback complete!"); return; } DiskStorageHistory edit = edits.remove(0); diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index afe1eb5d..c22c4329 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -4,6 +4,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.configuration.file.YamlConfiguration; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.util.StringMan; +import com.sk89q.worldedit.extension.platform.Actor; import java.io.File; import java.util.EnumSet; import java.util.HashMap; @@ -16,14 +17,10 @@ public enum BBC { * Things to note about this class: * Can use multiple arguments %s, %s1, %s2, %s3 etc */ - PREFIX("[FAWE]", "Info"), - COMMAND_SYNTAX("&cUsage: &7%s0", "Error"), - NO_PERM("&cYou are lacking the permission node: %s0", "Error"), - SCHEMATIC_NOT_FOUND("&cSchematic not found: &7%s0", "Error"), + PREFIX("&8(&5&lFAWE&8)&7", "Info"), SCHEMATIC_PASTING("&7The schematic is pasting. This cannot be undone.", "Info"), FIX_LIGHTING_CHUNK("&7Lighting has been fixed in your current chunk. Relog to see the affect.", "Info"), FIX_LIGHTING_SELECTION("&7Lighting has been fixed in %s0 chunks. Relog to see the affect.", "Info"), - NO_REGION("&cYou have no current WorldEdit region", "Error"), SET_REGION("&7Selection set to your current WorldEdit region", "Info"), WORLDEDIT_COMMAND_LIMIT("&7Please wait until your current action completes", "Info"), WORLDEDIT_DELAYED("&7Please wait while we process your WorldEdit action...", "Info"), @@ -39,6 +36,76 @@ public enum BBC { WORLDEDIT_BYPASSED("&7Currently bypassing WorldEdit restriction.", "Info"), WORLDEDIT_UNMASKED("&6Your WorldEdit is now unrestricted.", "Info"), WORLDEDIT_RESTRICTED("&6Your WorldEdit is now restricted.", "Info"), + WORLDEDIT_OOM_ADMIN("&cPossible options:\n&8 - &7//fast\n&8 - &7Do smaller edits\n&8 - &7Allocate more memory\n&8 - &7Disable this safeguard", "Info"), + COMPRESSED("History compressed. Saved ~ %s0b (%s1x smaller)", "Info"), + ACTION_COMPLETE("Action completed in %s0 seconds", "Info"), + + COMMAND_COPY("%s0 blocks were copied", "WorldEdit.Copy"), + COMMAND_PASTE("The clipboard has been pasted at %s0", "WorldEdit.Paste"), + COMMAND_ROTATE("The clipboard has been rotated", "WorldEdit.Rotate"), + COMMAND_FLIPPED("The clipboard has been flipped", "WorldEdit.Flip"), + COMMAND_REGEN("Region regenerated.", "WorldEdit.Regen"), + COMMAND_TREE("%s0 trees created.", "WorldEdit.Tree"), + COMMAND_FLORA("%s0 flora created.", "WorldEdit.Flora"), + COMMAND_HISTORY_CLEAR("History cleared", "WorldEdit.History"), + COMMAND_REDO_FAIL("Nothing left to redo.", "WorldEdit.History"), + COMMAND_REDO_SUCCESS("Redo successful.", "WorldEdit.History"), + COMMAND_UNDO_FAIL("Nothing left to undo.", "WorldEdit.History"), + COMMAND_UNDO_SUCCESS("Undo successful.", "WorldEdit.History"), + + OPERATION("Operation complete (%s0)", "WorldEdit.Operation"), + + SELECTION_WAND("Left click: select pos #1; Right click: select pos #2", "WorldEdit.Selection"), + SELECTION_WAND_DISABLE("Edit wand disabled.", "WorldEdit.Selection"), + SELECTION_WAND_ENABLE("Edit wand enabled.", "WorldEdit.Selection"), + SELECTION_CHUNK("Chunk selected (%s0)", "WorldEdit.Selection"), + SELECTION_CHUNKS("Chunks selected (%s0) - (%s1)", "WorldEdit.Selection"), + SELECTION_CONTRACT("Region contracted %s0 blocks.", "WorldEdit.Selection"), + SELECTION_COUNT("Counted %s0 blocks.", "WorldEdit.Selection"), + SELECTION_DISTR("# total blocks: %s0", "WorldEdit.Selection"), + SELECTION_EXPAND("Region expanded %s0 blocks", "WorldEdit.Selection"), + SELECTION_EXPAND_VERT("Region expanded %s0 blocks (top to bottom)", "WorldEdit.Selection"), + SELECTION_INSET("Region inset", "WorldEdit.Selection"), + SELECTION_OUTSET("Region outset", "WorldEdit.Selection"), + SELECTION_SHIFT("Region shifted", "WorldEdit.Selection"), + SELECTION_CLEARED("Selection cleared", "WorldEdit.Selection"), + + BRUSH_BUTCHER("Butcher brush equiped (%s0)", "WorldEdit.Brush"), + BRUSH_CLIPBOARD("Clipboard brush shape equipped", "WorldEdit.Brush"), + BRUSH_CYLINDER("Cylinder brush shape equipped (%s0 by %s1).", "WorldEdit.Brush"), + BRUSH_EXTINGUISHER("Extinguisher equipped (%s0).", "WorldEdit.Brush"), + BRUSH_GRAVITY("Gravity brush equipped (%s0)", "WorldEdit.Brush"), + BRUSH_HEIGHT("Height brush equipped (%s0)", "WorldEdit.Brush"), + BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"), + BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2).", "WorldEdit.Brush"), + BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"), + + SCHEMATIC_DELETE("%s0 has been deleted.", "Worldedit.Schematic"), + SCHEMATIC_FORMAT("Available clipboard formats (Name: Lookup names)", "Worldedit.Schematic"), + SCHEMATIC_LIST("Available schematics (Filename (Format)):", "Worldedit.Schematic"), + SCHEMATIC_LOADED("%s0 loaded. Paste it with //paste", "Worldedit.Schematic"), + SCHEMATIC_SAVED("%s0 saved.", "Worldedit.Schematic"), + + CLIPBOARD_CLEARED("Clipboard cleared", "WorldEdit.Clipboard"), + + VISITOR_BLOCK("%s0 blocks affected", "WorldEdit.Visitor"), + VISITOR_ENTITY("%s0 entities affected", "WorldEdit.Visitor"), + VISITOR_FLAT("%s0 columns affected", "WorldEdit.Visitor"), + + SELECTOR_CUBOID_POS1("First position set to %s0 %s1.", "WorldEdit.Selector"), + SELECTOR_CUBOID_POS2("Second position set to %s0 %s1.", "WorldEdit.Selector"), + + + + COMMAND_SYNTAX("&cUsage: &7%s0", "Error"), + NO_PERM("&cYou are lacking the permission node: %s0", "Error"), + SCHEMATIC_NOT_FOUND("&cSchematic not found: &7%s0", "Error"), + NO_REGION("&cYou have no current WorldEdit region", "Error"), + NOT_PLAYER("&cYou must be a player to perform this action!", "Error"), + OOM( + "&8[&cCritical&8] &cDetected low memory i.e. < 1%. FAWE will take the following actions:\n&8 - &7Terminate WE block placement\n&8 - &7Clear WE history\n&8 - &7Unload non essential chunks\n&8 - &7Kill entities\n&8 - &7Garbage collect\n&cIgnore this if trying to crash server.\n&7Note: Low memory is likely (but not necessarily) caused by WE", + "Error"), + WORLDEDIT_CANCEL_COUNT("&cCancelled %s0 edits.", "Cancel"), WORLDEDIT_CANCEL_REASON("&cYour WorldEdit action was cancelled:&7 %s0&c.", "Cancel"), WORLDEDIT_CANCEL_REASON_MANUAL("Manual cancellation", "Cancel"), @@ -51,16 +118,15 @@ public enum BBC { WORLDEDIT_CANCEL_REASON_MAX_FAILS("Outside allowed region", "Cancel"), WORLDEDIT_FAILED_LOAD_CHUNK("&cSkipped loading chunk: &7%s0;%s1&c. Try increasing chunk-wait.", "Cancel"), - LOADING_CLIPBOARD("&dLoading clipboard from disk, please wait.", "History"), - INDEXING_HISTORY("&dIndexing %s history objects on disk, please wait.", "History"), - INDEXING_COMPLETE("&dIndexing complete. Took: %s seconds!", "History"), + LOADING_CLIPBOARD("Loading clipboard from disk, please wait.", "History"), + INDEXING_HISTORY("Indexing %s history objects on disk, please wait.", "History"), + INDEXING_COMPLETE("Indexing complete. Took: %s seconds!", "History"), + + + + + ; - WORLDEDIT_OOM_ADMIN("&cPossible options:\n&8 - &7//fast\n&8 - &7Do smaller edits\n&8 - &7Allocate more memory\n&8 - &7Disable this safeguard", "Info"), - NOT_PLAYER("&cYou must be a player to perform this action!", "Error"), - COMPRESSED("History compressed. Saved ~ %s0b (%s1x smaller)", "Info"), - OOM( - "&8[&cCritical&8] &cDetected low memory i.e. < 1%. FAWE will take the following actions:\n&8 - &7Terminate WE block placement\n&8 - &7Clear WE history\n&8 - &7Unload non essential chunks\n&8 - &7Kill entities\n&8 - &7Garbage collect\n&cIgnore this if trying to crash server.\n&7Note: Low memory is likely (but not necessarily) caused by WE", - "Error"); private static final HashMap replacements = new HashMap<>(); /** @@ -194,6 +260,19 @@ public enum BBC { } } + @Override + public String toString() { + return s(); + } + + public boolean isEmpty() { + return length() == 0; + } + + public int length() { + return toString().length(); + } + public static String color(String string) { return StringMan.replaceFromMap(string, replacements); } @@ -210,11 +289,25 @@ public enum BBC { return this.cat; } - public void send(final FawePlayer player, final Object... args) { - if (player == null) { - Fawe.debug(this.format(args)); + public void send(Actor actor, final Object... args) { + if (isEmpty()) { + return; + } + if (actor == null) { + Fawe.debug((PREFIX.isEmpty() ? "" : PREFIX.s() + " ") + this.format(args)); } else { - player.sendMessage(this.format(args)); + actor.print((PREFIX.isEmpty() ? "" : PREFIX.s() + " ") + this.format(args)); + } + } + + public void send(final FawePlayer player, final Object... args) { + if (isEmpty()) { + return; + } + if (player == null) { + Fawe.debug((PREFIX.isEmpty() ? "" : PREFIX.s() + " ") + this.format(args)); + } else { + player.sendMessage((PREFIX.isEmpty() ? "" : PREFIX.s() + " ") + this.format(args)); } } diff --git a/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java b/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java index 8cc8b363..0a7aa1ce 100644 --- a/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java +++ b/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java @@ -77,7 +77,7 @@ public abstract class CharFaweChunk extends FaweChunk { public int getTotalCount() { int total = 0; for (int i = 0; i < 16; i++) { - total += this.count[i]; + total += Math.min(4096, this.count[i]); } return total; } diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java index bd3b7a09..2e49bd1f 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -120,7 +120,7 @@ public abstract class FawePlayer { } } catch (EmptyClipboardException e) {} if (player != null && session != null) { - sendMessage("&d" + BBC.PREFIX.s() + " " + BBC.LOADING_CLIPBOARD.s()); + BBC.LOADING_CLIPBOARD.send(this); WorldData worldData = player.getWorld().getWorldData(); Clipboard clip = doc.toClipboard(); ClipboardHolder holder = new ClipboardHolder(clip, worldData); @@ -162,7 +162,7 @@ public abstract class FawePlayer { } } if (editIds.size() > 0) { - sendMessage("&d" + BBC.PREFIX.s() + " " + BBC.INDEXING_HISTORY.format(editIds.size())); + BBC.INDEXING_HISTORY.send(this, editIds.size()); TaskManager.IMP.async(new Runnable() { @Override public void run() { @@ -177,7 +177,7 @@ public abstract class FawePlayer { return; } } - sendMessage("&d" + BBC.PREFIX.s() + " " + BBC.INDEXING_COMPLETE.format((System.currentTimeMillis() - start) / 1000d)); + BBC.INDEXING_COMPLETE.send(FawePlayer.this, (System.currentTimeMillis() - start) / 1000d); } }); } diff --git a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java index 906157d7..60d1e2fa 100644 --- a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java @@ -28,7 +28,14 @@ public class NullChangeSet implements FaweChangeSet { } @Override - public void flush() {} + public boolean flush() { + return false; + } + + @Override + public int getCompressedSize() { + return 0; + } @Override public void add(Vector location, BaseBlock from, BaseBlock to) {} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java new file mode 100644 index 00000000..efaf378a --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java @@ -0,0 +1,110 @@ +package com.boydti.fawe.object.brush; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.brush.heightmap.ArrayHeightMap; +import com.boydti.fawe.object.brush.heightmap.HeightMap; +import com.boydti.fawe.object.exception.FaweException; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.function.pattern.Pattern; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +public class HeightBrush implements Brush { + + public final HeightMap heightMap; + private final int rotation; + double yscale = 1; + + public HeightBrush(File file, int rotation, double yscale) { + this.rotation = (rotation / 90) % 4; + this.yscale = yscale; + if (file == null || !file.exists()) { + heightMap = new HeightMap(); + } else { + try { + BufferedImage heightFile = ImageIO.read(file); + int width = heightFile.getWidth(); + int length = heightFile.getHeight(); + Raster data = heightFile.getData(); + byte[][] array = new byte[width][length]; + for (int x = 0; x < width; x++) { + for (int z = 0; z < length; z++) { + int pixel = heightFile.getRGB(x, z); + int red = (pixel >> 16) & 0xFF; + int green = (pixel >> 8) & 0xFF; + int blue = (pixel >> 0) & 0xFF; + int intensity = (red + green + blue) / 3; + array[x][z] = (byte) intensity; + } + } + heightMap = new ArrayHeightMap(array); + } catch (IOException e) { + throw new FaweException(BBC.BRUSH_HEIGHT_INVALID); + } + } + } + + @Override + public void build(EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { + + int size = (int) sizeDouble; + heightMap.setSize(size); + + int size2 = size * size; + int startY = position.getBlockY() + size; + int endY = position.getBlockY() - size; + int cx = position.getBlockX(); + int cz = position.getBlockZ(); + Vector mutablePos = new Vector(0, 0, 0); + for (int x = -size; x <= size; x++) { + int xx = cx + x; + for (int z = -size; z <= size; z++) { + int zz = cz + z; + int raise; + switch (rotation) { + default: + raise = heightMap.getHeight(x, z); + break; + case 1: + raise = heightMap.getHeight(z, x); + break; + case 2: + raise = heightMap.getHeight(-x, -z); + break; + case 3: + raise = heightMap.getHeight(-z, -x); + break; + } + raise = (int) (yscale * raise); + if (raise == 0) { + continue; + } + int foundHeight = Integer.MAX_VALUE; + BaseBlock block = null; + for (int y = startY; y >= endY; y--) { + block = editSession.getLazyBlock(xx, y, zz); + if (block != EditSession.nullBlock) { + foundHeight = y; + break; + } + } + if (foundHeight == Integer.MAX_VALUE) { + continue; + } + for (int y = foundHeight + 1; y <= foundHeight + raise; y++) { + mutablePos.x = xx; + mutablePos.y = y; + mutablePos.z = zz; + editSession.setBlock(mutablePos, block); + } + } + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java new file mode 100644 index 00000000..57f635e5 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java @@ -0,0 +1,33 @@ +package com.boydti.fawe.object.brush.heightmap; + +public class ArrayHeightMap extends HeightMap { + // The heights + private final byte[][] height; + // The height map width/length + private final int width, length; + // The size to width/length ratio + private double rx,rz; + + public ArrayHeightMap(byte[][] height) { + setSize(5); + this.height = height; + this.width = height.length; + this.length = height[0].length; + } + + @Override + public void setSize(int size) { + super.setSize(size); + this.rx = (double) width / (size << 1); + this.rz = (double) width / (size << 1); + + } + + @Override + public int getHeight(int x, int z) { + x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx)); + z = (int) Math.max(0, Math.min(length - 1, (z + size) * rz)); + return (((int) height[x][z] & 0xFF) * size) / 256; + + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java new file mode 100644 index 00000000..beecee72 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java @@ -0,0 +1,31 @@ +package com.boydti.fawe.object.brush.heightmap; + +import com.boydti.fawe.util.MathMan; + +public class HeightMap { + public int size2; + public int size; + + public HeightMap() { + setSize(5); + } + + public HeightMap(int size) { + setSize(size); + } + + public void setSize(int size) { + this.size = size; + this.size2 = size * size; + } + + public int getHeight(int x, int z) { + int dx = Math.abs(x); + int dz = Math.abs(z); + int d2 = dx * dx + dz * dz; + if (d2 > size2) { + return 0; + } + return size - MathMan.sqrt(d2); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index d64a86aa..6ba3355d 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -2,7 +2,6 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.RegionWrapper; @@ -96,7 +95,7 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet { private int ox; private int oz; - private AtomicInteger size = new AtomicInteger(); + private int size; private World world; public void deleteFiles() { @@ -108,7 +107,6 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet { } public DiskStorageHistory(World world, UUID uuid) { - size = new AtomicInteger(); String base = "history" + File.separator + world.getName() + File.separator + uuid; File folder = new File(Fawe.imp().getDirectory(), base); int max = 0; @@ -159,29 +157,32 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet { @Override public void add(Change change) { - size.incrementAndGet(); if ((change instanceof BlockChange)) { add((BlockChange) change); } else { - Fawe.debug(BBC.PREFIX.s() + "Does not support " + change + " yet! (Please bug Empire92)"); + Fawe.debug("Does not support " + change + " yet! (Please bug Empire92)"); } } @Override - public void flush() { + public boolean flush() { + boolean flushed = false; try { if (osBD != null) { + flushed = true; osBD.flush(); osBD.close(); osBD = null; } if (osNBTF != null) { + flushed = true; osNBTFG.flush(); osNBTF.close(); osNBTF = null; osNBTFG = null; } if (osNBTT != null) { + flushed = true; osNBTTG.flush(); osNBTT.close(); osNBTT = null; @@ -190,10 +191,17 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet { } catch (Exception e) { e.printStackTrace(); } + return flushed; + } + + @Override + public int getCompressedSize() { + return bdFile.exists() ? (int) bdFile.length() : 0; } @Override public void add(int x, int y, int z, int combinedFrom, int combinedTo) { + size++; try { OutputStream stream = getBAOS(x, y, z); //x @@ -548,7 +556,7 @@ public class DiskStorageHistory implements ChangeSet, FaweChangeSet { @Override public int size() { flush(); - return size.get(); + return size; } public static class DiskStorageSummary { diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java index ab36eae8..47a407ca 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -5,7 +5,9 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.history.changeset.ChangeSet; public interface FaweChangeSet extends ChangeSet { - void flush(); + boolean flush(); + + int getCompressedSize(); void add(Vector location, BaseBlock from, BaseBlock to); diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java index 5055f291..59e1445c 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java @@ -1,8 +1,6 @@ package com.boydti.fawe.object.changeset; -import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; @@ -281,43 +279,26 @@ public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet { } @Override - public void flush() { + public boolean flush() { if (idsStreamZip != null) { try { idsStream.flush(); idsStreamZip.flush(); idsStreamZip.close(); ids = idsStream.toByteArray(); - /* - * BlockVector - * - reference to the object --> 8 bytes - * - object header (java internals) --> 8 bytes - * - double x, y, z --> 24 bytes - * - * BaseBlock - * - reference to the object --> 8 bytes - * - object header (java internals) --> 8 bytes - * - short id, data --> 4 bytes - * - NBTCompound (assuming null) --> 4 bytes - * - * There are usually two lists for the block changes: - * 2 * BlockVector + 2 * BaseBlock = 128b - * - * This compares FAWE's usage to standard WE. - */ - int total = 128 * size; - int current = ids.length + 16; - int ratio = total / current; - int saved = total - current; - if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive() && BBC.COMPRESSED.s().length() > 0) { - actor.print(BBC.PREFIX.s() + " " + BBC.COMPRESSED.format(saved, ratio)); - } idsStream = null; idsStreamZip = null; } catch (IOException e) { e.printStackTrace(); } + return true; } + return false; } - + + @Override + public int getCompressedSize() { + return ids == null ? 0 : ids.length; + } + } diff --git a/core/src/main/java/com/boydti/fawe/util/FaweQueue.java b/core/src/main/java/com/boydti/fawe/util/FaweQueue.java index 0fbb189a..844045ab 100644 --- a/core/src/main/java/com/boydti/fawe/util/FaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/FaweQueue.java @@ -94,7 +94,7 @@ public abstract class FaweQueue { try { return getCombinedId4Data(x, y, z); } catch (FaweException ignore) { - session.debug(BBC.WORLDEDIT_FAILED_LOAD_CHUNK.format(x >> 4, z >> 4)); + session.debug(BBC.WORLDEDIT_FAILED_LOAD_CHUNK, x >> 4, z >> 4); return def; } } diff --git a/core/src/main/java/com/boydti/fawe/util/MainUtil.java b/core/src/main/java/com/boydti/fawe/util/MainUtil.java index 020a3f0c..ca90d4c4 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -5,10 +5,12 @@ import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.EndTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.extension.platform.Actor; import java.io.File; import java.lang.reflect.Array; import java.util.Map; @@ -37,6 +39,44 @@ public class MainUtil { Fawe.debug(s); } + public static void sendCompressedMessage(FaweChangeSet set, Actor actor) + { + try { + int elements = set.size(); + int compressedSize = set.getCompressedSize(); + /* + * BlockVector + * - reference to the object --> 8 bytes + * - object header (java internals) --> 8 bytes + * - double x, y, z --> 24 bytes + * + * BaseBlock + * - reference to the object --> 8 bytes + * - object header (java internals) --> 8 bytes + * - short id, data --> 4 bytes + * - NBTCompound (assuming null) --> 4 bytes + * + * There are usually two lists for the block changes: + * 2 * BlockVector + 2 * BaseBlock = 128b + * + * WE has a lot more overhead, this is just a generous lower bound + * + * This compares FAWE's usage to standard WE. + */ + int total = 128 * elements; + int current = compressedSize; + + int ratio = total / current; + int saved = total - current; + + if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive()) { + BBC.COMPRESSED.send(actor, saved, ratio); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + public static void warnDeprecated(Class... alternatives) { StackTraceElement[] stack = new RuntimeException().getStackTrace(); if (stack.length > 1) { diff --git a/core/src/main/java/com/boydti/fawe/util/MathMan.java b/core/src/main/java/com/boydti/fawe/util/MathMan.java index 6b7705fd..a7a63f58 100644 --- a/core/src/main/java/com/boydti/fawe/util/MathMan.java +++ b/core/src/main/java/com/boydti/fawe/util/MathMan.java @@ -21,6 +21,96 @@ public class MathMan { } } + private final static int[] table = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, + 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, + 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, + 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, + 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, + 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, + 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, + 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, + 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, + 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206, + 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, + 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, + 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, + 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, + 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, + 253, 254, 254, 255 + }; + + public static int sqrt(int x) { + int xn; + + if (x >= 0x10000) { + if (x >= 0x1000000) { + if (x >= 0x10000000) { + if (x >= 0x40000000) { + xn = table[x >> 24] << 8; + } else { + xn = table[x >> 22] << 7; + } + } else { + if (x >= 0x4000000) { + xn = table[x >> 20] << 6; + } else { + xn = table[x >> 18] << 5; + } + } + + xn = (xn + 1 + (x / xn)) >> 1; + xn = (xn + 1 + (x / xn)) >> 1; + return ((xn * xn) > x) ? --xn : xn; + } else { + if (x >= 0x100000) { + if (x >= 0x400000) { + xn = table[x >> 16] << 4; + } else { + xn = table[x >> 14] << 3; + } + } else { + if (x >= 0x40000) { + xn = table[x >> 12] << 2; + } else { + xn = table[x >> 10] << 1; + } + } + + xn = (xn + 1 + (x / xn)) >> 1; + + return ((xn * xn) > x) ? --xn : xn; + } + } else { + if (x >= 0x100) { + if (x >= 0x1000) { + if (x >= 0x4000) { + xn = (table[x >> 8]) + 1; + } else { + xn = (table[x >> 6] >> 1) + 1; + } + } else { + if (x >= 0x400) { + xn = (table[x >> 4] >> 2) + 1; + } else { + xn = (table[x >> 2] >> 3) + 1; + } + } + + return ((xn * xn) > x) ? --xn : xn; + } else { + if (x >= 0) { + return table[x] >> 4; + } + } + } + throw new IllegalArgumentException("Invalid number:" + x); + } + + public static double getMean(int[] array) { double count = 0; for (int i : array) { diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 568ac2a7..d1cd3cb0 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -359,10 +359,8 @@ public class EditSession implements Extent { return; } - public void debug(String message) { - if (actor != null && message != null && message.length() > 0) { - actor.print(BBC.PREFIX.s() + " " + message); - } + public void debug(BBC message, Object... args) { + message.send(actor, args); } public FaweQueue getQueue() { diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index 897eca12..209029c8 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.SetQueue; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; @@ -217,7 +218,11 @@ public class LocalSession { } ChangeSet set = editSession.getChangeSet(); if (set instanceof FaweChangeSet) { - ((FaweChangeSet) set).flush(); + FaweChangeSet fcs = (FaweChangeSet) set; + if (fcs.flush() && append) { + MainUtil.sendCompressedMessage(fcs, editSession.actor); + } + } if (append) { history.add(editSession); diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index c46fedce..8587d665 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -19,9 +19,12 @@ package com.sk89q.worldedit.command; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.brush.HeightBrush; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -51,6 +54,8 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; +import java.io.File; + import static com.google.common.base.Preconditions.checkNotNull; @@ -96,8 +101,7 @@ public class BrushCommands { } else { tool.setBrush(new SphereBrush(), "worldedit.brush.sphere"); } - - player.print(String.format("Sphere brush shape equipped (%.0f).", radius)); + BBC.BRUSH_SPHERE.send(player, radius); } @Command( @@ -126,8 +130,7 @@ public class BrushCommands { } else { tool.setBrush(new CylinderBrush(height), "worldedit.brush.cylinder"); } - - player.print(String.format("Cylinder brush shape equipped (%.0f by %d).", radius, height)); + BBC.BRUSH_CYLINDER.send(player, radius, height); } @Command( @@ -154,8 +157,7 @@ public class BrushCommands { BrushTool tool = session.getBrushTool(player.getItemInHand()); tool.setBrush(new ClipboardBrush(holder, ignoreAir, usingOrigin), "worldedit.brush.clipboard"); - - player.print("Clipboard brush shape equipped."); + BBC.BRUSH_CLIPBOARD.send(player); } @Command( @@ -183,8 +185,7 @@ public class BrushCommands { tool.setSize(radius); tool.setBrush(new SmoothBrush(iterations, naturalBlocksOnly), "worldedit.brush.smooth"); - player.print(String.format("Smooth brush equipped (%.0f x %dx, using " + (naturalBlocksOnly ? "natural blocks only" : "any block") + ").", - radius, iterations)); + BBC.BRUSH_SMOOTH.send(player, radius, iterations, (naturalBlocksOnly ? "natural blocks only" : "any block")); } @Command( @@ -204,8 +205,7 @@ public class BrushCommands { tool.setSize(radius); tool.setMask(new BlockMask(editSession, new BaseBlock(BlockID.FIRE))); tool.setBrush(new SphereBrush(), "worldedit.brush.ex"); - - player.print(String.format("Extinguisher equipped (%.0f).", radius)); + BBC.BRUSH_EXTINGUISHER.send(player, radius); } @Command( @@ -227,9 +227,27 @@ public class BrushCommands { BrushTool tool = session.getBrushTool(player.getItemInHand()); tool.setSize(radius); tool.setBrush(new GravityBrush(fromMaxY), "worldedit.brush.gravity"); + BBC.BRUSH_GRAVITY.send(player, radius); + } - player.print(String.format("Gravity brush equipped (%.0f).", - radius)); + @Command( + aliases = { "height", "high" }, + usage = "[radius] [file] [rotation] [yscale]", + flags = "h", + desc = "Height brush", + help = + "This brush raises land.\n", + min = 0, + max = 4 + ) + @CommandPermissions("worldedit.brush.height") + public void heightBrush(Player player, LocalSession session, EditSession editSession, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + File file = new File(Fawe.imp().getDirectory(), "heightmap" + File.separator + (filename.endsWith(".png") ? filename : filename + ".png")); + BrushTool tool = session.getBrushTool(player.getItemInHand()); + tool.setSize(radius); + tool.setBrush(new HeightBrush(file, rotation, yscale), "worldedit.brush.height"); + BBC.BRUSH_HEIGHT.send(player, radius); } @Command( @@ -274,8 +292,7 @@ public class BrushCommands { BrushTool tool = session.getBrushTool(player.getItemInHand()); tool.setSize(radius); tool.setBrush(new ButcherBrush(flags), "worldedit.brush.butcher"); - - player.print(String.format("Butcher brush equipped (%.0f).", radius)); + BBC.BRUSH_BUTCHER.send(player, radius); } public static Class inject() { diff --git a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 9f5fd4b6..a22f1819 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.clipboard.LazyClipboard; import com.sk89q.minecraft.util.commands.Command; @@ -135,7 +136,7 @@ public class ClipboardCommands { BlockArrayClipboard clipboard = new BlockArrayClipboard(region, lazyClipboard); clipboard.setOrigin(session.getPlacementPosition(player)); session.setClipboard(new ClipboardHolder(clipboard, editSession.getWorld().getWorldData())); - player.print(region.getArea() + " block(s) were not copied. I'll do it later, promise!"); + BBC.COMMAND_COPY.send(player, region.getArea()); } @@ -167,7 +168,7 @@ public class ClipboardCommands { Operations.completeLegacy(copy); session.setClipboard(new ClipboardHolder(clipboard, editSession.getWorld().getWorldData())); - player.print(region.getArea() + " block(s) were copied. Note: For faster copying use //lazycopy"); + BBC.COMMAND_COPY.send(player, region.getArea()); } @Command( @@ -199,7 +200,7 @@ public class ClipboardCommands { Operations.completeLegacy(copy); session.setClipboard(new ClipboardHolder(clipboard, editSession.getWorld().getWorldData())); - player.print(region.getArea() + " block(s) were copied."); + BBC.COMMAND_COPY.send(player, region.getArea()); } @Command( @@ -244,8 +245,7 @@ public class ClipboardCommands { selector.learnChanges(); selector.explainRegionAdjust(player, session); } - - player.print("The clipboard has been pasted at " + to); + BBC.COMMAND_PASTE.send(player, to); } @Command( @@ -332,7 +332,7 @@ public class ClipboardCommands { selector.learnChanges(); selector.explainRegionAdjust(player, session); } - player.print("The clipboard has been placed at " + to); + BBC.COMMAND_PASTE.send(player, to); } @Command( @@ -357,7 +357,7 @@ public class ClipboardCommands { transform = transform.rotateX(-(xRotate != null ? xRotate : 0)); transform = transform.rotateZ(-(zRotate != null ? zRotate : 0)); holder.setTransform(holder.getTransform().combine(transform)); - player.print("The clipboard copy has been rotated."); + BBC.COMMAND_ROTATE.send(player); } @Command( @@ -377,7 +377,7 @@ public class ClipboardCommands { AffineTransform transform = new AffineTransform(); transform = transform.scale(direction.positive().multiply(-2).add(1, 1, 1)); holder.setTransform(holder.getTransform().combine(transform)); - player.print("The clipboard copy has been flipped."); + BBC.COMMAND_FLIPPED.send(player); } @Command( @@ -416,7 +416,7 @@ public class ClipboardCommands { @CommandPermissions("worldedit.clipboard.clear") public void clearClipboard(Player player, LocalSession session, EditSession editSession) throws WorldEditException { session.setClipboard(null); - player.print("Clipboard cleared."); + BBC.CLIPBOARD_CLEARED.send(player); } public static Class inject() { return ClipboardCommands.class; diff --git a/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java new file mode 100644 index 00000000..26b7f998 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java @@ -0,0 +1,131 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.command; + +import com.boydti.fawe.config.BBC; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Commands to undo, redo, and clear history. + */ +public class HistoryCommands { + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public HistoryCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; + } + + @Command( + aliases = { "/undo", "undo" }, + usage = "[times] [player]", + desc = "Undoes the last action", + min = 0, + max = 2 + ) + @CommandPermissions("worldedit.history.undo") + public void undo(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + int times = Math.max(1, args.getInteger(0, 1)); + for (int i = 0; i < times; ++i) { + EditSession undone; + if (args.argsLength() < 2) { + undone = session.undo(session.getBlockBag(player), player); + } else { + player.checkPermission("worldedit.history.undo.other"); + LocalSession sess = worldEdit.getSession(args.getString(1)); + if (sess == null) { + player.printError("Unable to find session for " + args.getString(1)); + break; + } + undone = sess.undo(session.getBlockBag(player), player); + } + if (undone != null) { + BBC.COMMAND_UNDO_SUCCESS.send(player); + worldEdit.flushBlockBag(player, undone); + } else { + BBC.COMMAND_UNDO_FAIL.send(player); + break; + } + } + } + + @Command( + aliases = { "/redo", "redo" }, + usage = "[times] [player]", + desc = "Redoes the last action (from history)", + min = 0, + max = 2 + ) + @CommandPermissions("worldedit.history.redo") + public void redo(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + int times = Math.max(1, args.getInteger(0, 1)); + + for (int i = 0; i < times; ++i) { + EditSession redone; + if (args.argsLength() < 2) { + redone = session.redo(session.getBlockBag(player), player); + } else { + player.checkPermission("worldedit.history.redo.other"); + LocalSession sess = worldEdit.getSession(args.getString(1)); + if (sess == null) { + player.printError("Unable to find session for " + args.getString(1)); + break; + } + redone = sess.redo(session.getBlockBag(player), player); + } + if (redone != null) { + BBC.COMMAND_REDO_SUCCESS.send(player); + worldEdit.flushBlockBag(player, redone); + } else { + BBC.COMMAND_REDO_FAIL.send(player); + } + } + } + + @Command( + aliases = { "/clearhistory", "clearhistory" }, + usage = "", + desc = "Clear your history", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.history.clear") + public void clearHistory(Player player, LocalSession session, EditSession editSession) throws WorldEditException { + session.clearHistory(); + BBC.COMMAND_HISTORY_CLEAR.send(player); + } + + public static Class inject() { + return HistoryCommands.class; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java new file mode 100644 index 00000000..abba0a44 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -0,0 +1,483 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.command; + +import com.boydti.fawe.config.BBC; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.minecraft.util.commands.Logging; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.GroundFunction; +import com.sk89q.worldedit.function.generator.FloraGenerator; +import com.sk89q.worldedit.function.generator.ForestGenerator; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.NoiseFilter2D; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; +import com.sk89q.worldedit.function.visitor.LayerVisitor; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.math.convolution.GaussianKernel; +import com.sk89q.worldedit.math.convolution.HeightMap; +import com.sk89q.worldedit.math.convolution.HeightMapFilter; +import com.sk89q.worldedit.math.noise.RandomNoise; +import com.sk89q.worldedit.regions.ConvexPolyhedralRegion; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.TreeGenerator.TreeType; +import com.sk89q.worldedit.util.command.binding.Range; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.command.binding.Text; +import com.sk89q.worldedit.util.command.parametric.Optional; +import java.util.ArrayList; +import java.util.List; + + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.ORIENTATION_REGION; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import static com.sk89q.worldedit.regions.Regions.asFlatRegion; +import static com.sk89q.worldedit.regions.Regions.maximumBlockY; +import static com.sk89q.worldedit.regions.Regions.minimumBlockY; + +/** + * Commands that operate on regions. + */ +public class RegionCommands { + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public RegionCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; + } + + @Command( + aliases = { "/line" }, + usage = " [thickness]", + desc = "Draws a line segment between cuboid selection corners", + help = + "Draws a line segment between cuboid selection corners.\n" + + "Can only be used with cuboid selections.\n" + + "Flags:\n" + + " -h generates only a shell", + flags = "h", + min = 1, + max = 2 + ) + @CommandPermissions("worldedit.region.line") + @Logging(REGION) + public void line(Player player, EditSession editSession, + @Selection Region region, + Pattern pattern, + @Optional("0") @Range(min = 0) int thickness, + @Switch('h') boolean shell) throws WorldEditException { + + if (!(region instanceof CuboidRegion)) { + player.printError("//line only works with cuboid selections"); + return; + } + + CuboidRegion cuboidregion = (CuboidRegion) region; + Vector pos1 = cuboidregion.getPos1(); + Vector pos2 = cuboidregion.getPos2(); + int blocksChanged = editSession.drawLine(Patterns.wrap(pattern), pos1, pos2, thickness, !shell); + + BBC.VISITOR_BLOCK.send(player, blocksChanged); + } + + @Command( + aliases = { "/curve" }, + usage = " [thickness]", + desc = "Draws a spline through selected points", + help = + "Draws a spline through selected points.\n" + + "Can only be used with convex polyhedral selections.\n" + + "Flags:\n" + + " -h generates only a shell", + flags = "h", + min = 1, + max = 2 + ) + @CommandPermissions("worldedit.region.curve") + @Logging(REGION) + public void curve(Player player, EditSession editSession, + @Selection Region region, + Pattern pattern, + @Optional("0") @Range(min = 0) int thickness, + @Switch('h') boolean shell) throws WorldEditException { + if (!(region instanceof ConvexPolyhedralRegion)) { + player.printError("//line only works with convex polyhedral selections"); + return; + } + + ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; + List vectors = new ArrayList(cpregion.getVertices()); + + int blocksChanged = editSession.drawSpline(Patterns.wrap(pattern), vectors, 0, 0, 0, 10, thickness, !shell); + + BBC.VISITOR_BLOCK.send(player, blocksChanged); + } + + @Command( + aliases = { "/replace", "/re", "/rep" }, + usage = "[from-block] ", + desc = "Replace all blocks in the selection with another", + flags = "f", + min = 1, + max = 2 + ) + @CommandPermissions("worldedit.region.replace") + @Logging(REGION) + public void replace(Player player, EditSession editSession, @Selection Region region, @Optional Mask from, Pattern to) throws WorldEditException { + if (from == null) { + from = new ExistingBlockMask(editSession); + } + int affected = editSession.replaceBlocks(region, from, Patterns.wrap(to)); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/overlay" }, + usage = "", + desc = "Set a block on top of blocks in the region", + min = 1, + max = 1 + ) + @CommandPermissions("worldedit.region.overlay") + @Logging(REGION) + public void overlay(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.overlayCuboidBlocks(region, Patterns.wrap(pattern)); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/center", "/middle" }, + usage = "", + desc = "Set the center block(s)", + min = 1, + max = 1 + ) + @Logging(REGION) + @CommandPermissions("worldedit.region.center") + public void center(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.center(region, Patterns.wrap(pattern)); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/naturalize" }, + usage = "", + desc = "3 layers of dirt on top then rock below", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.region.naturalize") + @Logging(REGION) + public void naturalize(Player player, EditSession editSession, @Selection Region region) throws WorldEditException { + int affected = editSession.naturalizeCuboidBlocks(region); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/walls" }, + usage = "", + desc = "Build the four sides of the selection", + min = 1, + max = 1 + ) + @CommandPermissions("worldedit.region.walls") + @Logging(REGION) + public void walls(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.makeCuboidWalls(region, Patterns.wrap(pattern)); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/faces", "/outline" }, + usage = "", + desc = "Build the walls, ceiling, and floor of a selection", + min = 1, + max = 1 + ) + @CommandPermissions("worldedit.region.faces") + @Logging(REGION) + public void faces(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.makeCuboidFaces(region, Patterns.wrap(pattern)); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/smooth" }, + usage = "[iterations]", + flags = "n", + desc = "Smooth the elevation in the selection", + help = + "Smooths the elevation in the selection.\n" + + "The -n flag makes it only consider naturally occuring blocks.", + min = 0, + max = 1 + ) + @CommandPermissions("worldedit.region.smooth") + @Logging(REGION) + public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations, @Switch('n') boolean affectNatural) throws WorldEditException { + HeightMap heightMap = new HeightMap(editSession, region, affectNatural); + HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); + int affected = heightMap.applyFilter(filter, iterations); + + BBC.VISITOR_BLOCK.send(player, affected); + + } + + @Command( + aliases = { "/move" }, + usage = "[count] [direction] [leave-id]", + flags = "s", + desc = "Move the contents of the selection", + help = + "Moves the contents of the selection.\n" + + "The -s flag shifts the selection to the target location.\n" + + "Optionally fills the old location with .", + min = 0, + max = 3 + ) + @CommandPermissions("worldedit.region.move") + @Logging(ORIENTATION_REGION) + public void move(Player player, EditSession editSession, LocalSession session, + @Selection Region region, + @Optional("1") @Range(min = 1) int count, + @Optional(Direction.AIM) @Direction Vector direction, + @Optional("air") BaseBlock replace, + @Switch('s') boolean moveSelection) throws WorldEditException { + + int affected = editSession.moveRegion(region, direction, count, true, replace); + + if (moveSelection) { + try { + region.shift(direction.multiply(count)); + + session.getRegionSelector(player.getWorld()).learnChanges(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + } + + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/stack" }, + usage = "[count] [direction]", + flags = "sa", + desc = "Repeat the contents of the selection", + help = + "Repeats the contents of the selection.\n" + + "Flags:\n" + + " -s shifts the selection to the last stacked copy\n" + + " -a skips air blocks", + min = 0, + max = 2 + ) + @CommandPermissions("worldedit.region.stack") + @Logging(ORIENTATION_REGION) + public void stack(Player player, EditSession editSession, LocalSession session, + @Selection Region region, + @Optional("1") @Range(min = 1) int count, + @Optional(Direction.AIM) @Direction Vector direction, + @Switch('s') boolean moveSelection, + @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { + int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); + + if (moveSelection) { + try { + final Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()); + + final Vector shiftVector = direction.multiply(count * (Math.abs(direction.dot(size)) + 1)); + region.shift(shiftVector); + + session.getRegionSelector(player.getWorld()).learnChanges(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + } + + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/regen" }, + usage = "", + desc = "Regenerates the contents of the selection", + help = + "Regenerates the contents of the current selection.\n" + + "This command might affect things outside the selection,\n" + + "if they are within the same chunk.", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.regen") + @Logging(REGION) + public void regenerateChunk(Player player, LocalSession session, EditSession editSession, @Selection Region region) throws WorldEditException { + Mask mask = session.getMask(); + try { + session.setMask((Mask) null); + player.getWorld().regenerate(region, editSession); + } finally { + session.setMask(mask); + } + BBC.COMMAND_REGEN.send(player); + } + + @Command( + aliases = { "/deform" }, + usage = "", + desc = "Deforms a selected region with an expression", + help = + "Deforms a selected region with an expression\n" + + "The expression is executed for each block and is expected\n" + + "to modify the variables x, y and z to point to a new block\n" + + "to fetch. See also tinyurl.com/wesyntax.", + flags = "ro", + min = 1, + max = -1 + ) + @CommandPermissions("worldedit.region.deform") + @Logging(ALL) + public void deform(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + @Text String expression, + @Switch('r') boolean useRawCoords, + @Switch('o') boolean offset) throws WorldEditException { + final Vector zero; + Vector unit; + + if (useRawCoords) { + zero = Vector.ZERO; + unit = Vector.ONE; + } else if (offset) { + zero = session.getPlacementPosition(player); + unit = Vector.ONE; + } else { + final Vector min = region.getMinimumPoint(); + final Vector max = region.getMaximumPoint(); + + zero = max.add(min).multiply(0.5); + unit = max.subtract(zero); + + if (unit.getX() == 0) unit = unit.setX(1.0); + if (unit.getY() == 0) unit = unit.setY(1.0); + if (unit.getZ() == 0) unit = unit.setZ(1.0); + } + + try { + final int affected = editSession.deformRegion(region, zero, unit, expression); + player.findFreePosition(); + BBC.VISITOR_BLOCK.send(player, affected); + } catch (ExpressionException e) { + player.printError(e.getMessage()); + } + } + + @Command( + aliases = { "/hollow" }, + usage = "[[ ]]", + desc = "Hollows out the object contained in this selection", + help = + "Hollows out the object contained in this selection.\n" + + "Optionally fills the hollowed out part with the given block.\n" + + "Thickness is measured in manhattan distance.", + min = 0, + max = 2 + ) + @CommandPermissions("worldedit.region.hollow") + @Logging(REGION) + public void hollow(Player player, EditSession editSession, + @Selection Region region, + @Optional("0") @Range(min = 0) int thickness, + @Optional("air") Pattern pattern) throws WorldEditException { + + int affected = editSession.hollowOutRegion(region, thickness, Patterns.wrap(pattern)); + BBC.VISITOR_BLOCK.send(player, affected); + } + + @Command( + aliases = { "/forest" }, + usage = "[type] [density]", + desc = "Make a forest within the region", + min = 0, + max = 2 + ) + @CommandPermissions("worldedit.region.forest") + @Logging(REGION) + public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, + @Optional("5") @Range(min = 0, max = 100) double density) throws WorldEditException { + density = density / 100; + ForestGenerator generator = new ForestGenerator(editSession, new TreeGenerator(type)); + GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); + LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); + visitor.setMask(new NoiseFilter2D(new RandomNoise(), density)); + Operations.completeLegacy(visitor); + + BBC.COMMAND_TREE.send(player, ground.getAffected()); + } + + @Command( + aliases = { "/flora" }, + usage = "[density]", + desc = "Make flora within the region", + min = 0, + max = 1 + ) + @CommandPermissions("worldedit.region.flora") + @Logging(REGION) + public void flora(Player player, EditSession editSession, @Selection Region region, @Optional("10") @Range(min = 0, max = 100) double density) throws WorldEditException { + density = density / 100; + FloraGenerator generator = new FloraGenerator(editSession); + GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); + LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); + visitor.setMask(new NoiseFilter2D(new RandomNoise(), density)); + Operations.completeLegacy(visitor); + + BBC.COMMAND_FLORA.send(player, ground.getAffected()); + } + + public static Class inject() { + return RegionCommands.class; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index ad643f47..2b168261 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.sk89q.minecraft.util.commands.Command; @@ -119,7 +120,7 @@ public class SchematicCommands { } session.setClipboard(new ClipboardHolder(clipboard, worldData)); log.info(player.getName() + " loaded " + filePath); - player.print(filename + " loaded. Paste it with //paste"); + BBC.SCHEMATIC_LOADED.send(player, filename); } } catch (final IOException e) { player.printError("Schematic could not read or it does not exist: " + e.getMessage()); @@ -183,7 +184,7 @@ public class SchematicCommands { final ClipboardWriter writer = closer.register(format.getWriter(bos)); writer.write(target, holder.getWorldData()); log.info(player.getName() + " saved " + f.getCanonicalPath()); - player.print(filename + " saved."); + BBC.SCHEMATIC_SAVED.send(player, filename); } catch (final IOException e) { player.printError("Schematic could not written: " + e.getMessage()); log.log(Level.WARNING, "Failed to write a saved clipboard", e); @@ -219,7 +220,7 @@ public class SchematicCommands { return; } - player.print(filename + " has been deleted."); + BBC.SCHEMATIC_DELETE.send(player, filename); } }); } @@ -227,7 +228,7 @@ public class SchematicCommands { @Command(aliases = { "formats", "listformats", "f" }, desc = "List available formats", max = 0) @CommandPermissions("worldedit.schematic.formats") public void formats(final Actor actor) throws WorldEditException { - actor.print("Available clipboard formats (Name: Lookup names)"); + BBC.SCHEMATIC_FORMAT.send(actor); StringBuilder builder; boolean first = true; for (final ClipboardFormat format : ClipboardFormat.values()) { @@ -284,8 +285,7 @@ public class SchematicCommands { return result; } }); - - actor.print("Available schematics (Filename (Format)):"); + BBC.SCHEMATIC_LIST.send(actor); actor.print(this.listFiles("", files)); } diff --git a/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java new file mode 100644 index 00000000..f1127676 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -0,0 +1,795 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.command; + +import com.boydti.fawe.config.BBC; +import com.google.common.base.Optional; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.minecraft.util.commands.Logging; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.selector.ConvexPolyhedralRegionSelector; +import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.regions.selector.CylinderRegionSelector; +import com.sk89q.worldedit.regions.selector.EllipsoidRegionSelector; +import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector; +import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector; +import com.sk89q.worldedit.regions.selector.RegionSelectorType; +import com.sk89q.worldedit.regions.selector.SphereRegionSelector; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.Countable; +import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; +import com.sk89q.worldedit.util.formatting.Style; +import com.sk89q.worldedit.util.formatting.StyledFragment; +import com.sk89q.worldedit.util.formatting.component.CommandListBox; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.storage.ChunkStore; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; + +/** + * Selection commands. + */ +public class SelectionCommands { + + private final WorldEdit we; + + public SelectionCommands(WorldEdit we) { + this.we = we; + } + + @Command( + aliases = { "/pos1" }, + usage = "[coordinates]", + desc = "Set position 1", + min = 0, + max = 1 + ) + @Logging(POSITION) + @CommandPermissions("worldedit.selection.pos") + public void pos1(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + Vector pos; + + if (args.argsLength() == 1) { + if (args.getString(0).matches("-?\\d+,-?\\d+,-?\\d+")) { + String[] coords = args.getString(0).split(","); + pos = new Vector(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]), Integer.parseInt(coords[2])); + } else { + player.printError("Invalid coordinates " + args.getString(0)); + return; + } + } else { + pos = player.getBlockIn(); + } + + if (!session.getRegionSelector(player.getWorld()).selectPrimary(pos, ActorSelectorLimits.forActor(player))) { + player.printError("Position already set."); + return; + } + + session.getRegionSelector(player.getWorld()) + .explainPrimarySelection(player, session, pos); + } + + @Command( + aliases = { "/pos2" }, + usage = "[coordinates]", + desc = "Set position 2", + min = 0, + max = 1 + ) + @Logging(POSITION) + @CommandPermissions("worldedit.selection.pos") + public void pos2(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + Vector pos; + if (args.argsLength() == 1) { + if (args.getString(0).matches("-?\\d+,-?\\d+,-?\\d+")) { + String[] coords = args.getString(0).split(","); + pos = new Vector(Integer.parseInt(coords[0]), + Integer.parseInt(coords[1]), + Integer.parseInt(coords[2])); + } else { + player.printError("Invalid coordinates " + args.getString(0)); + return; + } + } else { + pos = player.getBlockIn(); + } + + if (!session.getRegionSelector(player.getWorld()).selectSecondary(pos, ActorSelectorLimits.forActor(player))) { + player.printError("Position already set."); + return; + } + + session.getRegionSelector(player.getWorld()) + .explainSecondarySelection(player, session, pos); + } + + @Command( + aliases = { "/hpos1" }, + usage = "", + desc = "Set position 1 to targeted block", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.selection.hpos") + public void hpos1(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + Vector pos = player.getBlockTrace(300); + + if (pos != null) { + if (!session.getRegionSelector(player.getWorld()).selectPrimary(pos, ActorSelectorLimits.forActor(player))) { + player.printError("Position already set."); + return; + } + + session.getRegionSelector(player.getWorld()) + .explainPrimarySelection(player, session, pos); + } else { + player.printError("No block in sight!"); + } + } + + @Command( + aliases = { "/hpos2" }, + usage = "", + desc = "Set position 2 to targeted block", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.selection.hpos") + public void hpos2(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + Vector pos = player.getBlockTrace(300); + + if (pos != null) { + if (!session.getRegionSelector(player.getWorld()).selectSecondary(pos, ActorSelectorLimits.forActor(player))) { + player.printError("Position already set."); + return; + } + + session.getRegionSelector(player.getWorld()) + .explainSecondarySelection(player, session, pos); + } else { + player.printError("No block in sight!"); + } + } + + @Command( + aliases = { "/chunk" }, + usage = "[x,z coordinates]", + flags = "sc", + desc = "Set the selection to your current chunk.", + help = + "Set the selection to the chunk you are currently in.\n" + + "With the -s flag, your current selection is expanded\n" + + "to encompass all chunks that are part of it.\n\n" + + "Specifying coordinates will use those instead of your\n"+ + "current position. Use -c to specify chunk coordinates,\n" + + "otherwise full coordinates will be implied.\n" + + "(for example, the coordinates 5,5 are the same as -c 0,0)", + min = 0, + max = 1 + ) + @Logging(POSITION) + @CommandPermissions("worldedit.selection.chunk") + public void chunk(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + final Vector min; + final Vector max; + final World world = player.getWorld(); + if (args.hasFlag('s')) { + Region region = session.getSelection(world); + + final Vector2D min2D = ChunkStore.toChunk(region.getMinimumPoint()); + final Vector2D max2D = ChunkStore.toChunk(region.getMaximumPoint()); + + min = new Vector(min2D.getBlockX() * 16, 0, min2D.getBlockZ() * 16); + max = new Vector(max2D.getBlockX() * 16 + 15, world.getMaxY(), max2D.getBlockZ() * 16 + 15); + + BBC.SELECTION_CHUNKS.send(player, min2D.getBlockX() + ", " + min2D.getBlockZ(), max2D.getBlockX() + ", " + max2D.getBlockZ()); + } else { + final Vector2D min2D; + if (args.argsLength() == 1) { + // coords specified + String[] coords = args.getString(0).split(","); + if (coords.length != 2) { + throw new InsufficientArgumentsException("Invalid coordinates specified."); + } + int x = Integer.parseInt(coords[0]); + int z = Integer.parseInt(coords[1]); + Vector2D pos = new Vector2D(x, z); + min2D = (args.hasFlag('c')) ? pos : ChunkStore.toChunk(pos.toVector()); + } else { + // use player loc + min2D = ChunkStore.toChunk(player.getBlockIn()); + } + + min = new Vector(min2D.getBlockX() * 16, 0, min2D.getBlockZ() * 16); + max = min.add(15, world.getMaxY(), 15); + + BBC.SELECTION_CHUNK.send(player, min2D.getBlockX() + ", " + min2D.getBlockZ()); + } + + final CuboidRegionSelector selector; + if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) { + selector = new ExtendingCuboidRegionSelector(world); + } else { + selector = new CuboidRegionSelector(world); + } + selector.selectPrimary(min, ActorSelectorLimits.forActor(player)); + selector.selectSecondary(max, ActorSelectorLimits.forActor(player)); + session.setRegionSelector(world, selector); + + session.dispatchCUISelection(player); + + } + + @Command( + aliases = { "/wand" }, + usage = "", + desc = "Get the wand object", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.wand") + public void wand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + player.giveItem(we.getConfiguration().wandItem, 1); + BBC.SELECTION_WAND.send(player); + } + + @Command( + aliases = { "toggleeditwand" }, + usage = "", + desc = "Toggle functionality of the edit wand", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.wand.toggle") + public void toggleWand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + session.setToolControl(!session.isToolControlEnabled()); + + if (session.isToolControlEnabled()) { + BBC.SELECTION_WAND_ENABLE.send(player); + } else { + BBC.SELECTION_WAND_DISABLE.send(player); + } + } + + @Command( + aliases = { "/expand" }, + usage = " [reverse-amount] ", + desc = "Expand the selection area", + min = 1, + max = 3 + ) + @Logging(REGION) + @CommandPermissions("worldedit.selection.expand") + public void expand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + // Special syntax (//expand vert) to expand the selection between + // sky and bedrock. + if (args.getString(0).equalsIgnoreCase("vert") + || args.getString(0).equalsIgnoreCase("vertical")) { + Region region = session.getSelection(player.getWorld()); + try { + int oldSize = region.getArea(); + region.expand( + new Vector(0, (player.getWorld().getMaxY() + 1), 0), + new Vector(0, -(player.getWorld().getMaxY() + 1), 0)); + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_EXPAND_VERT.send(player, (newSize - oldSize)); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + + return; + } + + List dirs = new ArrayList(); + int change = args.getInteger(0); + int reverseChange = 0; + + switch (args.argsLength()) { + case 2: + // Either a reverse amount or a direction + try { + reverseChange = args.getInteger(1); + dirs.add(we.getDirection(player, "me")); + } catch (NumberFormatException e) { + if (args.getString(1).contains(",")) { + String[] split = args.getString(1).split(","); + for (String s : split) { + dirs.add(we.getDirection(player, s.toLowerCase())); + } + } else { + dirs.add(we.getDirection(player, args.getString(1).toLowerCase())); + } + } + break; + + case 3: + // Both reverse amount and direction + reverseChange = args.getInteger(1); + if (args.getString(2).contains(",")) { + String[] split = args.getString(2).split(","); + for (String s : split) { + dirs.add(we.getDirection(player, s.toLowerCase())); + } + } else { + dirs.add(we.getDirection(player, args.getString(2).toLowerCase())); + } + break; + + default: + dirs.add(we.getDirection(player, "me")); + break; + + } + + Region region = session.getSelection(player.getWorld()); + int oldSize = region.getArea(); + + if (reverseChange == 0) { + for (Vector dir : dirs) { + region.expand(dir.multiply(change)); + } + } else { + for (Vector dir : dirs) { + region.expand(dir.multiply(change), dir.multiply(-reverseChange)); + } + } + + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_EXPAND.send(player, (newSize - oldSize)); + } + + @Command( + aliases = { "/contract" }, + usage = " [reverse-amount] [direction]", + desc = "Contract the selection area", + min = 1, + max = 3 + ) + @Logging(REGION) + @CommandPermissions("worldedit.selection.contract") + public void contract(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + List dirs = new ArrayList(); + int change = args.getInteger(0); + int reverseChange = 0; + + switch (args.argsLength()) { + case 2: + // Either a reverse amount or a direction + try { + reverseChange = args.getInteger(1); + dirs.add(we.getDirection(player, "me")); + } catch (NumberFormatException e) { + if (args.getString(1).contains(",")) { + String[] split = args.getString(1).split(","); + for (String s : split) { + dirs.add(we.getDirection(player, s.toLowerCase())); + } + } else { + dirs.add(we.getDirection(player, args.getString(1).toLowerCase())); + } + } + break; + + case 3: + // Both reverse amount and direction + reverseChange = args.getInteger(1); + if (args.getString(2).contains(",")) { + String[] split = args.getString(2).split(","); + for (String s : split) { + dirs.add(we.getDirection(player, s.toLowerCase())); + } + } else { + dirs.add(we.getDirection(player, args.getString(2).toLowerCase())); + } + break; + + default: + dirs.add(we.getDirection(player, "me")); + break; + } + + try { + Region region = session.getSelection(player.getWorld()); + int oldSize = region.getArea(); + if (reverseChange == 0) { + for (Vector dir : dirs) { + region.contract(dir.multiply(change)); + } + } else { + for (Vector dir : dirs) { + region.contract(dir.multiply(change), dir.multiply(-reverseChange)); + } + } + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + + BBC.SELECTION_CONTRACT.send(player, (oldSize - newSize)); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + } + + @Command( + aliases = { "/shift" }, + usage = " [direction]", + desc = "Shift the selection area", + min = 1, + max = 2 + ) + @Logging(REGION) + @CommandPermissions("worldedit.selection.shift") + public void shift(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + List dirs = new ArrayList(); + int change = args.getInteger(0); + if (args.argsLength() == 2) { + if (args.getString(1).contains(",")) { + for (String s : args.getString(1).split(",")) { + dirs.add(we.getDirection(player, s.toLowerCase())); + } + } else { + dirs.add(we.getDirection(player, args.getString(1).toLowerCase())); + } + } else { + dirs.add(we.getDirection(player, "me")); + } + + try { + Region region = session.getSelection(player.getWorld()); + + for (Vector dir : dirs) { + region.shift(dir.multiply(change)); + } + + session.getRegionSelector(player.getWorld()).learnChanges(); + + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_SHIFT.send(player); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + } + + @Command( + aliases = { "/outset" }, + usage = "", + desc = "Outset the selection area", + help = + "Expands the selection by the given amount in all directions.\n" + + "Flags:\n" + + " -h only expand horizontally\n" + + " -v only expand vertically\n", + flags = "hv", + min = 1, + max = 1 + ) + @Logging(REGION) + @CommandPermissions("worldedit.selection.outset") + public void outset(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + Region region = session.getSelection(player.getWorld()); + region.expand(getChangesForEachDir(args)); + session.getRegionSelector(player.getWorld()).learnChanges(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_OUTSET.send(player); + } + + @Command( + aliases = { "/inset" }, + usage = "", + desc = "Inset the selection area", + help = + "Contracts the selection by the given amount in all directions.\n" + + "Flags:\n" + + " -h only contract horizontally\n" + + " -v only contract vertically\n", + flags = "hv", + min = 1, + max = 1 + ) + @Logging(REGION) + @CommandPermissions("worldedit.selection.inset") + public void inset(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + Region region = session.getSelection(player.getWorld()); + region.contract(getChangesForEachDir(args)); + session.getRegionSelector(player.getWorld()).learnChanges(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_INSET.send(player); + } + + private Vector[] getChangesForEachDir(CommandContext args) { + List changes = new ArrayList(6); + int change = args.getInteger(0); + + if (!args.hasFlag('h')) { + changes.add((new Vector(0, 1, 0)).multiply(change)); + changes.add((new Vector(0, -1, 0)).multiply(change)); + } + + if (!args.hasFlag('v')) { + changes.add((new Vector(1, 0, 0)).multiply(change)); + changes.add((new Vector(-1, 0, 0)).multiply(change)); + changes.add((new Vector(0, 0, 1)).multiply(change)); + changes.add((new Vector(0, 0, -1)).multiply(change)); + } + + return changes.toArray(new Vector[0]); + } + + @Command( + aliases = { "/size" }, + flags = "c", + usage = "", + desc = "Get information about the selection", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.selection.size") + public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + if (args.hasFlag('c')) { + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + Region region = clipboard.getRegion(); + Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()); + Vector origin = clipboard.getOrigin(); + + player.print("Cuboid dimensions (max - min): " + size); + player.print("Offset: " + origin); + player.print("Cuboid distance: " + size.distance(Vector.ONE)); + player.print("# of blocks: " + (int) (size.getX() * size.getY() * size.getZ())); + return; + } + + Region region = session.getSelection(player.getWorld()); + Vector size = region.getMaximumPoint() + .subtract(region.getMinimumPoint()) + .add(1, 1, 1); + + player.print("Type: " + session.getRegionSelector(player.getWorld()) + .getTypeName()); + + for (String line : session.getRegionSelector(player.getWorld()) + .getInformationLines()) { + player.print(line); + } + + player.print("Size: " + size); + player.print("Cuboid distance: " + region.getMaximumPoint().distance(region.getMinimumPoint())); + player.print("# of blocks: " + region.getArea()); + } + + + @Command( + aliases = { "/count" }, + usage = "", + desc = "Counts the number of a certain type of block", + flags = "d", + min = 1, + max = 1 + ) + @CommandPermissions("worldedit.analysis.count") + public void count(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + + boolean useData = args.hasFlag('d'); + if (args.getString(0).contains(":")) { + useData = true; //override d flag, if they specified data they want it + } + int count; + if (useData) { + Set searchBlocks = we.getBlocks(player, args.getString(0), true); + count = editSession.countBlocks(session.getSelection(player.getWorld()), searchBlocks); + } else { + Set searchIDs = we.getBlockIDs(player, args.getString(0), true); + count = editSession.countBlock(session.getSelection(player.getWorld()), searchIDs); + } + BBC.SELECTION_COUNT.send(player, count); + } + + @Command( + aliases = { "/distr" }, + usage = "", + desc = "Get the distribution of blocks in the selection", + help = + "Gets the distribution of blocks in the selection.\n" + + "The -c flag gets the distribution of your clipboard.\n" + + "The -d flag separates blocks by data", + flags = "cd", + min = 0, + max = 0 + ) + @CommandPermissions("worldedit.analysis.distr") + public void distr(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException, CommandException { + + int size; + boolean useData = args.hasFlag('d'); + List> distribution = null; + List> distributionData = null; + + if (args.hasFlag('c')) { + // TODO: Update for new clipboard + throw new CommandException("Needs to be re-written again"); + } else { + if (useData) { + distributionData = editSession.getBlockDistributionWithData(session.getSelection(player.getWorld())); + } else { + distribution = editSession.getBlockDistribution(session.getSelection(player.getWorld())); + } + size = session.getSelection(player.getWorld()).getArea(); + } + + if ((useData && distributionData.size() <= 0) + || (!useData && distribution.size() <= 0)) { // *Should* always be false + player.printError("No blocks counted."); + return; + } + BBC.SELECTION_DISTR.send(player, size); + + if (useData) { + for (Countable c : distributionData) { + String name = BlockType.fromID(c.getID().getId()).getName(); + String str = String.format("%-7s (%.3f%%) %s #%d:%d", + String.valueOf(c.getAmount()), + c.getAmount() / (double) size * 100, + name == null ? "Unknown" : name, + c.getID().getType(), c.getID().getData()); + player.print(str); + } + } else { + for (Countable c : distribution) { + BlockType block = BlockType.fromID(c.getID()); + String str = String.format("%-7s (%.3f%%) %s #%d", + String.valueOf(c.getAmount()), + c.getAmount() / (double) size * 100, + block == null ? "Unknown" : block.getName(), c.getID()); + player.print(str); + } + } + } + + @Command( + aliases = { "/sel", ";", "/desel", "/deselect" }, + flags = "d", + usage = "[cuboid|extend|poly|ellipsoid|sphere|cyl|convex]", + desc = "Choose a region selector", + min = 0, + max = 1 + ) + public void select(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + final World world = player.getWorld(); + if (args.argsLength() == 0) { + session.getRegionSelector(world).clear(); + session.dispatchCUISelection(player); + BBC.SELECTION_CLEARED.send(player); + return; + } + + final String typeName = args.getString(0); + final RegionSelector oldSelector = session.getRegionSelector(world); + + final RegionSelector selector; + if (typeName.equalsIgnoreCase("cuboid")) { + selector = new CuboidRegionSelector(oldSelector); + player.print("Cuboid: left click for point 1, right click for point 2"); + } else if (typeName.equalsIgnoreCase("extend")) { + selector = new ExtendingCuboidRegionSelector(oldSelector); + player.print("Cuboid: left click for a starting point, right click to extend"); + } else if (typeName.equalsIgnoreCase("poly")) { + selector = new Polygonal2DRegionSelector(oldSelector); + player.print("2D polygon selector: Left/right click to add a point."); + Optional limit = ActorSelectorLimits.forActor(player).getPolygonVertexLimit(); + if (limit.isPresent()) { + player.print(limit.get() + " points maximum."); + } + } else if (typeName.equalsIgnoreCase("ellipsoid")) { + selector = new EllipsoidRegionSelector(oldSelector); + player.print("Ellipsoid selector: left click=center, right click to extend"); + } else if (typeName.equalsIgnoreCase("sphere")) { + selector = new SphereRegionSelector(oldSelector); + player.print("Sphere selector: left click=center, right click to set radius"); + } else if (typeName.equalsIgnoreCase("cyl")) { + selector = new CylinderRegionSelector(oldSelector); + player.print("Cylindrical selector: Left click=center, right click to extend."); + } else if (typeName.equalsIgnoreCase("convex") || typeName.equalsIgnoreCase("hull") || typeName.equalsIgnoreCase("polyhedron")) { + selector = new ConvexPolyhedralRegionSelector(oldSelector); + player.print("Convex polyhedral selector: Left click=First vertex, right click to add more."); + Optional limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit(); + if (limit.isPresent()) { + player.print(limit.get() + " points maximum."); + } + } else { + CommandListBox box = new CommandListBox("Selection modes"); + StyledFragment contents = box.getContents(); + StyledFragment tip = contents.createFragment(Style.RED); + tip.append("Select one of the modes below:").newLine(); + + box.appendCommand("cuboid", "Select two corners of a cuboid"); + box.appendCommand("extend", "Fast cuboid selection mode"); + box.appendCommand("poly", "Select a 2D polygon with height"); + box.appendCommand("ellipsoid", "Select an ellipsoid"); + box.appendCommand("sphere", "Select a sphere"); + box.appendCommand("cyl", "Select a cylinder"); + box.appendCommand("convex", "Select a convex polyhedral"); + + player.printRaw(ColorCodeBuilder.asColorCodes(box)); + return; + } + + if (args.hasFlag('d')) { + RegionSelectorType found = null; + for (RegionSelectorType type : RegionSelectorType.values()) { + if (type.getSelectorClass() == selector.getClass()) { + found = type; + break; + } + } + + if (found != null) { + session.setDefaultRegionSelector(found); + player.print("Your default region selector is now " + found.name() + "."); + } else { + throw new RuntimeException("Something unexpected happened. Please report this."); + } + } + + session.setRegionSelector(world, selector); + session.dispatchCUISelection(player); + } + + public static Class inject() { + return SelectionCommands.class; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java b/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java index ee6430ed..200ef2fa 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java +++ b/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command.composition; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.NullChangeSet; @@ -138,15 +139,24 @@ public class SelectionCommand extends SimpleCommand { newChunk = fc.copy(true); newChunk.setLoc(queue, value[0], value[1]); } else { - newChunk = queue.getChunk(value[0], value[1]); - newChunk.fillCuboid(value[2] & 15, value[4] & 15, minY, maxY, value[3] & 15, value[5] & 15, id, data); + int bx = value[2] & 15; + int tx = value[4] & 15; + int bz = value[3] & 15; + int tz = value[4] & 15; + if (bx == 0 && tx == 15 && bz == 0 && tz == 15) { + newChunk = fc.copy(true); + newChunk.setLoc(queue, value[0], value[1]); + } else { + newChunk = queue.getChunk(value[0], value[1]); + newChunk.fillCuboid(value[2] & 15, value[4] & 15, minY, maxY, value[3] & 15, value[5] & 15, id, data); + } } newChunk.addToQueue(); } }); queue.enqueue(); editSession.setChangeSet(new NullChangeSet()); - actor.print("[FAWE] Finished queueing " + cuboid.getArea() + " blocks."); + BBC.OPERATION.send(actor, BBC.VISITOR_BLOCK.format(cuboid.getArea())); return null; } } catch (Throwable e) { @@ -159,9 +169,9 @@ public class SelectionCommand extends SimpleCommand { List messages = Lists.newArrayList(); operation.addStatusMessages(messages); if (messages.isEmpty()) { - actor.print("Operation completed."); + BBC.OPERATION.send(actor, 0); } else { - actor.print("Operation completed (" + Joiner.on(", ").join(messages) + ")."); + BBC.OPERATION.send(actor, Joiner.on(", ").join(messages)); } return operation; diff --git a/core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java b/core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java index 00815fc6..3fad1d53 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java +++ b/core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java @@ -23,7 +23,6 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.function.pattern.Pattern; public class GravityBrush implements Brush { @@ -37,7 +36,6 @@ public class GravityBrush implements Brush { @Override public void build(EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { int size = (int) sizeDouble; - BaseBlock air = new BaseBlock(BlockID.AIR, 0); int endY = position.getBlockY() + size; int startPerformY = Math.max(0, position.getBlockY() - size); int startCheckY = fullHeight ? 0 : startPerformY; diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 28687f71..6516765a 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -268,7 +268,7 @@ public final class CommandManager { } catch (WrappedCommandException e) { FaweException faweException = FaweException.get(e); if (faweException != null) { - actor.printError(BBC.PREFIX.s() + " " + BBC.WORLDEDIT_CANCEL_REASON.format(faweException.getMessage())); + BBC.WORLDEDIT_CANCEL_REASON.send(actor, faweException.getMessage()); } else { Throwable t = e.getCause(); actor.printError("Please report this error: [See console]"); @@ -298,7 +298,7 @@ public final class CommandManager { @Override public void run() { final long time = System.currentTimeMillis() - start; - actor.print(BBC.PREFIX.s() + " Action completed in " + (time / 1000d) + " seconds"); + BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); } }); } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 3050d5a9..7b74a033 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -409,7 +409,7 @@ public class PlatformManager { } catch (Throwable e) { FaweException faweException = FaweException.get(e); if (faweException != null) { - actor.printError(BBC.PREFIX.s() + " " + BBC.WORLDEDIT_CANCEL_REASON.format(faweException.getMessage())); + BBC.WORLDEDIT_CANCEL_REASON.send(actor, faweException.getMessage()); } else { actor.printError("Please report this error: [See console]"); actor.printRaw(e.getClass().getName() + ": " + e.getMessage()); @@ -496,7 +496,7 @@ public class PlatformManager { } catch (Throwable e) { FaweException faweException = FaweException.get(e); if (faweException != null) { - player.printError(BBC.PREFIX.s() + " " + BBC.WORLDEDIT_CANCEL_REASON.format(faweException.getMessage())); + BBC.WORLDEDIT_CANCEL_REASON.send(player, faweException.getMessage()); } else { player.printError("Please report this error: [See console]"); player.printRaw(e.getClass().getName() + ": " + e.getMessage()); diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index f0b34e76..96950fee 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.function.visitor; +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; @@ -186,6 +187,6 @@ public abstract class BreadthFirstSearch implements Operation { @Override public void addStatusMessages(final List messages) { - messages.add(this.getAffected() + " blocks affected"); + messages.add(BBC.VISITOR_BLOCK.format(getAffected())); } } diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java index b75a8f8e..ebe45ed0 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.function.visitor; +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.function.EntityFunction; @@ -78,7 +79,7 @@ public class EntityVisitor implements Operation { @Override public void addStatusMessages(final List messages) { - messages.add(this.getAffected() + " blocks affected"); + messages.add(BBC.VISITOR_ENTITY.format(getAffected())); } public static Class inject() { diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/FlatRegionVisitor.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/FlatRegionVisitor.java index 2e7d6d84..f55b98d4 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/FlatRegionVisitor.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/FlatRegionVisitor.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.function.visitor; +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.FlatRegionFunction; @@ -77,7 +78,7 @@ public class FlatRegionVisitor implements Operation { @Override public void addStatusMessages(final List messages) { - messages.add(this.getAffected() + " columns affected"); + messages.add(BBC.VISITOR_FLAT.format(getAffected())); } public static Class inject() { diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java index 494fcdc5..b25b4e63 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.function.visitor; +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.RegionFunction; @@ -66,7 +67,7 @@ public class RegionVisitor implements Operation { @Override public void addStatusMessages(final List messages) { - messages.add(this.getAffected() + " blocks affected"); + messages.add(BBC.VISITOR_BLOCK.format(getAffected())); } public static Class inject() { diff --git a/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java b/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java new file mode 100644 index 00000000..502026a5 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java @@ -0,0 +1,301 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.regions.selector; + +import com.boydti.fawe.config.BBC; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.internal.cui.CUIRegion; +import com.sk89q.worldedit.internal.cui.SelectionPointEvent; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; +import com.sk89q.worldedit.world.World; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Creates a {@code CuboidRegion} from a user's selections. + */ +public class CuboidRegionSelector extends com.sk89q.worldedit.regions.CuboidRegionSelector implements RegionSelector, CUIRegion { + + public transient BlockVector position1; + public transient BlockVector position2; + public transient CuboidRegion region; + + /** + * Create a new region selector with a {@code null} world. + */ + public CuboidRegionSelector() { + this((World) null); + } + + /** + * Create a new region selector. + * + * @param world the world, which may be {@code null} + */ + public CuboidRegionSelector(@Nullable World world) { + region = new CuboidRegion(world, new Vector(), new Vector()); + } + + /** + * Create a copy of another selector. + * + * @param oldSelector another selector + */ + public CuboidRegionSelector(RegionSelector oldSelector) { + this(checkNotNull(oldSelector).getIncompleteRegion().getWorld()); + + if (oldSelector instanceof CuboidRegionSelector) { + final CuboidRegionSelector cuboidRegionSelector = (CuboidRegionSelector) oldSelector; + + position1 = cuboidRegionSelector.position1; + position2 = cuboidRegionSelector.position2; + } else { + final Region oldRegion; + try { + oldRegion = oldSelector.getRegion(); + } catch (IncompleteRegionException e) { + return; + } + + position1 = oldRegion.getMinimumPoint().toBlockVector(); + position2 = oldRegion.getMaximumPoint().toBlockVector(); + } + + region.setPos1(position1); + region.setPos2(position2); + } + + /** + * Create a new region selector with the given two positions. + * + * @param world the world + * @param position1 position 1 + * @param position2 position 2 + */ + public CuboidRegionSelector(@Nullable World world, Vector position1, Vector position2) { + this(world); + checkNotNull(position1); + checkNotNull(position2); + this.position1 = position1.toBlockVector(); + this.position2 = position2.toBlockVector(); + region.setPos1(position1); + region.setPos2(position2); + } + + @Nullable + @Override + public World getWorld() { + return region.getWorld(); + } + + @Override + public void setWorld(@Nullable World world) { + region.setWorld(world); + } + + @Override + public boolean selectPrimary(Vector position, SelectorLimits limits) { + checkNotNull(position); + + if (position1 != null && (position.compareTo(position1) == 0)) { + return false; + } + + position1 = position.toBlockVector(); + region.setPos1(position1); + return true; + } + + @Override + public boolean selectSecondary(Vector position, SelectorLimits limits) { + checkNotNull(position); + + if (position2 != null && (position.compareTo(position2)) == 0) { + return false; + } + + position2 = position.toBlockVector(); + region.setPos2(position2); + return true; + } + + @Override + public void explainPrimarySelection(Actor player, LocalSession session, Vector pos) { + checkNotNull(player); + checkNotNull(session); + checkNotNull(pos); + + if (position1 != null && position2 != null) { + BBC.SELECTOR_CUBOID_POS1.send(player, position1, "(" + region.getArea() + ")"); + } else { + BBC.SELECTOR_CUBOID_POS1.send(player, position1, ""); + } + + session.dispatchCUIEvent(player, new SelectionPointEvent(0, pos, getArea())); + } + + @Override + public void explainSecondarySelection(Actor player, LocalSession session, Vector pos) { + checkNotNull(player); + checkNotNull(session); + checkNotNull(pos); + + if (position1 != null && position2 != null) { + BBC.SELECTOR_CUBOID_POS2.send(player, position1, "(" + region.getArea() + ")"); + } else { + BBC.SELECTOR_CUBOID_POS2.send(player, position1, ""); + } + + session.dispatchCUIEvent(player, new SelectionPointEvent(1, pos, getArea())); + } + + @Override + public void explainRegionAdjust(Actor player, LocalSession session) { + checkNotNull(player); + checkNotNull(session); + + if (position1 != null) { + session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getArea())); + } + + if (position2 != null) { + session.dispatchCUIEvent(player, new SelectionPointEvent(1, position2, getArea())); + } + } + + @Override + public BlockVector getPrimaryPosition() throws IncompleteRegionException { + if (position1 == null) { + throw new IncompleteRegionException(); + } + + return position1; + } + + @Override + public boolean isDefined() { + return position1 != null && position2 != null; + } + + @Override + public CuboidRegion getRegion() throws IncompleteRegionException { + if (position1 == null || position2 == null) { + throw new IncompleteRegionException(); + } + + return region; + } + + @Override + public CuboidRegion getIncompleteRegion() { + return region; + } + + @Override + public void learnChanges() { + position1 = region.getPos1().toBlockVector(); + position2 = region.getPos2().toBlockVector(); + } + + @Override + public void clear() { + position1 = null; + position2 = null; + } + + @Override + public String getTypeName() { + return "cuboid"; + } + + @Override + public List getInformationLines() { + final List lines = new ArrayList(); + + if (position1 != null) { + lines.add("Position 1: " + position1); + } + + if (position2 != null) { + lines.add("Position 2: " + position2); + } + + return lines; + } + + @Override + public int getArea() { + if (position1 == null) { + return -1; + } + + if (position2 == null) { + return -1; + } + + return region.getArea(); + } + + @Override + public void describeCUI(LocalSession session, Actor player) { + if (position1 != null) { + session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getArea())); + } + + if (position2 != null) { + session.dispatchCUIEvent(player, new SelectionPointEvent(1, position2, getArea())); + } + } + + @Override + public void describeLegacyCUI(LocalSession session, Actor player) { + describeCUI(session, player); + } + + @Override + public int getProtocolVersion() { + return 0; + } + + @Override + public String getTypeID() { + return "cuboid"; + } + + @Override + public String getLegacyTypeID() { + return "cuboid"; + } + + public static Class inject() { + return CuboidRegionSelector.class; + } +} \ No newline at end of file diff --git a/sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java b/sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java index ac213694..293ec7cc 100644 --- a/sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java @@ -96,7 +96,7 @@ public class FaweSponge implements IFawe { @Override public void setupVault() { - debug(BBC.PREFIX.s() + "Permission hook not implemented yet!"); + debug("Permission hook not implemented yet!"); } @Override