From 0f480b87bc8dddac9c4f9be09c8daf3c3af4961c Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Thu, 25 Aug 2016 12:54:12 +1000 Subject: [PATCH] Add replaceall command /replaceall [from] --- build.gradle | 2 +- core/src/main/java/com/boydti/fawe/Fawe.java | 4 +- .../boydti/fawe/command/AnvilCommands.java | 40 +++++- .../java/com/boydti/fawe/command/Reload.java | 2 +- .../java/com/boydti/fawe/config/Settings.java | 5 +- .../com/boydti/fawe/jnbt/anvil/MCAChunk.java | 2 - .../com/boydti/fawe/jnbt/anvil/MCAFile.java | 24 ++-- .../com/boydti/fawe/jnbt/anvil/MCAQueue.java | 23 +-- .../jnbt/anvil/MutableMCABackedBaseBlock.java | 2 +- .../fawe/object/mask/FaweBlockMatcher.java | 136 ++++++++++++++++++ 10 files changed, 208 insertions(+), 32 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java diff --git a/build.gradle b/build.gradle index e7347b32..e6b810a0 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ ext { git = org.ajoberstar.grgit.Grgit.open(file(".git")) revision = "-${git.head().abbreviatedId}" parents = git.head().parentIds; - index = -45; // Offset to mach CI + index = -43; // Offset to mach CI for (;parents != null && !parents.isEmpty();index++) { commit = git.getResolve().toCommit(parents.get(0)); parents = commit.getParentIds(); diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index a3759e39..1347b9ff 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -36,6 +36,7 @@ import com.sk89q.worldedit.command.tool.brush.GravityBrush; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extension.platform.PlatformManager; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.SchematicReader; @@ -263,7 +264,7 @@ public class Fawe { String versionString = scanner.next().trim(); scanner.close(); this.version = new FaweVersion(versionString); - Settings.DATE = new Date(version.year, version.month, version.day).toGMTString(); + Settings.DATE = new Date(100 + version.year, version.month, version.day).toGMTString(); Settings.BUILD = "http://ci.athion.net/job/FastAsyncWorldEdit/" + version.build; Settings.COMMIT = "https://github.com/boy0001/FastAsyncWorldedit/commit/" + Integer.toHexString(version.hash); } catch (Throwable ignore) {} @@ -351,6 +352,7 @@ public class Fawe { CuboidRegion.inject(); // Optimizations // Extents BlockTransformExtent.inject(); // Fix for cache not being mutable + AbstractDelegateExtent.inject(); // Optimizations // Vector Vector.inject(); // Optimizations // Operations 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 1cb7246c..e04afb1f 100644 --- a/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java +++ b/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java @@ -5,6 +5,7 @@ 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.MCAQueue; +import com.boydti.fawe.object.mask.FaweBlockMatcher; import com.boydti.fawe.object.number.LongAdder; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -35,6 +36,41 @@ public class AnvilCommands { this.worldEdit = worldEdit; } + @Command( + aliases = { "/replaceall", "/rea", "/repall" }, + usage = " [from-block] ", + desc = "Replace all blocks in the selection with another", + flags = "d", + min = 2, + max = 4 + ) + @CommandPermissions("worldedit.region.replace") + public void replaceAll(Player player, EditSession editSession, String folder, @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)); + BaseBlock tmp = new BaseBlock(35, 14); + File root = new File(folder + File.separator + "region"); + MCAQueue queue = new MCAQueue(folder, root, true); + final LongAdder count = new LongAdder(); + queue.filterWorld(new MCAFilter() { + @Override + public void applyBlock(int x, int y, int z, BaseBlock block) { + if (matchFrom.apply(block) && matchTo.apply(block)) { + count.add(1); + } + } + }); + BBC.VISITOR_BLOCK.send(player, count.longValue()); + } + @Command( aliases = { "/countall" }, usage = " [hasSky] ", @@ -44,9 +80,9 @@ public class AnvilCommands { max = 3 ) @CommandPermissions("worldedit.anvil.countallstone") - public void countAll(Player player, EditSession editSession, String folder, @Optional("true") boolean hasSky, String arg, @Switch('d') boolean useData) throws WorldEditException { + 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, hasSky); + 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 diff --git a/core/src/main/java/com/boydti/fawe/command/Reload.java b/core/src/main/java/com/boydti/fawe/command/Reload.java index 50088867..5ca4cb4b 100644 --- a/core/src/main/java/com/boydti/fawe/command/Reload.java +++ b/core/src/main/java/com/boydti/fawe/command/Reload.java @@ -31,7 +31,7 @@ public class Reload extends FaweCommand { MainUtil.sendMessage(player, "No version information available."); return false; } - MainUtil.sendMessage(player, "Version Date: " + new Date(version.year, version.month, version.day).toLocaleString()); + MainUtil.sendMessage(player, "Version Date: " + new Date(100 + version.year, version.month, version.day).toLocaleString()); MainUtil.sendMessage(player, "Version Commit: " + Integer.toHexString(version.hash)); MainUtil.sendMessage(player, "Version Build: #" + version.build); return true; diff --git a/core/src/main/java/com/boydti/fawe/config/Settings.java b/core/src/main/java/com/boydti/fawe/config/Settings.java index 70b3d5d9..c470f2eb 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -193,11 +193,10 @@ public class Settings extends Config { } } - @Comment("Experimental options") + @Comment("Experimental options, use at your own risk") public static class EXPERIMENTAL { @Comment({ - "Directly modify the region files:", - " - May corrupt world if in use" + "Directly modify the region files.", }) public static boolean ANVIL_QUEUE_MODE = false; @Comment({ diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index cd16377a..b9e6c97c 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -316,7 +316,6 @@ public class MCAChunk extends FaweChunk { } public int getSkyLight(int x, int y, int z) { - modified = true; int layer = y >> 4; byte[] skyLayer = skyLight[layer]; if (skyLayer == null) { @@ -327,7 +326,6 @@ public class MCAChunk extends FaweChunk { } public int getBlockLight(int x, int y, int z) { - modified = true; int layer = y >> 4; byte[] blockLayer = blockLight[layer]; if (blockLayer == null) { diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java index 21a9072e..84c7f2b4 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java @@ -15,7 +15,6 @@ import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; @@ -67,7 +66,7 @@ public class MCAFile { fieldBuf2.setAccessible(true); fieldBuf3 = NBTInputStream.class.getDeclaredField("buf"); fieldBuf3.setAccessible(true); - fieldBuf4 = ByteArrayOutputStream.class.getDeclaredField("buf"); + fieldBuf4 = FastByteArrayOutputStream.class.getDeclaredField("array"); fieldBuf4.setAccessible(true); fieldBuf5 = DeflaterOutputStream.class.getDeclaredField("buf"); fieldBuf5.setAccessible(true); @@ -102,6 +101,7 @@ public class MCAFile { } NBTInputStream nis = getChunkIS(offset); MCAChunk chunk = new MCAChunk(nis, queue, cx, cz, size); + nis.close(); int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31)); chunks.put(pair, chunk); return chunk; @@ -207,6 +207,7 @@ public class MCAFile { NBTStreamer ns = new NBTStreamer(is); addReaders.run(ns); ns.readFully(); + is.close(); } /** @@ -248,6 +249,7 @@ public class MCAFile { NBTOutputStream nos = new NBTOutputStream(bos); nos.writeNamedTag("", tag); bos.flush(); + bos.close(); byte[] result = baos.toByteArray(); return result; } @@ -267,13 +269,14 @@ public class MCAFile { private void writeHeader(int cx, int cz, int offsetMedium, int sizeByte) throws IOException { int i = ((cx & 31) << 2) + ((cz & 31) << 7); raf.seek(i); - raf.write((offsetMedium >>> 16) & 0xFF); - raf.write((offsetMedium >>> 8) & 0xFF); - raf.write((offsetMedium >>> 0) & 0xFF); + raf.write((offsetMedium >> 16)); + raf.write((offsetMedium >> 8)); + raf.write((offsetMedium >> 0)); raf.write(sizeByte); } public void close() { + flush(); try { raf.close(); } catch (IOException e) { @@ -336,7 +339,6 @@ public class MCAFile { } } if (newBytes == null) { - System.out.println("Deleting: " + cx + "," + cz); // Don't write continue; } @@ -353,16 +355,16 @@ public class MCAFile { byte[] nextBytes = getChunkCompressedBytes(nextOffset2); relocate.put(pair, nextBytes); } - System.out.println("Relocating " + nextCX + "," + nextCZ); +// System.out.println("Relocating " + nextCX + "," + nextCZ); int nextSize = MathMan.unpairY(nextLoc) << 12; end += nextSize; nextOffset2 += nextSize; } - System.out.println("Writing: " + cx + "," + cz); +// System.out.println("Writing: " + cx + "," + cz); writeSafe(start, newBytes); - if (offset != start || end != start + size) { - System.out.println("Header: " + cx + "," + cz + " | " + offset + "," + start + " | " + end + "," + (start + size) + " | " + size + " | " + start); - writeHeader(cx, cz, offset >> 12, newSize); + if (offset != start || end != start + size || oldSize != newSize) { +// System.out.println("Header: " + cx + "," + cz + " | " + offset + "," + start + " | " + end + "," + (start + size) + " | " + size + " | " + start); + writeHeader(cx, cz, start >> 12, newSize); } start += newSize << 12; } diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java index e19e6559..fa8e5ec0 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueue.java @@ -1,6 +1,5 @@ package com.boydti.fawe.jnbt.anvil; -import com.boydti.fawe.FaweCache; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.object.FaweChunk; @@ -43,7 +42,7 @@ public class MCAQueue extends NMSMappedFaweQueue (local) " + rcx + "," + rcz); + e.printStackTrace(); } } } }); + finalFile.close(); + System.gc(); + System.gc(); } }; - TaskManager.IMP.getPublicForkJoinPool().submit(run); +// TaskManager.IMP.getPublicForkJoinPool().submit(run); + run.run(); } } } catch (Throwable ignore) {} diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MutableMCABackedBaseBlock.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MutableMCABackedBaseBlock.java index b48c0a7e..6114405e 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MutableMCABackedBaseBlock.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MutableMCABackedBaseBlock.java @@ -27,8 +27,8 @@ public class MutableMCABackedBaseBlock extends BaseBlock { } public void setArrays(int layer) { - data = chunk.data[layer]; ids = chunk.ids[layer]; + data = chunk.data[layer]; } public void setIndex(int x, int y, int z, int index) { diff --git a/core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java b/core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java new file mode 100644 index 00000000..5c616c04 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java @@ -0,0 +1,136 @@ +package com.boydti.fawe.object.mask; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.PseudoRandom; +import com.sk89q.worldedit.blocks.BaseBlock; +import java.util.Set; + +public abstract class FaweBlockMatcher { + public abstract boolean apply(BaseBlock block); + + public static FaweBlockMatcher ALWAYS_TRUE = new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + return true; + } + }; + + public static FaweBlockMatcher NOT_AIR = new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + return block.getId() != 0; + } + }; + + public static FaweBlockMatcher setBlock(BaseBlock block) { + final int id = block.getId(); + final int data = block.getData(); + if (data == 0) { + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock oldBlock) { + int currentId = oldBlock.getId(); + oldBlock.setId(id); + if (FaweCache.hasData(currentId)) { + oldBlock.setData(0); + } + if (FaweCache.hasNBT(id) && oldBlock.hasNbtData()) { + oldBlock.setNbtData(null); + } + return true; + } + }; + } + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock oldBlock) { + int currentId = oldBlock.getId(); + oldBlock.setId(id); + oldBlock.setData(data); + if (FaweCache.hasNBT(id) && oldBlock.hasNbtData()) { + oldBlock.setNbtData(null); + } + return true; + } + }; + } + + public static FaweBlockMatcher setBlocks(Set blocks) { + if (blocks.size() == 1) { + return setBlock(blocks.iterator().next()); + } + final BaseBlock[] array = blocks.toArray(new BaseBlock[blocks.size()]); + final PseudoRandom random = new PseudoRandom(System.nanoTime()); + final int size = array.length; + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + BaseBlock replace = array[random.random(size)]; + int currentId = block.getId(); + block.setId(replace.getId()); + if (FaweCache.hasNBT(currentId)) { + block.setNbtData(null); + } + if (FaweCache.hasData(currentId) || replace.getData() != 0) { + block.setData(replace.getData()); + } + return true; + } + }; + } + + public static FaweBlockMatcher fromBlock(BaseBlock block, boolean checkData) { + final int id = block.getId(); + final int data = block.getData(); + if (checkData && FaweCache.hasData(id)) { + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + return (block.getId() == id && block.getData() == data); + } + }; + } else { + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + return (block.getId() == id); + } + }; + } + } + + public static FaweBlockMatcher fromBlocks(Set searchBlocks, boolean checkData) { + if (searchBlocks.size() == 1) { + return fromBlock(searchBlocks.iterator().next(), checkData); + } + final boolean[] allowedId = new boolean[FaweCache.getId(Character.MAX_VALUE)]; + for (BaseBlock block : searchBlocks) { + allowedId[block.getId()] = true; + } + final boolean[] allowed = new boolean[Character.MAX_VALUE]; + for (BaseBlock block : searchBlocks) { + allowed[FaweCache.getCombined(block)] = true; + } + if (checkData) { + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + int id = block.getId(); + if (allowedId[id]) { + if (FaweCache.hasData(id)) { + return allowed[(id << 4) + block.getData()]; + } + return true; + } + return false; + } + }; + } + return new FaweBlockMatcher() { + @Override + public boolean apply(BaseBlock block) { + return allowedId[block.getId()]; + } + }; + } +}