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 25008e72..68b4a288 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -121,6 +121,8 @@ public enum BBC { BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"), BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2. Note: Use the blend brush if you want to smooth overhangs or caves.).", "WorldEdit.Brush"), BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"), + BRUSH_SHATTER("Shatter brush shape equipped (%s0, %s1).", "WorldEdit.Brush"), + BRUSH_STENCIL("Stencil brush equipped (%s0).", "WorldEdit.Brush"), BRUSH_LINE("Line brush shape equipped (%s0).", "WorldEdit.Brush"), BRUSH_SPLINE("Spline brush shape equipped (%s0). Right click an end to add a shape", "WorldEdit.Brush"), BRUSH_SPLINE_PRIMARY_2("Added position, Click the same spot to join!", "WorldEdit.Brush"), diff --git a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java index 234a3bf4..704e1fd6 100644 --- a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java @@ -572,14 +572,38 @@ public abstract class MappedFaweQueue exte @Override public int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { - FaweChunk fc = map.getCachedFaweChunk(x >> 4, z >> 4); + int cx = x >> 4; + int cz = z >> 4; + FaweChunk fc = map.getCachedFaweChunk(cx, cz); if (fc != null) { int combined = fc.getBlockCombinedId(x & 15, y, z & 15); if (combined != 0) { return combined; } } - return getCombinedId4Data(x, y, z); + int cy = y >> 4; + if (cx != lastSectionX || cz != lastSectionZ) { + lastSectionX = cx; + lastSectionZ = cz; + lastChunk = ensureChunkLoaded(cx, cz); + if (lastChunk != null) { + lastChunkSections = getSections(lastChunk); + lastSection = getCachedSection(lastChunkSections, cy); + } else { + lastChunkSections = null; + return 0; + } + } else if (cy != lastSectionY) { + if (lastChunkSections != null) { + lastSection = getCachedSection(lastChunkSections, cy); + } else { + return 0; + } + } + if (lastSection == null) { + return 0; + } + return getCombinedId4Data(lastSection, x, y, z); } @Override diff --git a/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java index a31b8583..79f1f185 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java @@ -24,6 +24,6 @@ public class FlattenBrush extends HeightBrush { mask = null; } heightMap.setSize(size); - heightMap.apply(editSession, mask, position, size, rotation, yscale, true, true); + heightMap.perform(editSession, mask, position, size, rotation, yscale, true, true); } } 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 index b0772b0b..53263f14 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java @@ -48,6 +48,6 @@ public class HeightBrush implements Brush { mask = null; } heightMap.setSize(size); - heightMap.apply(editSession, mask, position, size, rotation, yscale, true, false); + heightMap.perform(editSession, mask, position, size, rotation, yscale, true, false); } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java new file mode 100644 index 00000000..d5718bec --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java @@ -0,0 +1,135 @@ +package com.boydti.fawe.object.brush; + +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.collection.LocalBlockVectorSet; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.MutableBlockVector; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; +import com.sk89q.worldedit.regions.CuboidRegion; + +public class ShatterBrush implements Brush { + private final int count; + + public ShatterBrush(int count) { + this.count = count; + } + + @Override + public void build(EditSession editSession, final Vector position, Pattern pattern, double size) throws MaxChangedBlocksException { + // We'll want this to be somewhat circular, so the cuboid needs to fit. + int r2Radius = (int) Math.ceil(size * Math.sqrt(2)); + int radius2 = (int) (Math.ceil(r2Radius * r2Radius)); + Vector bot = new MutableBlockVector(position.subtract(size, size, size)); + Vector top = new MutableBlockVector(position.add(size, size, size)); + CuboidRegion region = new CuboidRegion(bot, top); + // We'll want to use a fast random + PseudoRandom random = new PseudoRandom(); + // We don't need double precision, so use a BlockVector + BlockVector min = region.getMinimumPoint().toBlockVector(); + BlockVector max = region.getMaximumPoint().toBlockVector(); + // Let's keep it inside the brush radius + int dx = max.getBlockX() - min.getBlockX() + 1; + int dy = max.getBlockY() - min.getBlockY() + 1; + int dz = max.getBlockZ() - min.getBlockZ() + 1; + // We'll store the points in a set + LocalBlockVectorSet queue = new LocalBlockVectorSet(); + // User could select a single block and try to create 10 points = infinite loop + // To avoid being stuck in an infinite loop, let's stop after 5 collisions + int maxFails = 5; + for (int added = 0; added < count;) { + int x = (int) (random.nextDouble() * dx) + min.getBlockX(); + int z = (int) (random.nextDouble() * dz) + min.getBlockZ(); + int y = editSession.getHighestTerrainBlock(x, z, 0, 255); + // Check the adjacent blocks efficiently (loops over the adjacent blocks, or the set; whichever is faster) + if (!queue.containsRadius(x, y, z, 1)) { + added++; + queue.add(x, y, z); + } else if (maxFails-- <= 0) { + break; + } + } + // Ideally we'd calculate all the bisecting planes, but that's complex to program + // With this algorithm compute time depends on the number of blocks rather than the number of points + // - Expand from each point (block by block) until there is a collision + { + // Keep track of where we've visited + LocalBlockVectorSet visited = queue; + LocalBlockVectorSet tmp = new LocalBlockVectorSet(); + // Individual frontier for each point + LocalBlockVectorSet[] frontiers = new LocalBlockVectorSet[queue.size()]; + // Keep track of where each frontier has visited + LocalBlockVectorSet[] frontiersVisited = new LocalBlockVectorSet[queue.size()]; + // Initiate the frontier with the starting points + int i = 0; + for (Vector pos : queue) { + LocalBlockVectorSet set = new LocalBlockVectorSet(); + set.add(pos); + frontiers[i] = set; + frontiersVisited[i] = set.clone(); + i++; + } + // Mask + Mask mask = editSession.getMask(); + if (mask == null) { + mask = Masks.alwaysTrue(); + } + final Mask finalMask = mask; + // Expand + boolean notEmpty = true; + while (notEmpty) { + notEmpty = false; + for (i = 0; i < frontiers.length; i++) { + LocalBlockVectorSet frontier = frontiers[i]; + notEmpty |= !frontier.isEmpty(); + final LocalBlockVectorSet frontierVisited = frontiersVisited[i]; + // This is a temporary set with the next blocks the frontier will visit + final LocalBlockVectorSet finalTmp = tmp; + frontier.forEach(new LocalBlockVectorSet.BlockVectorSetVisitor() { + @Override + public void run(int x, int y, int z, int index) { + if (PseudoRandom.random.random(2) == 0) { + finalTmp.add(x, y, z); + return; + } + for (Vector direction : BreadthFirstSearch.DEFAULT_DIRECTIONS) { + int x2 = x + direction.getBlockX(); + int y2 = y + direction.getBlockY(); + int z2 = z + direction.getBlockZ(); + // Check boundary + int dx = position.getBlockX() - x2; + int dy = position.getBlockY() - y2; + int dz = position.getBlockZ() - z2; + int dSqr = (dx * dx) + (dy * dy) + (dz * dz); + if (dSqr <= radius2) { + if (finalMask.test(MutableBlockVector.get(x2, y2, z2))) { + // (collision) If it's visited and part of another frontier, set the block + if (!visited.add(x2, y2, z2)) { + if (!frontierVisited.contains(x2, y2, z2)) { + editSession.setBlock(x2, y2, z2, pattern); + } + } else { + // Hasn't visited and not a collision = add it + finalTmp.add(x2, y2, z2); + frontierVisited.add(x2, y2, z2); + } + } + } + } + } + }); + // Swap the frontier with the temporary set + frontiers[i] = tmp; + tmp = frontier; + tmp.clear(); + } + } + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java new file mode 100644 index 00000000..e96b0e84 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java @@ -0,0 +1,54 @@ +package com.boydti.fawe.object.brush; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.pattern.Pattern; +import java.io.InputStream; + +public class StencilBrush extends HeightBrush { + private final boolean onlyWhite; + + public StencilBrush(InputStream stream, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard) { + super(stream, rotation, yscale, clipboard); + this.onlyWhite = onlyWhite; + } + + @Override + public void build(EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { + int size = (int) sizeDouble; + Mask mask = editSession.getMask(); + if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) { + mask = null; + } + heightMap.setSize(size); + for (int x = -size; x <= size; x++) { + int xx = position.getBlockX() + x; + for (int z = -size; z <= size; z++) { + int zz = position.getBlockZ() + z; + double 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 *= yscale; + if (raise == 0 || (onlyWhite && raise < 255)) { + continue; + } + } + } + + + int[] data = heightMap.generateHeightData(editSession, mask, position, size, rotation, yscale, true, false); + int diameter = size * 2; + int x = position.getBlockX(); + int y = position.getBlockY(); + int z = position.getBlockZ(); + } + +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java index 460ff7f7..6b79238d 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java @@ -118,7 +118,32 @@ public class ScalableHeightMap { return new ArrayHeightMap(array); } - public void apply(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { + public void perform(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { + int[] data = generateHeightData(session, mask, pos, size, rotationMode, yscale, smooth, towards); + applyHeightMapData(data, session, mask, pos, size, rotationMode, yscale, smooth, towards); + } + + public void applyHeightMapData(int[] data, EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { + Vector top = session.getMaximumPoint(); + int maxY = top.getBlockY(); + int diameter = 2 * size + 1; + int iterations = 1; + WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, maxY, size)); + Vector max = pos.add(size, maxY, size); + Region region = new CuboidRegion(session.getWorld(), min, max); + HeightMap heightMap = new HeightMap(session, region, true); + if (smooth) { + try { + HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); + data = filter.filter(data, diameter, diameter); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + heightMap.apply(data); + } + + public int[] generateHeightData(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) { Vector top = session.getMaximumPoint(); int maxY = top.getBlockY(); int diameter = 2 * size + 1; @@ -201,19 +226,6 @@ public class ScalableHeightMap { } } } - int iterations = 1; - WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, maxY, size)); - Vector max = pos.add(size, maxY, size); - Region region = new CuboidRegion(session.getWorld(), min, max); - HeightMap heightMap = new HeightMap(session, region, true); - if (smooth) { - try { - HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); - newData = filter.filter(newData, diameter, diameter); - } catch (Throwable e) { - MainUtil.handleError(e); - } - } - heightMap.apply(newData); + return newData; } } 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 8070a9cf..d9c72654 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -35,7 +35,9 @@ import com.boydti.fawe.object.brush.LineBrush; import com.boydti.fawe.object.brush.RaiseBrush; import com.boydti.fawe.object.brush.RecurseBrush; import com.boydti.fawe.object.brush.SplineBrush; +import com.boydti.fawe.object.brush.StencilBrush; import com.boydti.fawe.object.brush.TargetMode; +import com.boydti.fawe.object.brush.ShatterBrush; import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap; import com.boydti.fawe.object.brush.scroll.ScrollClipboard; import com.boydti.fawe.object.brush.scroll.ScrollMask; @@ -79,6 +81,7 @@ import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; @@ -268,7 +271,7 @@ public class BrushCommands { File working = this.worldEdit.getWorkingDirectoryFile(config.saveDir); File dir = new File(working, (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? (player.getUniqueId().toString() + File.separator) : "") + filename); if (!dir.exists()) { - if (!filename.contains("/") && !filename.contains("\\")) { + if ((!filename.contains("/") && !filename.contains("\\")) || player.hasPermission("worldedit.schematic.load.other")) { dir = new File(this.worldEdit.getWorkingDirectoryFile(config.saveDir), filename); } } @@ -507,27 +510,52 @@ public class BrushCommands { if (!FawePlayer.wrap(player).hasPermission("fawe.tips")) BBC.TIP_BRUSH_COMMAND.or(BBC.TIP_BRUSH_RELATIVE, BBC.TIP_BRUSH_TRANSFORM, BBC.TIP_BRUSH_MASK_SOURCE, BBC.TIP_BRUSH_MASK, BBC.TIP_BRUSH_COPY, BBC.TIP_BRUSH_HEIGHT, BBC.TIP_BRUSH_SPLINE).send(player); } -// @Command( -// aliases = { "test" }, -// usage = " [radius] [count] [distance]", -// flags = "h", -// desc = "Choose the sphere brush", -// help = -// "Chooses the sphere brush.\n" + -// "The -h flag creates hollow spheres instead.", -// min = 1, -// max = -1 -// ) -// @CommandPermissions("worldedit.brush.test") -// public void testBrush(Player player, LocalSession session, Pattern fill, @Optional("10") double radius, @Optional("10") int count, @Optional("10") int distance) throws WorldEditException { -// worldEdit.checkMaxBrushRadius(radius); -// -// BrushTool tool = session.getBrushTool(player); -// tool.setFill(fill); -// tool.setSize(radius); -// tool.setBrush(new Test(count), "worldedit.brush.test"); -// player.print("equiped"); -// } + @Command( + aliases = { "shatter", "partition", "split" }, + usage = " [radius] [count] [distance]", + desc = "Creates random lines to break the terrain into pieces", + help = + "Chooses the shatter brush", + min = 1, + max = -1 + ) + @CommandPermissions("worldedit.brush.shatter") + public void shatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("10") double radius, @Optional("10") int count) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + + BrushTool tool = session.getBrushTool(player); + tool.setFill(fill); + tool.setSize(radius); + tool.setMask(new ExistingBlockMask(editSession)); + tool.setBrush(new ShatterBrush(count), "worldedit.brush.shatter"); + player.print(BBC.getPrefix() + BBC.BRUSH_SHATTER.f(radius, count)); + } + + @Command( + aliases = { "stencil", "color"}, + usage = " [radius] [file|#clipboard|null] [rotation] [yscale]", + desc = "Use a height map to paint a surface", + help = + "Chooses the stencil brush.\n" + + "The -w flag will only apply at maximum saturation", + min = 1, + max = -1 + ) + @CommandPermissions("worldedit.brush.stencil") + public void stencilBrush(Player player, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + BrushTool tool = session.getBrushTool(player); + InputStream stream = getHeightmapStream(filename); + tool.setFill(fill); + tool.setSize(radius); + try { + tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player); + } catch (EmptyClipboardException ignore) { + tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, null), "worldedit.brush.height", player); + } + + player.print(BBC.getPrefix() + BBC.BRUSH_STENCIL.f(radius)); + } @Command( aliases = { "cylinder", "cyl", "c" }, @@ -701,11 +729,9 @@ public class BrushCommands { terrainBrush(player, session, radius, filename, rotation, yscale, true, ScalableHeightMap.Shape.CONE); } - private void terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, ScalableHeightMap.Shape shape) throws WorldEditException { - worldEdit.checkMaxBrushRadius(radius); + private InputStream getHeightmapStream(String filename) { String filenamePng = (filename.endsWith(".png") ? filename : filename + ".png"); File file = new File(Fawe.imp().getDirectory(), "heightmap" + File.separator + filenamePng); - InputStream stream = null; if (!file.exists()) { if (!filename.equals("#clipboard") && filename.length() >= 7) { try { @@ -719,19 +745,24 @@ public class BrushCommands { url = new URL("https://i.imgur.com/" + filenamePng); } ReadableByteChannel rbc = Channels.newChannel(url.openStream()); - stream = Channels.newInputStream(rbc); + return Channels.newInputStream(rbc); } catch (IOException e) { throw new RuntimeException(e); } } } else if (!filename.equalsIgnoreCase("#clipboard")){ try { - stream = new FileInputStream(file); + return new FileInputStream(file); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } + return null; + } + private void terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, ScalableHeightMap.Shape shape) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + InputStream stream = getHeightmapStream(filename); BrushTool tool = session.getBrushTool(player); tool.setSize(radius); if (flat) { 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 c4338d09..e3511268 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -55,6 +55,7 @@ import java.io.InputStream; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -132,7 +133,7 @@ public class SchematicCommands { } } if (!f.exists()) { - if (!filename.contains("/") && !filename.contains("\\")) { + if ((!filename.contains("/") && !filename.contains("\\")) || player.hasPermission("worldedit.schematic.load.other")) { dir = this.worldEdit.getWorkingDirectoryFile(config.saveDir); f = this.worldEdit.getSafeSaveFile(player, dir, filename, format.getExtension(), format.getExtension()); } @@ -203,8 +204,13 @@ public class SchematicCommands { final File parent = f.getParentFile(); if ((parent != null) && !parent.exists()) { if (!parent.mkdirs()) { - log.info("Could not create folder for schematics!"); - return; + try { + Files.createDirectories(parent.toPath()); + } catch (IOException e) { + e.printStackTrace(); + log.info("Could not create folder for schematics!"); + return; + } } } try { diff --git a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index 101e1065..13ae8f5e 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -18,6 +18,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.MutableBlockVector; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseBlock; @@ -232,7 +233,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool public Vector getPosition(EditSession editSession, Player player) { switch (targetMode) { case TARGET_BLOCK_RANGE: - return player.getBlockTrace(getRange(), true); + return new MutableBlockVector(player.getBlockTrace(getRange(), true)); case FOWARD_POINT_PITCH: { int d = 0; Location loc = player.getLocation(); @@ -241,7 +242,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool d += (int) (Math.sin(Math.toRadians(pitch)) * 50); final Vector vector = loc.getDirection().setY(0).normalize().multiply(d); vector.add(loc.getX(), loc.getY(), loc.getZ()).toBlockVector(); - return vector; + return new MutableBlockVector(vector); } case TARGET_POINT_HEIGHT: { Location loc = player.getLocation(); @@ -256,10 +257,10 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool } } final int distance = (height - y) + 8; - return player.getBlockTrace(distance, true); + return new MutableBlockVector(player.getBlockTrace(distance, true)); } case TARGET_FACE_RANGE: - return player.getBlockTraceFace(getRange(), true); + return new MutableBlockVector(player.getBlockTraceFace(getRange(), true)); default: return null; } 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 7fb80b1b..2bc03faa 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 @@ -119,6 +119,7 @@ public final class CommandManager { private final WorldEdit worldEdit; private final PlatformManager platformManager; private volatile Dispatcher dispatcher; + private volatile Platform platform; private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); private final ExceptionConverter exceptionConverter; @@ -246,7 +247,9 @@ public final class CommandManager { .registerMethods(new BrushCommands(worldEdit)) .parent().graph().getDispatcher(); - + if (platform != null) { + platform.registerCommands(dispatcher); + } } public static CommandManager getInstance() { @@ -281,7 +284,7 @@ public final class CommandManager { } } - platform.registerCommands(dispatcher); + this.platform = platform; } public void unregister() {