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 3d3c01c3..7897ffe8 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 @@ -31,6 +31,7 @@ import org.bukkit.WorldCreator; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.WorldInitEvent; @@ -106,13 +107,21 @@ public abstract class BukkitQueue_0 extends NMSMa public static ConcurrentHashMap keepLoaded = new ConcurrentHashMap<>(8, 0.9f, 1); + + @EventHandler + public static void onChunkLoad(ChunkLoadEvent event) { + Chunk chunk = event.getChunk(); + long pair = MathMan.pairInt(chunk.getX(), chunk.getZ()); + keepLoaded.putIfAbsent(pair, Fawe.get().getTimer().getTickStart()); + } + @EventHandler public static void onChunkUnload(ChunkUnloadEvent event) { Chunk chunk = event.getChunk(); long pair = MathMan.pairInt(chunk.getX(), chunk.getZ()); Long lastLoad = keepLoaded.get(pair); if (lastLoad != null) { - if (System.currentTimeMillis() - lastLoad < 10000) { + if (Fawe.get().getTimer().getTickStart() - lastLoad < 10000) { event.setCancelled(true); } else { keepLoaded.remove(pair); 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 8281da4e..a11426d6 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 @@ -175,9 +175,9 @@ public class BukkitQueue_1_11 extends BukkitQueue_0 creation) { + if (shouldDelete(mca)) { mca.setDeleted(true); get().add(512 * 512 * 256); return null; @@ -44,45 +49,7 @@ public class DeleteUninhabitedFilter extends MCAFilterCounter { try { ForkJoinPool pool = new ForkJoinPool(); mca.init(); - mca.forEachSortedChunk(new RunnableVal4() { - @Override - public void run(Integer x, Integer z, Integer offset, Integer size) { - try { - byte[] bytes = mca.getChunkCompressedBytes(offset); - if (bytes == null) return; - Runnable task = new Runnable() { - @Override - public void run() { - try { - mca.streamChunk(offset, new RunnableVal() { - @Override - public void run(NBTStreamer value) { - value.addReader(".Level.InhabitedTime", new RunnableVal2() { - @Override - public void run(Integer index, Long value) { - if (value <= inhabitedTicks) { - MCAChunk chunk = new MCAChunk(null, x, z); - chunk.setDeleted(true); - synchronized (mca) { - mca.setChunk(chunk); - } - get().add(16 * 16 * 256); - } - } - }); - } - }); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; - pool.submit(task); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); + filter(mca, pool); pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); mca.close(pool); pool.shutdown(); @@ -91,4 +58,77 @@ public class DeleteUninhabitedFilter extends MCAFilterCounter { } return null; } + + public boolean shouldDelete(MCAFile mca) throws IOException { + File file = mca.getFile(); + BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class); + long creation = attr.creationTime().toMillis(); + long modified = attr.lastModifiedTime().toMillis(); + if ((modified - creation < fileAgeMillis && modified > creation) || file.length() < 12288) { + return true; + } + return false; + } + + public boolean shouldDeleteChunk(MCAFile mca, int cx, int cz) { + return false; + } + + public void filter(MCAFile mca, ForkJoinPool pool) throws IOException { + mca.forEachSortedChunk(new RunnableVal4() { + @Override + public void run(Integer x, Integer z, Integer offset, Integer size) { + try { + int bx = mca.getX() << 5; + int bz = mca.getZ() << 5; + if (shouldDeleteChunk(mca, bx, bz)) { + MCAChunk chunk = new MCAChunk(null, x, z); + chunk.setDeleted(true); + synchronized (mca) { + mca.setChunk(chunk); + } + get().add(16 * 16 * 256); + return; + } + byte[] bytes = mca.getChunkCompressedBytes(offset); + if (bytes == null) return; + Runnable task = new Runnable() { + @Override + public void run() { + try { + mca.streamChunk(offset, new RunnableVal() { + @Override + public void run(NBTStreamer value) { + addReaders(mca, x, z, value); + } + }); + } catch (FaweException ignore) { + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + pool.submit(task); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + public void addReaders(MCAFile mca, int x, int z, NBTStreamer streamer) { + streamer.addReader(".Level.InhabitedTime", new RunnableVal2() { + @Override + public void run(Integer index, Long value) { + if (value <= inhabitedTicks) { + MCAChunk chunk = new MCAChunk(null, x, z); + chunk.setDeleted(true); + synchronized (mca) { + mca.setChunk(chunk); + } + get().add(16 * 16 * 256); + } + } + }); + } } diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/PlotTrimFilter.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/PlotTrimFilter.java new file mode 100644 index 00000000..a157363e --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/PlotTrimFilter.java @@ -0,0 +1,127 @@ +package com.boydti.fawe.jnbt.anvil.filters; + +import com.boydti.fawe.jnbt.anvil.MCAChunk; +import com.boydti.fawe.jnbt.anvil.MCAFile; +import com.boydti.fawe.object.RunnableVal; +import com.intellectualcrafters.plot.PS; +import com.intellectualcrafters.plot.generator.HybridGen; +import com.intellectualcrafters.plot.generator.HybridPlotWorld; +import com.intellectualcrafters.plot.generator.IndependentPlotGenerator; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.Plot; +import com.intellectualcrafters.plot.object.PlotArea; +import com.intellectualcrafters.plot.util.expiry.ExpireManager; +import com.sk89q.worldguard.util.collect.LongHashSet; +import java.io.IOException; +import java.util.ArrayList; +import java.util.concurrent.ForkJoinPool; + +public class PlotTrimFilter extends DeleteUninhabitedFilter { + private final HybridPlotWorld hpw; + private final HybridGen hg; + private final MCAChunk reference; + private final LongHashSet occupiedRegions; + private final LongHashSet unoccupiedChunks; + + public static boolean shouldSuggest(PlotArea area) { + IndependentPlotGenerator gen = area.getGenerator(); + if (area instanceof HybridPlotWorld && gen instanceof HybridGen) { + HybridPlotWorld hpw = (HybridPlotWorld) area; + return hpw.PLOT_BEDROCK && !hpw.PLOT_SCHEMATIC && hpw.MAIN_BLOCK.length == 1 && hpw.TOP_BLOCK.length == 1; + } + return false; + } + + public PlotTrimFilter(PlotArea area, long fileAgeMillis, long inhabitedTicks) { + super(fileAgeMillis, inhabitedTicks); + IndependentPlotGenerator gen = area.getGenerator(); + if (!(area instanceof HybridPlotWorld) || !(gen instanceof HybridGen)) { + throw new UnsupportedOperationException("Trim does not support non hybrid plot worlds"); + } + this.hg = (HybridGen) gen; + this.hpw = (HybridPlotWorld) area; + + if (hpw.PLOT_BEDROCK && !hpw.PLOT_SCHEMATIC && hpw.MAIN_BLOCK.length == 1 && hpw.TOP_BLOCK.length == 1) { + this.reference = new MCAChunk(null, 0, 0); + this.reference.fillCuboid(0, 15, 0, 0, 0, 15, 7, (byte) 0); + this.reference.fillCuboid(0, 15, 1, hpw.PLOT_HEIGHT - 1, 0, 15, hpw.MAIN_BLOCK[0].id, (byte) 0); + this.reference.fillCuboid(0, 15, hpw.PLOT_HEIGHT, hpw.PLOT_HEIGHT, 0, 15, hpw.TOP_BLOCK[0].id, (byte) 0); + } else { + this.reference = null; + } + this.occupiedRegions = new LongHashSet(); + this.unoccupiedChunks = new LongHashSet(); + ArrayList plots = new ArrayList<>(); + plots.addAll(PS.get().getPlots(area)); + if (ExpireManager.IMP != null) { + plots.removeAll(ExpireManager.IMP.getPendingExpired()); + } + for (Plot plot : plots) { + Location pos1 = plot.getBottom(); + Location pos2 = plot.getTop(); + int ccx1 = pos1.getX() >> 9; + int ccz1 = pos1.getZ() >> 9; + int ccx2 = pos2.getX() >> 9; + int ccz2 = pos2.getZ() >> 9; + for (int x = ccx1; x <= ccx2; x++) { + for (int z = ccz1; z <= ccz2; z++) { + int bcx = x << 5; + int bcz = z << 5; + int tcx = bcx + 32; + int tcz = bcz + 32; + if (!occupiedRegions.containsKey(x, z)) { + occupiedRegions.add(x, z); + } else { + for (int cz = bcz; cz < tcz; cz++) { + for (int cx = bcx; cx < tcx; cx++) { + unoccupiedChunks.add(cx, cz); + } + } + } + } + } + int cx1 = pos1.getX() >> 4; + int cz1 = pos1.getZ() >> 4; + int cx2 = pos2.getX() >> 4; + int cz2 = pos2.getZ() >> 4; + for (int cz = cx1; cz < cx2; cz++) { + for (int cx = cz1; cx < cz2; cx++) { + unoccupiedChunks.remove(cx, cz); + } + } + } + } + + @Override + public boolean shouldDelete(MCAFile mca) throws IOException { + int x = mca.getX(); + int z = mca.getZ(); + return !occupiedRegions.containsKey(x, z) || super.shouldDelete(mca); + } + + @Override + public boolean shouldDeleteChunk(MCAFile mca, int cx, int cz) { + return !unoccupiedChunks.containsKey(cx, cz); + } + + @Override + public void filter(MCAFile mca, ForkJoinPool pool) throws IOException { + if (reference == null) { + super.filter(mca, pool); + return; + } + mca.forEachChunk(new RunnableVal() { + @Override + public void run(MCAChunk value) { + if (value.getInhabitedTime() < getInhabitedTicks()) { + value.setDeleted(true); + return; + } + if (reference.idsEqual(value, false)) { + value.setDeleted(true); + return; + } + } + }); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/MoveTo512.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/MoveTo512.java index 09c49f71..22e80806 100644 --- a/core/src/main/java/com/boydti/fawe/regions/general/plot/MoveTo512.java +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/MoveTo512.java @@ -45,6 +45,7 @@ import org.bukkit.World; usage = "/plots moveto512 [world]" ) public class MoveTo512 extends Command { + public MoveTo512() { super(MainCommand.getInstance(), true); } diff --git a/core/src/main/java/com/boydti/fawe/util/FaweTimer.java b/core/src/main/java/com/boydti/fawe/util/FaweTimer.java index 1d3334d6..f52a33e5 100644 --- a/core/src/main/java/com/boydti/fawe/util/FaweTimer.java +++ b/core/src/main/java/com/boydti/fawe/util/FaweTimer.java @@ -56,6 +56,10 @@ public class FaweTimer implements Runnable { return System.currentTimeMillis() - tickStart; } + public long getTickStart() { + return tickStart; + } + private long skip = 0; private long skipTick = 0; diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java index 66361d74..199b4e86 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java @@ -57,14 +57,14 @@ public class BrushOptionsCommands extends MethodCommands { aliases = {"/savebrush"}, usage = "[name]", desc = "Save your current brush\n" + - "prefix with ../ to save globally", + "use the -g flag to save globally", min = 1 ) @CommandPermissions("worldedit.brush.save") - public void saveBrush(Player player, LocalSession session, String name) throws WorldEditException, IOException { + public void saveBrush(Player player, LocalSession session, String name, @Switch('g') boolean root) throws WorldEditException, IOException { BrushTool tool = session.getBrushTool(player); if (tool != null) { - boolean root = name.startsWith("../"); + root |= name.startsWith("../"); name = FileSystems.getDefault().getPath(name).getFileName().toString(); File folder = MainUtil.getFile(Fawe.imp().getDirectory(), "brushes"); name = name.endsWith(".jsgz") ? name : name + ".jsgz"; 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 cf641b80..7b571f2b 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 @@ -68,7 +68,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool } protected static int MAX_RANGE = 500; - protected int range = -1; + protected int range = 240; private VisualMode visualMode = VisualMode.NONE; private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE; private Mask targetMask = null;