From 71306cb74962fc85454e0e85fbb4acb13f54b932 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Tue, 8 Aug 2017 17:36:17 +1000 Subject: [PATCH] Various Anvil API can now be used fully async - The underlying code still schedules things on the main thread plotsquared + plotme perms now default to true Minor optimization for DownwardVisitor --- .../fawe/bukkit/v1_10/BukkitQueue_1_10.java | 181 +++++--- .../fawe/bukkit/v1_11/BukkitQueue_1_11.java | 204 +++++---- .../fawe/bukkit/v1_12/BukkitQueue_1_12.java | 256 +++++------ .../fawe/bukkit/v1_7/BukkitQueue17.java | 161 ++++--- .../fawe/bukkit/v1_8/BukkitQueue18R3.java | 162 ++++--- .../fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java | 171 +++++--- bukkit/src/main/resources/plugin.yml | 4 + .../boydti/fawe/command/AnvilCommands.java | 72 +-- .../com/boydti/fawe/jnbt/anvil/MCAFile.java | 6 + .../com/boydti/fawe/jnbt/anvil/MCAFilter.java | 6 + .../com/boydti/fawe/jnbt/anvil/MCAQueue.java | 414 ++++++++++-------- .../boydti/fawe/jnbt/anvil/MCAQueueMap.java | 13 +- .../jnbt/anvil/filters/DelegateMCAFilter.java | 7 + .../boydti/fawe/object/FaweInputStream.java | 12 + .../boydti/fawe/object/FaweOutputStream.java | 1 - .../com/boydti/fawe/object/FaweQueue.java | 5 +- .../fawe/object/extent/ResettableExtent.java | 12 + .../fawe/object/extent/TransformExtent.java | 15 + .../boydti/fawe/util/DelegateFaweQueue.java | 4 +- .../java/com/boydti/fawe/util/MainUtil.java | 35 ++ .../java/com/sk89q/worldedit/EditSession.java | 33 +- .../function/visitor/DownwardVisitor.java | 4 +- 22 files changed, 1037 insertions(+), 741 deletions(-) diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java index 44fa718a..03083a8e 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java @@ -26,7 +26,6 @@ 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; @@ -39,7 +38,6 @@ import net.minecraft.server.v1_10_R1.BiomeBase; import net.minecraft.server.v1_10_R1.BiomeCache; import net.minecraft.server.v1_10_R1.Block; import net.minecraft.server.v1_10_R1.BlockPosition; -import net.minecraft.server.v1_10_R1.ChunkCoordIntPair; import net.minecraft.server.v1_10_R1.ChunkProviderGenerate; import net.minecraft.server.v1_10_R1.ChunkProviderServer; import net.minecraft.server.v1_10_R1.ChunkSection; @@ -61,8 +59,6 @@ import net.minecraft.server.v1_10_R1.PacketPlayOutMapChunk; import net.minecraft.server.v1_10_R1.PacketPlayOutMultiBlockChange; import net.minecraft.server.v1_10_R1.PlayerChunk; import net.minecraft.server.v1_10_R1.PlayerChunkMap; -import net.minecraft.server.v1_10_R1.RegionFile; -import net.minecraft.server.v1_10_R1.RegionFileCache; import net.minecraft.server.v1_10_R1.ServerNBTManager; import net.minecraft.server.v1_10_R1.TileEntity; import net.minecraft.server.v1_10_R1.WorldChunkManager; @@ -406,75 +402,122 @@ public class BukkitQueue_1_10 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_10_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_10_R1.Chunk chunk : chunks) { - provider.unload(chunk); - } - boolean autoSave = world.isAutoSave(); - world.setAutoSave(true); - for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) { - provider.unloadChunks(); - } - world.setAutoSave(autoSave); - } - 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_10_R1.Chunk chunk : chunks) { - chunk = provider.loadChunk(chunk.locX, chunk.locZ); - if (chunk != null) { - provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); - sendChunk(chunk, 0); - } - } - } + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (net.minecraft.server.v1_10_R1.RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + net.minecraft.server.v1_10_R1.ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_10_R1.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + if (chunk.a(false)) { + mustSave = true; + provider.saveChunk(chunk); + provider.saveChunkNOP(chunk); + } + continue; + } + iter.remove(); + boolean save = chunk.a(false); + mustSave |= save; + if (save) { + provider.unload(chunk); + } else { + chunk.bukkitChunk.unload(false, false); + } + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; + } + } + } + } + if (mustSave) provider.c(); // TODO only the necessary chunks + + File unloadedRegion = null; + if (load && !net.minecraft.server.v1_10_R1.RegionFileCache.a.isEmpty()) { + Map map = net.minecraft.server.v1_10_R1.RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + net.minecraft.server.v1_10_R1.RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); + } + } + break; + } + } + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value1) { + net.minecraft.server.v1_10_R1.Chunk chunk = provider.getChunkAt(cx, cz, null, false); + if (chunk != null) { + PlayerChunk pc = nmsWorld.getPlayerChunkMap().getChunk(cx, cz); + if (pc != null && !pc.c.isEmpty()) { + sendChunk(chunk, 0); + } + } + } + }); + } + } + } + } + }); } - } catch (Throwable e) { - e.printStackTrace(); } } - }); - return true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + } + }); + return true; } @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 c32b2d01..c42babc7 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 @@ -26,7 +26,6 @@ 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; @@ -56,12 +55,8 @@ import net.minecraft.server.v1_11_R1.MinecraftServer; import net.minecraft.server.v1_11_R1.NBTTagCompound; import net.minecraft.server.v1_11_R1.NibbleArray; import net.minecraft.server.v1_11_R1.PacketDataSerializer; -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; @@ -252,70 +247,131 @@ 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(); + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (net.minecraft.server.v1_11_R1.RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + net.minecraft.server.v1_11_R1.ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - 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(); + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_11_R1.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + if (chunk.a(false)) { + mustSave = true; + provider.saveChunk(chunk); + provider.saveChunkNOP(chunk); + } + continue; + } iter.remove(); + boolean save = chunk.a(false); + mustSave |= save; + provider.unloadChunk(chunk, save); + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; } } - whileLocked.run(); - // Load the chunks again - if (unload) { - for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) { - chunk = provider.getChunkAt(chunk.locX, chunk.locZ, null, false); - if (chunk != null) { - sendChunk(chunk, 0); + } + } + if (mustSave) provider.c(); // TODO only the necessary chunks + + File unloadedRegion = null; + if (load && !net.minecraft.server.v1_11_R1.RegionFileCache.a.isEmpty()) { + Map map = net.minecraft.server.v1_11_R1.RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + net.minecraft.server.v1_11_R1.RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); } } + break; } - } - } catch (Throwable e) { - e.printStackTrace(); + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value1) { + net.minecraft.server.v1_11_R1.Chunk chunk = provider.getChunkAt(cx, cz, null, false); + if (chunk != null) { + net.minecraft.server.v1_11_R1.PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz); + if (pc != null) { + sendChunk(pc, chunk, 0); + } + } + } + }); + } + } + } + } + }); + } } } - }); - return true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + } + }); + return true; } @Override @@ -474,7 +530,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0 chunks) { - ArrayDeque queue = TaskManager.IMP.sync(new RunnableVal>() { + @Override + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { @Override - public void run(ArrayDeque value) { - this.value = new ArrayDeque<>(); - ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - 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_12_R1.Chunk chunk = iter.next(); - int cx = chunk.locX; - int cz = chunk.locZ; - if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { - this.value.add(chunk); - if (getPlayerChunk(nmsWorld, cx, cz) != null) { - chunks.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)); + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); + + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_12_R1.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + if (chunk.a(false)) { + mustSave = true; + provider.saveChunk(chunk); + provider.saveChunkNOP(chunk); + } + continue; + } + iter.remove(); + boolean save = chunk.a(false); + mustSave |= save; + provider.unloadChunk(chunk, save); + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; + } + } + } + } + if (mustSave) provider.c(); // TODO only the necessary chunks + + File unloadedRegion = null; + if (load && !RegionFileCache.a.isEmpty()) { + Map map = RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); + } + } + break; + } + } + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value1) { + net.minecraft.server.v1_12_R1.Chunk chunk = provider.getChunkAt(cx, cz, null, false); + if (chunk != null) { + PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz); + if (pc != null) { + sendChunk(pc, chunk, 0); + } + } + } + }); + } + } + } + } + }); } } } } }); - if (Fawe.get().getMainThread() == Thread.currentThread()) { - ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - for (net.minecraft.server.v1_12_R1.Chunk chunk : queue) { - provider.unloadChunk(chunk, true); - } - World world = getWorld(); - boolean autoSave = world.isAutoSave(); - world.setAutoSave(true); - for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) { - provider.unloadChunks(); - } - world.setAutoSave(autoSave); - } else { - new TaskBuilder().syncWhenFree(new TaskBuilder.SplitTask(50) { - @Override - public Object exec(Object previous) { - ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - for (net.minecraft.server.v1_12_R1.Chunk chunk : queue) { - provider.unloadChunk(chunk, true); - split(); - } - try { - IChunkLoader loader = (IChunkLoader) fieldChunkLoader.get(provider); - if (loader instanceof ChunkRegionLoader) { - ChunkRegionLoader crl = (ChunkRegionLoader) loader; - for (int i = 0; i < queue.size() && crl.a(); i++) { - split(); - } - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - }).build(); - } - } - - @Override - public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) { - try { - Object lock = new Object(); - ForkJoinPool pool = new ForkJoinPool(); - PrimitiveList chunks = new PrimitiveList(Long.class); - if (unload && Fawe.get().getMainThread() != Thread.currentThread()) unload(allowed, chunks); - Thread operation = new Thread(new Runnable() { - @Override - public void run() { - try { - synchronized (lock) { - lock.wait(); - } - synchronized (RegionFileCache.class) { - whileLocked.run(); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - }); - TaskManager.IMP.sync(new RunnableVal() { - @Override - public void run(Object value) { - try { - synchronized (RegionFileCache.class) { - operation.start(); // Async - ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - if (unload) unload(allowed, chunks); - 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(); - pool.submit(new Runnable() { - @Override - public void run() { - try { - regionFile.c(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - iter.remove(); - } - pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - pool.shutdown(); - } - synchronized (lock) { - lock.notifyAll(); - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - }); - operation.join(); - - new TaskBuilder().syncWhenFree(new TaskBuilder.SplitTask(5) { - @Override - public Object exec(Object previous) { - ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - for (long pos : chunks) { - int x = (int) pos; - int z = (int) (pos >> 32); - net.minecraft.server.v1_12_R1.Chunk chunk = provider.getOrLoadChunkAt(x, z); - if (chunk != null) { - PlayerChunk pc = getPlayerChunk(nmsWorld, x, z); - sendChunk(pc, chunk, 0); - } - split(); - } - return null; - } - }).buildAsync(); - return true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + return true; } @Override diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java index 1c2ee30c..6c1acb77 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java @@ -15,8 +15,8 @@ import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.world.biome.BaseBiome; import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; -import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -31,7 +31,6 @@ import net.minecraft.server.v1_7_R4.Block; import net.minecraft.server.v1_7_R4.Chunk; import net.minecraft.server.v1_7_R4.ChunkCoordIntPair; import net.minecraft.server.v1_7_R4.ChunkPosition; -import net.minecraft.server.v1_7_R4.ChunkProviderServer; import net.minecraft.server.v1_7_R4.ChunkSection; import net.minecraft.server.v1_7_R4.Entity; import net.minecraft.server.v1_7_R4.EntityPlayer; @@ -46,8 +45,6 @@ import net.minecraft.server.v1_7_R4.NBTTagCompound; import net.minecraft.server.v1_7_R4.NibbleArray; import net.minecraft.server.v1_7_R4.PacketPlayOutMapChunk; import net.minecraft.server.v1_7_R4.PlayerChunkMap; -import net.minecraft.server.v1_7_R4.RegionFile; -import net.minecraft.server.v1_7_R4.RegionFileCache; import net.minecraft.server.v1_7_R4.ServerNBTManager; import net.minecraft.server.v1_7_R4.TileEntity; import net.minecraft.server.v1_7_R4.WorldManager; @@ -133,71 +130,117 @@ public class BukkitQueue17 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.chunkProviderServer; - if (unload) { // Unload chunks - boolean autoSave = world.isAutoSave(); - world.setAutoSave(true); - 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()) { - Chunk chunk = iter.next(); - int cx = chunk.locX; - int cz = chunk.locZ; - if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { - provider.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)); - } - } - for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) provider.unloadChunks(); - world.setAutoSave(autoSave); - } - provider.c(); + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (net.minecraft.server.v1_7_R4.RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + net.minecraft.server.v1_7_R4.ChunkProviderServer provider = nmsWorld.chunkProviderServer; - 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(); + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_7_R4.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + if (chunk.a(false)) { + mustSave = true; + provider.saveChunk(chunk); + provider.saveChunkNOP(chunk); + } + continue; + } iter.remove(); + boolean save = chunk.a(false); + mustSave |= save; + chunk.bukkitChunk.unload(save, false); + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; } } - whileLocked.run(); - // Load the chunks again - if (unload) { - for (Chunk chunk : chunks) { - chunk = provider.loadChunk(chunk.locX, chunk.locZ); - if (chunk != null) { - provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); - sendChunk(chunk, 0); + } + } + if (mustSave) provider.c(); // TODO only the necessary chunks + + File unloadedRegion = null; + if (load && !net.minecraft.server.v1_7_R4.RegionFileCache.a.isEmpty()) { + Map map = net.minecraft.server.v1_7_R4.RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + net.minecraft.server.v1_7_R4.RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); } } + break; } - } - } catch (Throwable e) { - e.printStackTrace(); + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value1) { + net.minecraft.server.v1_7_R4.Chunk chunk = provider.getChunkAt(cx, cz, null); + if (chunk != null) { + if (nmsWorld.getPlayerChunkMap().isChunkInUse(cx, cz)) { + sendChunk(chunk, 0); + } + } + } + }); + } + } + } + } + }); + } } } - }); - return true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + } + }); + return true; } @Override diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java index 26fef159..e882142c 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java @@ -15,8 +15,8 @@ import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.world.biome.BaseBiome; import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; -import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -28,8 +28,6 @@ import java.util.UUID; import net.minecraft.server.v1_8_R3.Block; import net.minecraft.server.v1_8_R3.BlockPosition; import net.minecraft.server.v1_8_R3.Chunk; -import net.minecraft.server.v1_8_R3.ChunkCoordIntPair; -import net.minecraft.server.v1_8_R3.ChunkProviderServer; import net.minecraft.server.v1_8_R3.ChunkSection; import net.minecraft.server.v1_8_R3.Entity; import net.minecraft.server.v1_8_R3.EntityPlayer; @@ -45,8 +43,6 @@ import net.minecraft.server.v1_8_R3.NibbleArray; import net.minecraft.server.v1_8_R3.Packet; import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk; import net.minecraft.server.v1_8_R3.PlayerChunkMap; -import net.minecraft.server.v1_8_R3.RegionFile; -import net.minecraft.server.v1_8_R3.RegionFileCache; import net.minecraft.server.v1_8_R3.ServerNBTManager; import net.minecraft.server.v1_8_R3.TileEntity; import net.minecraft.server.v1_8_R3.WorldData; @@ -132,71 +128,117 @@ public class BukkitQueue18R3 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.chunkProviderServer; - if (unload) { // Unload chunks - boolean autoSave = world.isAutoSave(); - world.setAutoSave(true); - 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()) { - Chunk chunk = iter.next(); - int cx = chunk.locX; - int cz = chunk.locZ; - if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { - provider.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)); - } - } - for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) provider.unloadChunks(); - world.setAutoSave(autoSave); - } - provider.c(); + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (net.minecraft.server.v1_8_R3.RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + net.minecraft.server.v1_8_R3.ChunkProviderServer provider = nmsWorld.chunkProviderServer; - 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(); + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_8_R3.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + if (chunk.a(false)) { + mustSave = true; + provider.saveChunk(chunk); + provider.saveChunkNOP(chunk); + } + continue; + } iter.remove(); + boolean save = chunk.a(false); + mustSave |= save; + chunk.bukkitChunk.unload(save, false); + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; } } - whileLocked.run(); - // Load the chunks again - if (unload) { - for (Chunk chunk : chunks) { - chunk = provider.loadChunk(chunk.locX, chunk.locZ); - if (chunk != null) { - provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); - sendChunk(chunk, 0); + } + } + if (mustSave) provider.c(); // TODO only the necessary chunks + + File unloadedRegion = null; + if (load && !net.minecraft.server.v1_8_R3.RegionFileCache.a.isEmpty()) { + Map map = net.minecraft.server.v1_8_R3.RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + net.minecraft.server.v1_8_R3.RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); } } + break; } - } - } catch (Throwable e) { - e.printStackTrace(); + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value1) { + net.minecraft.server.v1_8_R3.Chunk chunk = provider.getChunkAt(cx, cz, null); + if (chunk != null) { + if (nmsWorld.getPlayerChunkMap().isChunkInUse(cx, cz)) { + sendChunk(chunk, 0); + } + } + } + }); + } + } + } + } + }); + } } } - }); - return true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + } + }); + return true; } @Override diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java index b1c17ecf..a29b49aa 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java @@ -10,7 +10,6 @@ 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 java.util.concurrent.atomic.LongAdder; import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -26,7 +25,6 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -35,10 +33,9 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.LongAdder; import net.minecraft.server.v1_9_R2.Block; import net.minecraft.server.v1_9_R2.BlockPosition; -import net.minecraft.server.v1_9_R2.ChunkCoordIntPair; -import net.minecraft.server.v1_9_R2.ChunkProviderServer; import net.minecraft.server.v1_9_R2.ChunkSection; import net.minecraft.server.v1_9_R2.DataBits; import net.minecraft.server.v1_9_R2.DataPaletteBlock; @@ -58,8 +55,6 @@ import net.minecraft.server.v1_9_R2.PacketPlayOutMapChunk; import net.minecraft.server.v1_9_R2.PacketPlayOutMultiBlockChange; import net.minecraft.server.v1_9_R2.PlayerChunk; import net.minecraft.server.v1_9_R2.PlayerChunkMap; -import net.minecraft.server.v1_9_R2.RegionFile; -import net.minecraft.server.v1_9_R2.RegionFileCache; import net.minecraft.server.v1_9_R2.ServerNBTManager; import net.minecraft.server.v1_9_R2.TileEntity; import net.minecraft.server.v1_9_R2.WorldData; @@ -268,74 +263,122 @@ public class BukkitQueue_1_9_R1 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_9_R2.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_9_R2.Chunk chunk : chunks) { - provider.unload(chunk); - } - boolean autoSave = world.isAutoSave(); - world.setAutoSave(true); - for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) provider.unloadChunks(); - world.setAutoSave(autoSave); - } - provider.c(); + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (net.minecraft.server.v1_9_R2.RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + net.minecraft.server.v1_9_R2.ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); - 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(); + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_9_R2.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + if (chunk.a(false)) { + mustSave = true; + provider.saveChunk(chunk); + provider.saveChunkNOP(chunk); + } + continue; + } iter.remove(); + boolean save = chunk.a(false); + mustSave |= save; + if (save) { + provider.unload(chunk); + } else { + chunk.bukkitChunk.unload(false, false); + } + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; } } - whileLocked.run(); - // Load the chunks again - if (unload) { - for (net.minecraft.server.v1_9_R2.Chunk chunk : chunks) { - chunk = provider.loadChunk(chunk.locX, chunk.locZ); - if (chunk != null) { - provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); - sendChunk(chunk, 0); + } + } + if (mustSave) provider.c(); // TODO only the necessary chunks + + File unloadedRegion = null; + if (load && !net.minecraft.server.v1_9_R2.RegionFileCache.a.isEmpty()) { + Map map = net.minecraft.server.v1_9_R2.RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + net.minecraft.server.v1_9_R2.RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); } } + break; } - } - } catch (Throwable e) { - e.printStackTrace(); + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value1) { + net.minecraft.server.v1_9_R2.Chunk chunk = provider.getChunkAt(cx, cz, null, false); + if (chunk != null) { + net.minecraft.server.v1_9_R2.PlayerChunk pc = nmsWorld.getPlayerChunkMap().getChunk(cx, cz); + if (pc != null && !pc.c.isEmpty()) { + sendChunk(chunk, 0); + } + } + } + }); + } + } + } + } + }); + } } } - }); - return true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + } + }); + return true; } @Override diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index 38e0f58c..2e61da0c 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -12,6 +12,10 @@ commands: description: (FAWE) Cancel your edit aliases: [fawecancel,/fcancel,/cancel,/fawecancel] permissions: + fawe.plotsquared: + default: true + fawe.plotme: + default: true fawe.bypass: default: false fawe.tips: 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 7449a326..ae4ac94f 100644 --- a/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java +++ b/core/src/main/java/com/boydti/fawe/command/AnvilCommands.java @@ -45,7 +45,6 @@ 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.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; @@ -93,9 +92,9 @@ public class AnvilCommands { copy = true; } FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false); - MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky()); + MCAQueue queue = new MCAQueue(defaultQueue); if (copy) { - return queue.filterCopy(filter, true); + return queue.filterCopy(filter, RegionWrapper.GLOBAL()); } else { return queue.filterWorld(filter); } @@ -122,17 +121,8 @@ public class AnvilCommands { 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); + MCAQueue queue = new MCAQueue(tmp); + queue.filterCopy(filter, wrappedRegion); return filter; } @@ -147,7 +137,7 @@ public class AnvilCommands { max = 4 ) @CommandPermissions("worldedit.anvil.replaceall") - public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData, @Switch('f') boolean force) throws WorldEditException { + public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { final FaweBlockMatcher matchFrom; if (from == null) { matchFrom = FaweBlockMatcher.NOT_AIR; @@ -159,7 +149,7 @@ public class AnvilCommands { } final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true)); ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo); - ReplaceSimpleFilter result = runWithWorld(player, folder, filter, force); + ReplaceSimpleFilter result = runWithWorld(player, folder, filter, true); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } @@ -172,7 +162,7 @@ public class AnvilCommands { max = 1 ) @CommandPermissions("worldedit.anvil.remapall") - public void remapall(Player player, String folder, @Switch('f') boolean force) throws WorldEditException { + public void remapall(Player player, String folder) throws WorldEditException { ClipboardRemapper mapper; ClipboardRemapper.RemapPlatform from; ClipboardRemapper.RemapPlatform to; @@ -184,7 +174,7 @@ public class AnvilCommands { to = ClipboardRemapper.RemapPlatform.PC; } RemapFilter filter = new RemapFilter(from, to); - RemapFilter result = runWithWorld(player, folder, filter, force); + RemapFilter result = runWithWorld(player, folder, filter, true); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } @@ -201,10 +191,10 @@ public class AnvilCommands { max = 3 ) @CommandPermissions("worldedit.anvil.deleteallunvisited") - public void deleteAllUnvisited(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileDurationMillis, @Switch('f') boolean force) throws WorldEditException { + public void deleteAllUnvisited(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileDurationMillis) throws WorldEditException { long chunkInactivityMillis = fileDurationMillis; // Use same value for now DeleteUninhabitedFilter filter = new DeleteUninhabitedFilter(fileDurationMillis, inhabitedTicks, chunkInactivityMillis); - DeleteUninhabitedFilter result = runWithWorld(player, folder, filter, force); + DeleteUninhabitedFilter result = runWithWorld(player, folder, filter, true); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } @@ -220,10 +210,10 @@ public class AnvilCommands { max = 3 ) @CommandPermissions("worldedit.anvil.deletealloldregions") - public void deleteAllOldRegions(Player player, String folder, String time, @Switch('f') boolean force) throws WorldEditException { + public void deleteAllOldRegions(Player player, String folder, String time) throws WorldEditException { long duration = MainUtil.timeToSec(time) * 1000l; DeleteOldFilter filter = new DeleteOldFilter(duration); - DeleteOldFilter result = runWithWorld(player, folder, filter, force); + DeleteOldFilter result = runWithWorld(player, folder, filter, true); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } @@ -253,7 +243,7 @@ public class AnvilCommands { max = 4 ) @CommandPermissions("worldedit.anvil.replaceall") - public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap, @Switch('f') boolean force) throws WorldEditException { + public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { MCAFilterCounter filter; if (useMap) { if (to instanceof RandomPattern) { @@ -272,7 +262,7 @@ public class AnvilCommands { } filter = new ReplacePatternFilter(matchFrom, to); } - MCAFilterCounter result = runWithWorld(player, folder, filter, force); + MCAFilterCounter result = runWithWorld(player, folder, filter, true); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } @@ -285,7 +275,7 @@ public class AnvilCommands { max = 3 ) @CommandPermissions("worldedit.anvil.countall") - public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData, @Switch('f') boolean force) throws WorldEditException { + public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException { Set searchBlocks = worldEdit.getBlocks(player, arg, true); MCAFilterCounter filter; if (useData || arg.contains(":")) { // Optimize for both cases @@ -297,7 +287,7 @@ public class AnvilCommands { searchBlocks.forEach(counter::addBlock); filter = counter; } - MCAFilterCounter result = runWithWorld(player, folder, filter, force); + MCAFilterCounter result = runWithWorld(player, folder, filter, true); if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal())); } @@ -539,8 +529,7 @@ public class AnvilCommands { 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()); + MCAQueue queue = new MCAQueue(tmp); Vector origin = session.getPlacementPosition(player); MCAClipboard clipboard = new MCAClipboard(queue, cuboid, origin); FawePlayer fp = FawePlayer.wrap(player); @@ -558,7 +547,7 @@ public class AnvilCommands { ) @CommandPermissions("worldedit.anvil.pastechunks") - public void paste(Player player, LocalSession session, EditSession editSession, @Switch('c') boolean alignChunk) throws WorldEditException { + public void paste(Player player, LocalSession session, EditSession editSession, @Switch('c') boolean alignChunk) throws WorldEditException, IOException { FawePlayer fp = FawePlayer.wrap(player); MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD); if (clipboard == null) { @@ -576,27 +565,10 @@ public class AnvilCommands { 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); + MCAQueue pasteQueue = new MCAQueue(tmpTo); + + pasteQueue.pasteRegion(copyQueue, copyRegion, offset); + BBC.COMMAND_PASTE.send(player, player.getPosition()); } } \ No newline at end of file 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 29b43be2..4b1eef91 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 @@ -460,6 +460,11 @@ public class MCAFile { public void flush(ForkJoinPool pool) { synchronized (raf) { + if (isDeleted()) { + clear(); + file.delete(); + return; + } boolean wait; if (pool == null) { wait = true; @@ -500,6 +505,7 @@ public class MCAFile { } } if (modified) { + file.setLastModified(now); forEachChunk(new RunnableVal4() { @Override public void run(Integer cx, Integer cz, Integer offset, Integer size) { diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFilter.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFilter.java index 21b28366..3a89a782 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFilter.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFilter.java @@ -4,6 +4,8 @@ import com.boydti.fawe.object.collection.IterableThreadLocal; import com.sk89q.worldedit.blocks.BaseBlock; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.ForkJoinPool; /** * MCAQueue.filterWorld(MCAFilter)
@@ -11,6 +13,10 @@ import java.nio.file.attribute.BasicFileAttributes; */ public class MCAFilter extends IterableThreadLocal { + public void withPool(ForkJoinPool pool, MCAQueue queue) { + return; + } + public boolean appliesFile(Path path, BasicFileAttributes attr) { return true; } 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 8c8d935c..ab7cc1a7 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 @@ -9,7 +9,6 @@ import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.RegionWrapper; -import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.RunnableVal4; import com.boydti.fawe.object.collection.IterableThreadLocal; @@ -21,6 +20,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collection; @@ -146,6 +146,12 @@ public class MCAQueue extends NMSMappedFaweQueue> 4; int oCZ = oZ >> 4; RegionWrapper regionTo = new RegionWrapper(regionFrom.minX + oX, regionFrom.maxX + oX, regionFrom.minZ + oZ, regionFrom.maxZ + oZ); + File folder = getSaveFolder(); - final ForkJoinPool pool = new ForkJoinPool(); int bMcaX = (regionTo.minX >> 9); int bMcaZ = (regionTo.minZ >> 9); int tMcaX = (regionTo.maxX >> 9); int tMcaZ = (regionTo.maxZ >> 9); - for (int mcaZ = bMcaZ; mcaZ <= tMcaZ; mcaZ++) { - for (int mcaX = bMcaX; mcaX <= tMcaX; mcaX++) { - int bcx = Math.max(mcaX << 5, regionTo.minX >> 4); - int bcz = Math.max(mcaZ << 5, regionTo.minZ >> 4); - int tcx = Math.min((mcaX << 5) + 31, regionTo.maxX >> 4); - int tcz = Math.min((mcaZ << 5) + 31, regionTo.maxZ >> 4); - File file = new File(folder, "r." + mcaX + "." + mcaZ + ".mca"); - if (!file.exists()) { - file.createNewFile(); - } - MCAFile mcaFile = new MCAFile(null, file); - mcaFile.init(); - final long heapSize = Runtime.getRuntime().totalMemory(); - final long heapMaxSize = Runtime.getRuntime().maxMemory(); - int free = (int) (((heapMaxSize - heapSize) + Runtime.getRuntime().freeMemory()) / (1024 * 1024)); + filterCopy(new MCAFilter() { + @Override + public MCAFile applyFile(MCAFile mcaFile) { + try { + int mcaX = mcaFile.getX(); + int mcaZ = mcaFile.getZ(); + int bcx = Math.max(mcaX << 5, regionTo.minX >> 4); + int bcz = Math.max(mcaZ << 5, regionTo.minZ >> 4); + int tcx = Math.min((mcaX << 5) + 31, regionTo.maxX >> 4); + int tcz = Math.min((mcaZ << 5) + 31, regionTo.maxZ >> 4); + mcaFile.init(); + + final long heapSize = Runtime.getRuntime().totalMemory(); + final long heapMaxSize = Runtime.getRuntime().maxMemory(); + int free = (int) (((heapMaxSize - heapSize) + Runtime.getRuntime().freeMemory()) / (1024 * 1024)); // int obcx = bcx - oCX; // int obcz = bcz - oCX; // int otcx = tcx - oCX; // int otcz = tcz - oCX; - for (int cz = bcz; cz <= tcz; cz++) { - for (int cx = bcx; cx <= tcx; cx++) { - int bx = cx << 4; - int bz = cz << 4; - int tx = bx + 15; - int tz = bz + 15; - if (oX == 0 && oZ == 0) { - if (bx >= regionTo.minX && tx <= regionTo.maxX && bz >= regionTo.minZ && tz <= regionTo.maxZ) { - FaweChunk chunk = from.getFaweChunk(cx - oCX, cz - oCZ); - if (!(chunk instanceof NullFaweChunk)) { - if (regionTo.minY == 0 && regionTo.maxY == 255) { - MCAChunk mcaChunk = (MCAChunk) chunk; - mcaChunk.setLoc(null, cx, cz); - mcaChunk.setModified(); - mcaFile.setChunk(mcaChunk); - } else { - MCAChunk newChunk = mcaFile.getChunk(cx, cz); - if (newChunk == null) { - newChunk = new MCAChunk(this, cx, cz); - mcaFile.setChunk(newChunk); + for (int cz = bcz; cz <= tcz; cz++) { + for (int cx = bcx; cx <= tcx; cx++) { + int bx = cx << 4; + int bz = cz << 4; + int tx = bx + 15; + int tz = bz + 15; + if (oX == 0 && oZ == 0) { + if (bx >= regionTo.minX && tx <= regionTo.maxX && bz >= regionTo.minZ && tz <= regionTo.maxZ) { + FaweChunk chunk = from.getFaweChunk(cx - oCX, cz - oCZ); + if (!(chunk instanceof NullFaweChunk)) { + if (regionTo.minY == 0 && regionTo.maxY == 255) { + MCAChunk mcaChunk = (MCAChunk) chunk; + mcaChunk.setLoc(null, cx, cz); + mcaChunk.setModified(); + mcaFile.setChunk(mcaChunk); } else { - newChunk.setModified(); + MCAChunk newChunk = mcaFile.getChunk(cx, cz); + if (newChunk == null) { + newChunk = new MCAChunk(MCAQueue.this, cx, cz); + mcaFile.setChunk(newChunk); + } else { + newChunk.setModified(); + } + newChunk.copyFrom((MCAChunk) chunk, regionFrom.minY, regionFrom.maxY, oY); } - newChunk.copyFrom((MCAChunk) chunk, regionFrom.minY, regionFrom.maxY, oY); } - } - continue; - } - } - bx = Math.max(regionTo.minX, bx); - bz = Math.max(regionTo.minZ, bz); - tx = Math.min(regionTo.maxX, tx); - tz = Math.min(regionTo.maxZ, tz); - int obx = bx - oX; - int obz = bz - oZ; - int otx = tx - oX; - int otz = tz - oZ; - int otherBCX = (obx) >> 4; - int otherBCZ = (obz) >> 4; - int otherTCX = (otx) >> 4; - int otherTCZ = (otz) >> 4; - MCAChunk newChunk = mcaFile.getChunk(cx, cz); - boolean created; - if (newChunk == null) { - newChunk = new MCAChunk(this, cx, cz); - created = true; - } else { - created = false; - newChunk.setModified(); - } - boolean modified = false; - int cbx = (cx << 4) - oX; - int cbz = (cz << 4) - oZ; - for (int otherCZ = otherBCZ; otherCZ <= otherTCZ; otherCZ++) { - for (int otherCX = otherBCX; otherCX <= otherTCX; otherCX++) { - FaweChunk chunk = from.getFaweChunk(otherCX, otherCZ); - if (!(chunk instanceof NullFaweChunk)) { - MCAChunk other = (MCAChunk) chunk; - int ocbx = otherCX << 4; - int ocbz = otherCZ << 4; - int octx = ocbx + 15; - int octz = ocbz + 15; - int minY = regionFrom.minY; - int maxY = regionFrom.maxY; - int offsetY = oY; - int minX = obx > ocbx ? (obx - ocbx) & 15 : 0; - int maxX = otx < octx ? (otx - ocbx) : 15; - int minZ = obz > ocbz ? (obz - ocbz) & 15 : 0; - int maxZ = otz < octz ? (otz - ocbz) : 15; - int offsetX = ocbx - cbx; - int offsetZ = ocbz - cbz; - newChunk.copyFrom(other, minX, maxX, minY, maxY, minZ, maxZ, offsetX, offsetY, offsetZ); - newChunk.setModified(); - modified = true; + continue; } } - } - if (created && modified) { - mcaFile.setChunk(newChunk); - } - } - } - pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - mcaFile.close(pool); - from.clear(); - } - } - from.clear(); - pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - pool.shutdown(); - } - - public > T filterCopy(final T filter, boolean deleteOnCopyFail) { - this.filterWorld(new DelegateMCAFilter(filter) { - @Override - public MCAFile applyFile(MCAFile mca) { - File file = mca.getFile(); - File copyDest = new File(file.getParentFile(), file.getName() + "-copy"); - try { - Files.copy(file.toPath(), copyDest.toPath(), StandardCopyOption.REPLACE_EXISTING); - MCAFile copy = new MCAFile(mca.getParent(), copyDest); - MCAFile result = filter.applyFile(copy); - if (result == null) { - if (copy.isDeleted()) { - copy.clear(); - result.clear(); - if (file.exists()) { - file.delete(); + bx = Math.max(regionTo.minX, bx); + bz = Math.max(regionTo.minZ, bz); + tx = Math.min(regionTo.maxX, tx); + tz = Math.min(regionTo.maxZ, tz); + int obx = bx - oX; + int obz = bz - oZ; + int otx = tx - oX; + int otz = tz - oZ; + int otherBCX = (obx) >> 4; + int otherBCZ = (obz) >> 4; + int otherTCX = (otx) >> 4; + int otherTCZ = (otz) >> 4; + MCAChunk newChunk = mcaFile.getChunk(cx, cz); + boolean created; + if (newChunk == null) { + newChunk = new MCAChunk(MCAQueue.this, cx, cz); + created = true; + } else { + created = false; + newChunk.setModified(); } - if (copyDest.exists()) { - if (!copyDest.delete()) { - copyDest.deleteOnExit(); - } - } - } else if (copy.isModified()) { - if (copyDest.exists()) { - copy.clear(); - file.delete(); - if (!copyDest.renameTo(file) && deleteOnCopyFail) { - if (!copyDest.delete()) { - copyDest.deleteOnExit(); + boolean modified = false; + int cbx = (cx << 4) - oX; + int cbz = (cz << 4) - oZ; + for (int otherCZ = otherBCZ; otherCZ <= otherTCZ; otherCZ++) { + for (int otherCX = otherBCX; otherCX <= otherTCX; otherCX++) { + FaweChunk chunk = from.getFaweChunk(otherCX, otherCZ); + if (!(chunk instanceof NullFaweChunk)) { + MCAChunk other = (MCAChunk) chunk; + int ocbx = otherCX << 4; + int ocbz = otherCZ << 4; + int octx = ocbx + 15; + int octz = ocbz + 15; + int minY = regionFrom.minY; + int maxY = regionFrom.maxY; + int offsetY = oY; + int minX = obx > ocbx ? (obx - ocbx) & 15 : 0; + int maxX = otx < octx ? (otx - ocbx) : 15; + int minZ = obz > ocbz ? (obz - ocbz) & 15 : 0; + int maxZ = otz < octz ? (otz - ocbz) : 15; + int offsetX = ocbx - cbx; + int offsetZ = ocbz - cbz; + newChunk.copyFrom(other, minX, maxX, minY, maxY, minZ, maxZ, offsetX, offsetY, offsetZ); + newChunk.setModified(); + modified = true; } } } - } else { - copy.clear(); - if (!copyDest.delete()) { - copyDest.deleteOnExit(); + if (created && modified) { + mcaFile.setChunk(newChunk); } } } - return result; + from.clear(); } catch (IOException e) { e.printStackTrace(); - return null; + } + return null; + } + }, regionTo); + from.clear(); + } + + private void performCopy(MCAFile original, MCAFile copy, RegionWrapper region, ForkJoinPool pool) { + original.clear(); + File originalFile = original.getFile(); + File copyFile = copy.getFile(); + if (copy.isModified()) { + if (copy.isDeleted()) { + if (originalFile.delete()) return; + setMCA(original.getX(), original.getZ(), region, () -> originalFile.delete(), true); + return; + } else if (copyFile.exists()) { + try { + copy.close(pool); + Files.move(copyFile.toPath(), originalFile.toPath(), StandardCopyOption.ATOMIC_MOVE); + } catch (IOException e) { + setMCA(original.getX(), original.getZ(), region, () -> { + originalFile.delete(); + if (!copyFile.renameTo(originalFile)) { + Fawe.debug("Failed to copy (2)"); + } + }, true); } } - }, true, new RunnableVal() { + } + copy.clear(); + copyFile.delete(); + } + + public > T filterCopy(final T filter, RegionWrapper region) { + DelegateMCAFilter delegate = new DelegateMCAFilter(filter) { + MCAFile original; + MCAFile copy; + ForkJoinPool pool; + @Override - public void run(MCAFile value) { - if (deleteOnCopyFail) { - File file = value.getFile(); - boolean result = file.delete(); - if (!result) { - file.deleteOnExit(); - } - Fawe.debug("Deleted " + file + " = " + result); - } + public void withPool(ForkJoinPool pool, MCAQueue queue) { + this.pool = pool; } - }); + + @Override + public MCAFile applyFile(MCAFile original) { + this.original = original; + this.original.clear(); + File file = original.getFile(); + file.setWritable(true); + File copyDest = new File(file.getParentFile(), file.getName() + "-copy"); + setMCA(original.getX(), original.getZ(), region, () -> { + try { + Files.copy(file.toPath(), copyDest.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + e.printStackTrace(); + } + }, false); + this.copy = new MCAFile(original.getParent(), copyDest); + MCAFile result = filter.applyFile(copy); + if (result == null) { + performCopy(original, copy, region, pool); + } + if (result == null || !copy.getFile().equals(result.getFile())) { + copy.clear(); + if (copyDest.exists() && !copyDest.delete()) copyDest.deleteOnExit(); + } + return result; + } + + @Override + public void finishFile(MCAFile newRegion, G cache) { + performCopy(original, newRegion, region, pool); + } + }; + if (region == RegionWrapper.GLOBAL()) { + this.filterWorld(delegate); + } else { + this.filterRegion(delegate, region); + } return filter; } public > T filterRegion(final T filter, final RegionWrapper region) { - this.filterWorld(new DelegateMCAFilter(filter) { + DelegateMCAFilter delegate = new DelegateMCAFilter(filter) { @Override public boolean appliesFile(Path path, BasicFileAttributes attr) { - String name = path.getFileName().toString(); - String[] split = name.split("\\."); - final int mcaX = Integer.parseInt(split[1]); - final int mcaZ = Integer.parseInt(split[2]); + String name = path.toString(); + int[] coords = MainUtil.regionNameToCoords(name); + final int mcaX = coords[0]; + final int mcaZ = coords[1]; return region.isInMCA(mcaX, mcaZ) && filter.appliesFile(path, attr); } @@ -403,7 +425,34 @@ public class MCAQueue extends NMSMappedFaweQueue> 9; + final int minMCAZ = region.minZ >> 9; + final int maxMCAX = region.maxX >> 9; + final int maxMCAZ = region.maxZ >> 9; + long mcaArea = (maxMCAX - minMCAX + 1l) * (maxMCAZ - minMCAZ + 1l); + if (mcaArea < 128) { + this.filterWorld(delegate, new RunnableVal2>() { + @Override + public void run(Path root, RunnableVal2 funx) { + for (int x = minMCAX; x <= maxMCAX; x++) { + for (int z = minMCAZ; z <= maxMCAZ; z++) { + Path newPath = root.resolve(Paths.get("r." + x + "." + z + ".mca")); + if (Files.exists(newPath)) { + try { + BasicFileAttributes attrs = Files.readAttributes(newPath, BasicFileAttributes.class); + funx.run(newPath, attrs); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + } + }); + } else { + this.filterWorld(delegate); + } return filter; } @@ -420,14 +469,8 @@ public class MCAQueue extends NMSMappedFaweQueue> T filterWorld(final T filter) { - return filterWorld(filter, false, null); - } - - private > T filterWorld(final T filter, boolean replaceOriginalOnCopy, RunnableVal onReplaceFail) { - File folder = getSaveFolder(); - final ForkJoinPool pool = new ForkJoinPool(); - MainUtil.traverse(folder.toPath(), new RunnableVal2() { + private > RunnableVal2 filterFunction(final T filter, ForkJoinPool pool) { + return new RunnableVal2() { @Override public void run(Path path, BasicFileAttributes attr) { try { @@ -443,7 +486,6 @@ public class MCAQueue extends NMSMappedFaweQueue> T filterWorld(final T filter, RunnableVal2> traverser) { + File folder = getSaveFolder(); + final ForkJoinPool pool = new ForkJoinPool(); + filter.withPool(pool, this); + RunnableVal2 task = filterFunction(filter, pool); + traverser.run(folder.toPath(), task); pool.shutdown(); try { pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); @@ -559,6 +584,15 @@ public class MCAQueue extends NMSMappedFaweQueue> T filterWorld(final T filter) { + return filterWorld(filter, new RunnableVal2>() { + @Override + public void run(Path value1, RunnableVal2 value2) { + MainUtil.traverse(value1, value2); + } + }); + } + @Override public void relight(int x, int y, int z) { throw new UnsupportedOperationException("Not supported"); diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java index c450bd21..1f62e7b4 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java @@ -5,9 +5,11 @@ import com.boydti.fawe.example.MappedFaweQueue; import com.boydti.fawe.example.NullFaweChunk; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.SetQueue; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -49,6 +51,7 @@ public class MCAQueueMap implements IFaweQueueMap { lastFile = tmp = mcaFileMap.get(pair); if (lastFile == null) { try { + queue.setMCA(lastFileX, lastFileZ, RegionWrapper.GLOBAL(), null, false); lastFile = tmp = new MCAFile(queue, lastFileX, lastFileZ); } catch (FaweException.FaweChunkLoadException ignore) { lastFile = null; @@ -93,7 +96,8 @@ public class MCAQueueMap implements IFaweQueueMap { lastX = cx; lastZ = cz; if (isHybridQueue) { - lastChunk = ((MappedFaweQueue) queue).getFaweQueueMap().getCachedFaweChunk(cx, cz); + MappedFaweQueue mfq = ((MappedFaweQueue) queue); + lastChunk = mfq.getFaweQueueMap().getCachedFaweChunk(cx, cz); if (lastChunk != null) { return lastChunk; } @@ -175,7 +179,12 @@ public class MCAQueueMap implements IFaweQueueMap { if (result = iter.hasNext()) { MCAFile file = iter.next().getValue(); iter.remove(); - file.close(null); + queue.setMCA(file.getX(), file.getZ(), RegionWrapper.GLOBAL(), new Runnable() { + @Override + public void run() { + file.close(SetQueue.IMP.getForkJoinPool()); + } + }, true); } else { break; } diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/DelegateMCAFilter.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/DelegateMCAFilter.java index 8131cbe3..30578377 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/DelegateMCAFilter.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/filters/DelegateMCAFilter.java @@ -3,15 +3,22 @@ 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.jnbt.anvil.MCAFilter; +import com.boydti.fawe.jnbt.anvil.MCAQueue; import com.sk89q.worldedit.blocks.BaseBlock; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.Spliterator; +import java.util.concurrent.ForkJoinPool; import java.util.function.Consumer; public class DelegateMCAFilter extends MCAFilter { private final MCAFilter filter; + @Override + public void withPool(ForkJoinPool pool, MCAQueue queue) { + filter.withPool(pool, queue); + } + @Override public boolean appliesFile(Path path, BasicFileAttributes attr) { return filter.appliesFile(path, attr); diff --git a/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java b/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java index de299d8f..0670d0fe 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java @@ -42,6 +42,18 @@ public class FaweInputStream extends DataInputStream { return nbtIn.readNamedTag(); } + public int readVarInt() throws IOException { + int i = 0; + int offset = 0; + int b; + while ((b = read()) > 127) { + i |= (b - 128) << offset; + offset += 7; + } + i |= b << offset; + return i; + } + @Override public void close() throws IOException { if (nbtIn != null) { diff --git a/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java b/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java index 35c3314d..5abdfc95 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java @@ -41,7 +41,6 @@ public class FaweOutputStream extends DataOutputStream { this.writeByte(i & 127 | 128); i >>>= 7; } - this.writeByte(i); } diff --git a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java index 6129add6..f6e1d3d8 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java @@ -285,8 +285,9 @@ public abstract class FaweQueue implements HasFaweQueue, Extent { public abstract Collection getFaweChunks(); - public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) { - return false; + public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean load) { + if (whileLocked != null) whileLocked.run(); + return true; } public abstract void setChunk(final FaweChunk chunk); diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java index 011f5a77..1f526a14 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent; import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.World; @@ -17,6 +18,17 @@ public class ResettableExtent extends AbstractDelegateExtent implements Serializ super(parent); } + public final void init(Vector pos) { + if (getExtent() instanceof ResettableExtent) { + ((ResettableExtent) getExtent()).init(pos); + } + setOrigin(pos); + } + + protected void setOrigin(Vector pos) { + + } + public ResettableExtent setExtent(Extent extent) { checkNotNull(extent); Extent next = getExtent(); diff --git a/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java index 979a5d68..5a1cebc1 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java @@ -28,6 +28,21 @@ public class TransformExtent extends BlockTransformExtent { return super.setExtent(extent); } + @Override + public Vector getMinimumPoint() { + Vector pos1 = new MutableBlockVector(getPos(super.getMinimumPoint())); + Vector pos2 = new MutableBlockVector(getPos(super.getMaximumPoint())); + return Vector.getMinimum(pos1, pos2); + } + + @Override + public Vector getMaximumPoint() { + Vector pos1 = new MutableBlockVector(getPos(super.getMinimumPoint())); + Vector pos2 = new MutableBlockVector(getPos(super.getMaximumPoint())); + return Vector.getMaximum(pos1, pos2); + } + + @Override public void setOrigin(Vector pos) { this.min = pos; } diff --git a/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java b/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java index 8c9477be..cb9c3c16 100644 --- a/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java @@ -45,8 +45,8 @@ public class DelegateFaweQueue extends FaweQueue { } @Override - public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) { - return parent.setMCA(whileLocked, region, unload); + public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean load) { + return parent.setMCA(mcaX, mcaZ, region, whileLocked, load); } @Override 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 aac58113..9db378fc 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -791,6 +791,41 @@ public class MainUtil { } } + public static int[] regionNameToCoords(String fileName) { + int[] res = new int[2]; + int len = fileName.length() - 4; + int val = 0; + boolean neg = false; + boolean reading = false; + int index = 1; + int numIndex = 1; + outer: + for (int i = len; i >= 2; i--) { + char c = fileName.charAt(i); + if (!reading) { + reading = (c == '.'); + continue; + } + switch (c) { + case '-': + val = -val; + break; + case '.': + res[index--] = val; + if (index == -1) return res; + val = 0; + numIndex = 1; + break; + default: + val = val + (c - 48) * numIndex; + numIndex *= 10; + break; + } + } + res[index] = val; + return res; + } + public static boolean isInSubDirectory(File dir, File file) { if (file == null) return false; if (file.equals(dir)) return true; diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index bb614afa..f88bef9f 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -700,6 +700,19 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } } + public @Nullable ResettableExtent getTransform() { + ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); + if (traverser != null) { + return (ResettableExtent) traverser.get(); + } + return null; + } + + private void initTransform(Vector pos) { + ResettableExtent tfx = getTransform(); + if (tfx != null) tfx.init(pos); + } + /** * Set a mask. * @@ -957,10 +970,6 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting @Override public BaseBlock getLazyBlock(final Vector position) { - if (position.getY() > maxY || position.getY() < 0) { - if (!limit.MAX_FAILS()) throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); - return nullBlock; - } return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ()); } @@ -974,10 +983,6 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting @Override public BaseBlock getBlock(final Vector position) { - if (position.getY() > maxY || position.getY() < 0) { - if (!limit.MAX_FAILS()) throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); - return nullBlock; - } return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ()); } @@ -1275,8 +1280,8 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting @Override public Vector getMinimumPoint() { - if (getWorld() != null) { - return this.getWorld().getMinimumPoint(); + if (extent != null) { + return this.extent.getMinimumPoint(); } else { return new Vector(-30000000, 0, -30000000); } @@ -1284,8 +1289,8 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting @Override public Vector getMaximumPoint() { - if (getWorld() != null) { - return this.getWorld().getMaximumPoint(); + if (extent != null) { + return this.extent.getMaximumPoint(); } else { return new Vector(30000000, 255, 30000000); } @@ -1471,9 +1476,9 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting checkNotNull(pattern); checkArgument(radius >= 0, "radius >= 0"); checkArgument(depth >= 1, "depth >= 1"); - + initTransform(origin); final MaskIntersection mask = new MaskIntersection(new RegionMask(new EllipsoidRegion(null, origin, new Vector(radius, radius, radius))), new BoundedHeightMask(Math.max( - (origin.getBlockY() - depth) + 1, 0), Math.min(EditSession.this.getMaximumPoint().getBlockY(), origin.getBlockY())), Masks.negate(new ExistingBlockMask(EditSession.this))); + (origin.getBlockY() - depth) + 1, getMinimumPoint().getBlockY()), Math.min(getMaximumPoint().getBlockY(), origin.getBlockY())), Masks.negate(new ExistingBlockMask(EditSession.this))); // Want to replace blocks final BlockReplace replace = new BlockReplace(EditSession.this, pattern); diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java index df67109a..cb4f0b81 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java @@ -53,9 +53,7 @@ public class DownwardVisitor extends RecursiveVisitor { public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) { super(mask, function, depth, hasFaweQueue); checkNotNull(mask); - this.baseY = baseY; - final Collection directions = this.getDirections(); directions.clear(); directions.add(new Vector(1, 0, 0)); @@ -68,7 +66,7 @@ public class DownwardVisitor extends RecursiveVisitor { @Override public boolean isVisitable(final Vector from, final Vector to) { final int fromY = from.getBlockY(); - return ((fromY == this.baseY) || (to.subtract(from).getBlockY() < 0)) && super.isVisitable(from, to); + return ((fromY == this.baseY) || (to.getBlockY() - from.getBlockY() < 0)) && super.isVisitable(from, to); } public static Class inject() {