From c7d959d6dc5a640049adff6bce0b103282a0e46b Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Mon, 3 Apr 2017 20:07:57 +1000 Subject: [PATCH] Anvil replace + masking extent biomes --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 10 +- .../boydti/fawe/bukkit/v0/BukkitQueue_0.java | 30 ++ .../fawe/bukkit/v1_11/BukkitQueue_1_11.java | 71 ++++ core/src/main/java/com/boydti/fawe/Fawe.java | 2 + .../boydti/fawe/command/AnvilCommands.java | 249 ++++++++++++-- .../com/boydti/fawe/command/Rollback.java | 8 +- .../boydti/fawe/example/MappedFaweQueue.java | 5 + .../com/boydti/fawe/jnbt/anvil/MCAChunk.java | 116 ++++++- .../boydti/fawe/jnbt/anvil/MCAClipboard.java | 28 ++ .../com/boydti/fawe/jnbt/anvil/MCAFile.java | 97 ++++-- .../com/boydti/fawe/jnbt/anvil/MCAFilter.java | 15 +- .../fawe/jnbt/anvil/MCAFilterCounter.java | 23 ++ .../com/boydti/fawe/jnbt/anvil/MCAQueue.java | 310 ++++++++++++++---- .../boydti/fawe/jnbt/anvil/MCAQueueMap.java | 9 + .../com/boydti/fawe/object/FawePlayer.java | 5 + .../com/boydti/fawe/object/FaweQueue.java | 4 + .../com/boydti/fawe/object/RegionWrapper.java | 18 +- .../fawe/object/brush/FallingSphere.java | 4 + .../clipboard/CPUOptimizedClipboard.java | 1 - .../collection/IterableThreadLocal.java | 33 ++ .../boydti/fawe/object/extent/MCAExtent.java | 126 ------- .../fawe/object/number/MutableLong.java | 21 ++ .../object/pattern/IdDataMaskPattern.java | 2 +- .../regions/general/plot/CreateFromImage.java | 2 + .../fawe/regions/general/plot/PlotTrim.java | 3 +- .../boydti/fawe/util/DelegateFaweQueue.java | 6 + .../com/sk89q/worldedit/LocalSession.java | 1 - .../worldedit/command/BrushCommands.java | 2 +- .../worldedit/command/ToolUtilCommands.java | 4 +- .../worldedit/command/tool/BrushTool.java | 1 + .../extension/platform/CommandManager.java | 5 +- .../sk89q/worldedit/extent/MaskingExtent.java | 86 +++++ .../sk89q/worldedit/regions/CuboidRegion.java | 16 +- .../java/com/boydti/fawe/forge/ForgeMain.java | 4 - .../java/com/boydti/fawe/forge/ForgeMain.java | 4 - .../java/com/boydti/fawe/forge/ForgeMain.java | 4 - .../java/com/boydti/fawe/forge/ForgeMain.java | 4 - .../java/com/boydti/fawe/forge/ForgeMain.java | 4 - region/r.-1.-1.mca | Bin 0 -> 1597440 bytes 39 files changed, 1030 insertions(+), 303 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAClipboard.java create mode 100644 core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFilterCounter.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/FallingSphere.java create mode 100644 core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java delete mode 100644 core/src/main/java/com/boydti/fawe/object/extent/MCAExtent.java create mode 100644 core/src/main/java/com/boydti/fawe/object/number/MutableLong.java create mode 100644 core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java create mode 100644 region/r.-1.-1.mca diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 8160468f..29813b16 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -217,7 +217,7 @@ public class FaweBukkit implements IFawe, Listener { Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI if (hasNMS) { - ignore.printStackTrace(); + debug("====== NO NMS BLOCK PLACER FOUND ======"); debug("FAWE couldn't find a fast block placer"); debug("Bukkit version: " + Bukkit.getVersion()); @@ -227,6 +227,8 @@ public class FaweBukkit implements IFawe, Listener { debug("Download the version of FAWE for your platform"); debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target"); debug("======================================="); + ignore.printStackTrace(); + debug("======================================="); TaskManager.IMP.laterAsync(new Runnable() { @Override public void run() { @@ -262,9 +264,11 @@ public class FaweBukkit implements IFawe, Listener { } catch (Throwable ignore) { } } + Throwable error = null; try { return plugin.getQueue(world); } catch (Throwable ignore) { + error = ignore; } // Disable incompatible settings Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level @@ -273,12 +277,14 @@ public class FaweBukkit implements IFawe, Listener { debug("====== NO NMS BLOCK PLACER FOUND ======"); debug("FAWE couldn't find a fast block placer"); debug("Bukkit version: " + Bukkit.getVersion()); - debug("NMS label: " + plugin.getClass().getSimpleName().split("_")[1]); + debug("NMS label: " + plugin.getClass().getSimpleName()); debug("Fallback placer: " + BukkitQueue_All.class); debug("======================================="); debug("Download the version of FAWE for your platform"); debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target"); debug("======================================="); + error.printStackTrace(); + debug("======================================="); TaskManager.IMP.laterAsync(new Runnable() { @Override public void run() { diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index 4c781603..63303939 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -11,11 +11,13 @@ import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.world.biome.BaseBiome; import java.io.File; +import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; @@ -204,6 +206,11 @@ public abstract class BukkitQueue_0 extends NMSMa private static Field fieldTimingsEnabled; private static Field fieldAsyncCatcherEnabled; private static Method methodCheck; + private static Class classRegionFile; + private static Class classRegionFileCache; + private static Field fieldRegionMap; + private static Field fieldRegionFile; + private static Method methodUnloadChunk; static { try { fieldAsyncCatcherEnabled = Class.forName("org.spigotmc.AsyncCatcher").getField("enabled"); @@ -215,6 +222,29 @@ public abstract class BukkitQueue_0 extends NMSMa methodCheck = Class.forName("co.aikar.timings.TimingsManager").getDeclaredMethod("recheckEnabled"); methodCheck.setAccessible(true); } catch (Throwable ignore){} + try { + classRegionFile = ReflectionUtils.getNmsClass("RegionFile"); + classRegionFileCache = ReflectionUtils.getNmsClass("RegionFileCache"); + for (Field field : classRegionFileCache.getDeclaredFields()) { + if (Map.class.isAssignableFrom(field.getType())) { + fieldRegionMap = field; + fieldRegionMap.setAccessible(true); + break; + } + } + for (Field field : classRegionFile.getDeclaredFields()) { + if (RandomAccessFile.class.isAssignableFrom(field.getType())) { + fieldRegionFile = field; + fieldRegionFile.setAccessible(true); + break; + } + } + Class classCraftWorld = ReflectionUtils.getCbClass("CraftWorld"); + methodUnloadChunk = classCraftWorld.getDeclaredMethod("unloadChunk0", int.class, int.class, boolean.class); + methodUnloadChunk.setAccessible(true); + } catch (Throwable ignore) { + ignore.printStackTrace(); + } } @Override diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/BukkitQueue_1_11.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/BukkitQueue_1_11.java index 02de3f73..b2f34c26 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/BukkitQueue_1_11.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/BukkitQueue_1_11.java @@ -7,6 +7,7 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.brush.visualization.VisualChunk; import com.boydti.fawe.object.number.LongAdder; @@ -26,9 +27,11 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -57,6 +60,8 @@ import net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk; import net.minecraft.server.v1_11_R1.PacketPlayOutMultiBlockChange; import net.minecraft.server.v1_11_R1.PlayerChunk; import net.minecraft.server.v1_11_R1.PlayerChunkMap; +import net.minecraft.server.v1_11_R1.RegionFile; +import net.minecraft.server.v1_11_R1.RegionFileCache; import net.minecraft.server.v1_11_R1.ServerNBTManager; import net.minecraft.server.v1_11_R1.TileEntity; import net.minecraft.server.v1_11_R1.WorldChunkManager; @@ -241,6 +246,72 @@ public class BukkitQueue_1_11 extends BukkitQueue_0() { + @Override + public void run(Object value) { + try { + synchronized (RegionFileCache.class) { + ArrayDeque chunks = new ArrayDeque<>(); + World world = getWorld(); + world.setKeepSpawnInMemory(false); + ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); + if (unload) { // Unload chunks + int bcx = (allowed.minX >> 9) << 5; + int bcz = (allowed.minZ >> 9) << 5; + int tcx = 31 + (allowed.maxX >> 9) << 5; + int tcz = 31 + (allowed.maxZ >> 9) << 5; + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_11_R1.Chunk chunk = iter.next(); + int cx = chunk.locX; + int cz = chunk.locZ; + if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { + chunks.add(chunk); + } + } + for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) { + provider.unloadChunk(chunk, true); + } + } + provider.c(); + + if (unload) { // Unload regions + Map map = RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + RegionFile regionFile = entry.getValue(); + regionFile.c(); + iter.remove(); + } + } + whileLocked.run(); + // Load the chunks again + if (unload) { + for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) { + chunk = provider.loadChunk(chunk.locX, chunk.locZ); + if (chunk != null) { + sendChunk(chunk, 0); + } + } + } + + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + }); + return true; + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + @Override public void setHeightMap(FaweChunk chunk, byte[] heightMap) { CraftChunk craftChunk = (CraftChunk) chunk.getChunk(); diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index 7466fd82..87dd2cc1 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -63,6 +63,7 @@ import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.platform.PlayerProxy; import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.MaskingExtent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.SchematicReader; @@ -440,6 +441,7 @@ public class Fawe { // Regions CuboidRegion.inject(); // Optimizations // Extents + MaskingExtent.inject(); // Features BlockTransformExtent.inject(); // Fix for cache not being mutable AbstractDelegateExtent.inject(); // Optimizations BlockBagExtent.inject(); // Fixes + Optimizations diff --git a/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java b/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java index 8878d77c..c9ec4f9f 100644 --- a/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java +++ b/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java @@ -1,13 +1,16 @@ package com.boydti.fawe.command; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.BBC; import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.jnbt.anvil.MCAFilter; +import com.boydti.fawe.jnbt.anvil.MCAFilterCounter; import com.boydti.fawe.jnbt.anvil.MCAQueue; import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.mask.FaweBlockMatcher; -import com.boydti.fawe.object.number.LongAdder; +import com.boydti.fawe.object.number.MutableLong; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.StringMan; import com.sk89q.minecraft.util.commands.Command; @@ -17,13 +20,19 @@ import com.sk89q.worldedit.MutableBlockVector; 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.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.RandomPattern; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; import java.io.File; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Set; @@ -45,7 +54,7 @@ public class AnvilCommands { } @Command( - aliases = {"/replaceall", "/rea", "/repall"}, + aliases = {"replaceall", "rea", "repall"}, usage = " [from-block] ", desc = "Replace all blocks in the selection with another", flags = "d", @@ -66,13 +75,13 @@ public class AnvilCommands { final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true)); File root = new File(folder + File.separator + "region"); MCAQueue queue = new MCAQueue(folder, root, true); - queue.filterWorld(new MCAFilter() { + MCAFilterCounter counter = queue.filterWorld(new MCAFilterCounter() { @Override - public void applyBlock(int x, int y, int z, BaseBlock block) { + public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) { if (matchFrom.apply(block)) matchTo.apply(block); } }); - player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(-1)); + player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(counter.getTotal())); } @Command( @@ -87,6 +96,7 @@ public class AnvilCommands { public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false); MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky()); + MCAFilterCounter counter; if (useMap) { List split = StringMan.split(from, ','); if (to instanceof RandomPattern) { @@ -116,10 +126,11 @@ public class AnvilCommands { } } } - queue.filterWorld(new MCAFilter() { + + counter = queue.filterWorld(new MCAFilterCounter() { private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0); @Override - public void applyBlock(int x, int y, int z, BaseBlock block) { + public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) { int id = block.getId(); int data = FaweCache.hasData(id) ? block.getData() : 0; int combined = FaweCache.getCombined(id, data); @@ -137,9 +148,11 @@ public class AnvilCommands { }); } else { player.print(BBC.getPrefix() + "Mask:Pattern must be a 1:1 match"); + return; } } else { player.print(BBC.getPrefix() + "Must be a pattern list!"); + return; } } else { final FaweBlockMatcher matchFrom; @@ -151,9 +164,9 @@ public class AnvilCommands { } matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData); } - queue.filterWorld(new MCAFilter() { + counter = queue.filterWorld(new MCAFilterCounter() { @Override - public void applyBlock(int x, int y, int z, BaseBlock block) { + public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) { if (matchFrom.apply(block)) { BaseBlock newBlock = to.apply(x, y, z); int currentId = block.getId(); @@ -166,11 +179,11 @@ public class AnvilCommands { } }); } - player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(-1)); + player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(counter.getTotal())); } @Command( - aliases = {"/countall"}, + aliases = {"countall"}, usage = " [hasSky] ", desc = "Count all blocks in a world", flags = "d", @@ -181,7 +194,6 @@ public class AnvilCommands { public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException { File root = new File(folder + File.separator + "region"); MCAQueue queue = new MCAQueue(folder, root, true); - final LongAdder count = new LongAdder(); if (arg.contains(":")) { useData = true; //override d flag, if they specified data they want it } @@ -190,15 +202,15 @@ public class AnvilCommands { for (BaseBlock block : searchBlocks) { allowedId[block.getId()] = true; } - MCAFilter filter; + MCAFilterCounter filter; if (useData) { // Optimize for both cases final boolean[] allowed = new boolean[Character.MAX_VALUE]; for (BaseBlock block : searchBlocks) { allowed[FaweCache.getCombined(block)] = true; } - filter = new MCAFilter() { + filter = new MCAFilterCounter() { @Override - public MCAChunk applyChunk(MCAChunk chunk) { + public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) { for (int layer = 0; layer < chunk.ids.length; layer++) { byte[] ids = chunk.ids[layer]; if (ids == null) { @@ -215,7 +227,7 @@ public class AnvilCommands { combined += chunk.getNibble(i, datas); } if (allowed[combined]) { - count.add(1); + count.increment(); } } } @@ -223,15 +235,15 @@ public class AnvilCommands { } }; } else { - filter = new MCAFilter() { + filter = new MCAFilterCounter() { @Override - public MCAChunk applyChunk(MCAChunk chunk) { + public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) { for (int layer = 0; layer < chunk.ids.length; layer++) { byte[] ids = chunk.ids[layer]; if (ids != null) { for (byte i : ids) { if (allowedId[i & 0xFF]) { - count.add(1); + count.increment(); } } } @@ -241,7 +253,202 @@ public class AnvilCommands { }; } queue.filterWorld(filter); - player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(count.longValue())); + player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(filter.getTotal())); } -} + @Command( + aliases = {"distr"}, + desc = "Replace all blocks in the selection with another" + ) + @CommandPermissions("worldedit.anvil.distr") + public void distr(Player player, EditSession editSession, @Selection Region selection, @Switch('d') boolean useData) throws WorldEditException { + long total = 0; + long[] count; + MCAFilter counts; + if (useData) { + counts = runWithSelection(player, editSession, selection, new MCAFilter() { + @Override + public void applyBlock(int x, int y, int z, BaseBlock block, long[] counts) { + counts[block.getCombined()]++; + } + @Override + public long[] init() { + return new long[Character.MAX_VALUE + 1]; + } + }); + count = new long[Character.MAX_VALUE + 1]; + } else { + counts = runWithSelection(player, editSession, selection, new MCAFilter() { + @Override + public void applyBlock(int x, int y, int z, BaseBlock block, long[] counts) { + counts[block.getId()]++; + } + @Override + public long[] init() { + return new long[4096]; + } + }); + count = new long[4096]; + } + for (long[] value : counts) { + for (int i = 0; i < value.length; i++) { + count[i] += value[i]; + total += value[i]; + } + } + ArrayList map = new ArrayList<>(); + for (int i = 0; i < count.length; i++) { + if (count[i] != 0) map.add(new long[] { i, count[i]}); + } + Collections.sort(map, new Comparator() { + @Override + public int compare(long[] a, long[] b) { + long vA = a[1]; + long vB = b[1]; + return (vA < vB) ? -1 : ((vA == vB) ? 0 : 1); + } + }); + if (useData) { + for (long[] c : map) { + BaseBlock block = FaweCache.CACHE_BLOCK[(int) c[0]]; + String name = BlockType.fromID(block.getId()).getName(); + String str = String.format("%-7s (%.3f%%) %s #%d:%d", + String.valueOf(c[1]), + ((c[1] * 10000) / total) / 100d, + name == null ? "Unknown" : name, + block.getType(), block.getData()); + player.print(BBC.getPrefix() + str); + } + } else { + for (long[] c : map) { + BlockType block = BlockType.fromID((int) c[0]); + String str = String.format("%-7s (%.3f%%) %s #%d", + String.valueOf(c[1]), + ((c[1] * 10000) / total) / 100d, + block == null ? "Unknown" : block.getName(), c[0]); + player.print(BBC.getPrefix() + str); + } + } + } + + private > T runWithSelection(Player player, EditSession editSession, Region selection, T filter) { + if (!(selection instanceof CuboidRegion)) { + BBC.NO_REGION.send(player); + return null; + } + CuboidRegion cuboid = (CuboidRegion) selection; + RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint()); + String worldName = Fawe.imp().getWorldName(editSession.getWorld()); + FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false); + File folder = tmp.getSaveFolder(); + MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky()); + player.print(BBC.getPrefix() + "Safely unloading regions..."); + tmp.setMCA(new Runnable() { + @Override + public void run() { + player.print(BBC.getPrefix() + "Performing operation..."); + queue.filterRegion(filter, wrappedRegion); + player.print(BBC.getPrefix() + "Safely loading regions..."); + } + }, wrappedRegion, true); + return filter; + } + + @Command( + aliases = {"replace"}, + usage = "[from-block] ", + desc = "Replace all blocks in the selection with another" + ) + @CommandPermissions("worldedit.anvil.replace") + public void replace(Player player, EditSession editSession, @Selection Region selection, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { + final FaweBlockMatcher matchFrom; + if (from == null) { + matchFrom = FaweBlockMatcher.NOT_AIR; + } else { + if (from.contains(":")) { + useData = true; //override d flag, if they specified data they want it + } + matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData); + } + final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true)); + MCAFilterCounter filter = runWithSelection(player, editSession, selection, new MCAFilterCounter() { + @Override + public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong count) { + if (matchFrom.apply(block)) { + matchTo.apply(block); + count.increment(); + } + } + }); + if (filter != null) { + player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(filter.getTotal())); + } + } +// +// @Command( +// aliases = {"copychunks"}, +// desc = "Lazily copy chunks to your anvil clipboard" +// ) +// @CommandPermissions("worldedit.anvil.copychunks") +// public void copy(Player player, LocalSession session, EditSession editSession, @Selection Region selection) throws WorldEditException { +// if (!(selection instanceof CuboidRegion)) { +// BBC.NO_REGION.send(player); +// return; +// } +// CuboidRegion cuboid = (CuboidRegion) selection; +// String worldName = Fawe.imp().getWorldName(editSession.getWorld()); +// FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false); +// File folder = tmp.getSaveFolder(); +// MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky()); +// Vector origin = session.getPlacementPosition(player); +// MCAClipboard clipboard = new MCAClipboard(queue, cuboid, origin); +// FawePlayer fp = FawePlayer.wrap(player); +// fp.setMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD, clipboard); +// BBC.COMMAND_COPY.send(player, selection.getArea()); +// } +// +// @Command( +// aliases = {"pastechunks"}, +// desc = "Paste chunks from your anvil clipboard" +// ) +// @CommandPermissions("worldedit.anvil.pastechunks") +// public void paste(Player player, LocalSession session, EditSession editSession) throws WorldEditException { +// FawePlayer fp = FawePlayer.wrap(player); +// MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD); +// if (clipboard == null) { +// fp.sendMessage(BBC.getPrefix() + "You must first copy to your clipboard"); +// return; +// } +// CuboidRegion cuboid = clipboard.getRegion(); +// RegionWrapper copyRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint()); +// Vector offset = player.getPosition().subtract(clipboard.getOrigin()); +// int oX = offset.getBlockX(); +// int oZ = offset.getBlockZ(); +// RegionWrapper pasteRegion = new RegionWrapper(copyRegion.minX + oX, copyRegion.maxX + oX, copyRegion.minZ + oZ, copyRegion.maxZ + oZ); +// String pasteWorldName = Fawe.imp().getWorldName(editSession.getWorld()); +// FaweQueue tmpTo = SetQueue.IMP.getNewQueue(pasteWorldName, true, false); +// FaweQueue tmpFrom = SetQueue.IMP.getNewQueue(clipboard.getQueue().getWorldName(), true, false); +// File folder = tmpTo.getSaveFolder(); +// MCAQueue copyQueue = clipboard.getQueue(); +// MCAQueue pasteQueue = new MCAQueue(pasteWorldName, folder, tmpTo.hasSky()); +// player.print(BBC.getPrefix() + "Safely unloading regions..."); +// tmpTo.setMCA(new Runnable() { +// @Override +// public void run() { +// tmpFrom.setMCA(new Runnable() { +// @Override +// public void run() { +// try { +// player.print(BBC.getPrefix() + "Performing operation..."); +// pasteQueue.pasteRegion(copyQueue, copyRegion, offset); +// player.print(BBC.getPrefix() + "Safely loading regions..."); +// } catch (Throwable e) { +// e.printStackTrace(); +// } +// } +// }, copyRegion, false); +// } +// }, pasteRegion, true); +// player.print("Done!"); +// } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/command/Rollback.java b/core/src/main/java/com/boydti/fawe/command/Rollback.java index d7447971..61fe30c6 100644 --- a/core/src/main/java/com/boydti/fawe/command/Rollback.java +++ b/core/src/main/java/com/boydti/fawe/command/Rollback.java @@ -62,7 +62,7 @@ public class Rollback extends FaweCommand { BBC.COMMAND_SYNTAX.send(player, "/frb u: r: t: