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
This commit is contained in:
Jesse Boyd 2017-08-08 17:36:17 +10:00
parent 755103a558
commit 71306cb749
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
22 changed files with 1037 additions and 741 deletions

View File

@ -26,7 +26,6 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.BiomeCache;
import net.minecraft.server.v1_10_R1.Block; import net.minecraft.server.v1_10_R1.Block;
import net.minecraft.server.v1_10_R1.BlockPosition; 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.ChunkProviderGenerate;
import net.minecraft.server.v1_10_R1.ChunkProviderServer; import net.minecraft.server.v1_10_R1.ChunkProviderServer;
import net.minecraft.server.v1_10_R1.ChunkSection; 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.PacketPlayOutMultiBlockChange;
import net.minecraft.server.v1_10_R1.PlayerChunk; import net.minecraft.server.v1_10_R1.PlayerChunk;
import net.minecraft.server.v1_10_R1.PlayerChunkMap; 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.ServerNBTManager;
import net.minecraft.server.v1_10_R1.TileEntity; import net.minecraft.server.v1_10_R1.TileEntity;
import net.minecraft.server.v1_10_R1.WorldChunkManager; import net.minecraft.server.v1_10_R1.WorldChunkManager;
@ -406,75 +402,122 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<net.minecraft.server.v1_10_R
} }
@Override @Override
public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) { public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
try { TaskManager.IMP.sync(new RunnableVal<Boolean>() {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override @Override
public void run(Object value) { public void run(Boolean value) {
try { long start = System.currentTimeMillis();
synchronized (RegionFileCache.class) { long last = start;
ArrayDeque<net.minecraft.server.v1_10_R1.Chunk> chunks = new ArrayDeque<>(); synchronized (net.minecraft.server.v1_10_R1.RegionFileCache.class) {
World world = getWorld(); World world = getWorld();
world.setKeepSpawnInMemory(false); if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); net.minecraft.server.v1_10_R1.ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
if (unload) { // Unload chunks
int bcx = (allowed.minX >> 9) << 5; boolean mustSave = false;
int bcz = (allowed.minZ >> 9) << 5; boolean[][] chunksUnloaded = null;
int tcx = 31 + (allowed.maxX >> 9) << 5; { // Unload chunks
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<net.minecraft.server.v1_10_R1.Chunk> iter = provider.a().iterator(); Iterator<net.minecraft.server.v1_10_R1.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
net.minecraft.server.v1_10_R1.Chunk chunk = iter.next(); net.minecraft.server.v1_10_R1.Chunk chunk = iter.next();
int cx = chunk.locX; if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) {
int cz = chunk.locZ; boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { if (isIn) {
chunks.add(chunk); if (!load) {
if (chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
} }
continue;
} }
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<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove(); 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) { if (mustSave) provider.c(); // TODO only the necessary chunks
for (net.minecraft.server.v1_10_R1.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ); File unloadedRegion = null;
if (chunk != null) { if (load && !net.minecraft.server.v1_10_R1.RegionFileCache.a.isEmpty()) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); Map<File, net.minecraft.server.v1_10_R1.RegionFile> map = net.minecraft.server.v1_10_R1.RegionFileCache.a;
sendChunk(chunk, 0); Iterator<Map.Entry<File, net.minecraft.server.v1_10_R1.RegionFile>> iter = map.entrySet().iterator();
String requiredPath = world.getName() + File.separator + "region";
while (iter.hasNext()) {
Map.Entry<File, net.minecraft.server.v1_10_R1.RegionFile> 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<Object>() {
@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; return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} }
@Override @Override

View File

@ -26,7 +26,6 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.NBTTagCompound;
import net.minecraft.server.v1_11_R1.NibbleArray; import net.minecraft.server.v1_11_R1.NibbleArray;
import net.minecraft.server.v1_11_R1.PacketDataSerializer; 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.PacketPlayOutMultiBlockChange;
import net.minecraft.server.v1_11_R1.PlayerChunk;
import net.minecraft.server.v1_11_R1.PlayerChunkMap; 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.ServerNBTManager;
import net.minecraft.server.v1_11_R1.TileEntity; import net.minecraft.server.v1_11_R1.TileEntity;
import net.minecraft.server.v1_11_R1.WorldChunkManager; import net.minecraft.server.v1_11_R1.WorldChunkManager;
@ -252,70 +247,131 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
return super.regenerateChunk(world, x, z, biome, seed); return super.regenerateChunk(world, x, z, biome, seed);
} }
private net.minecraft.server.v1_11_R1.PlayerChunk getPlayerChunk(net.minecraft.server.v1_11_R1.WorldServer w, int cx, int cz) {
net.minecraft.server.v1_11_R1.PlayerChunkMap chunkMap = w.getPlayerChunkMap();
net.minecraft.server.v1_11_R1.PlayerChunk playerChunk = chunkMap.getChunk(cx, cz);
if (playerChunk == null) {
return null;
}
if (playerChunk.c.isEmpty()) {
return null;
}
return playerChunk;
}
@Override @Override
public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) { public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
try { TaskManager.IMP.sync(new RunnableVal<Boolean>() {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override @Override
public void run(Object value) { public void run(Boolean value) {
try { long start = System.currentTimeMillis();
synchronized (RegionFileCache.class) { long last = start;
ArrayDeque<net.minecraft.server.v1_11_R1.Chunk> chunks = new ArrayDeque<>(); synchronized (net.minecraft.server.v1_11_R1.RegionFileCache.class) {
World world = getWorld(); World world = getWorld();
world.setKeepSpawnInMemory(false); if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); net.minecraft.server.v1_11_R1.ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
if (unload) { // Unload chunks
int bcx = (allowed.minX >> 9) << 5; boolean mustSave = false;
int bcz = (allowed.minZ >> 9) << 5; boolean[][] chunksUnloaded = null;
int tcx = 31 + (allowed.maxX >> 9) << 5; { // Unload chunks
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<net.minecraft.server.v1_11_R1.Chunk> iter = provider.a().iterator(); Iterator<net.minecraft.server.v1_11_R1.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
net.minecraft.server.v1_11_R1.Chunk chunk = iter.next(); net.minecraft.server.v1_11_R1.Chunk chunk = iter.next();
int cx = chunk.locX; if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) {
int cz = chunk.locZ; boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { if (isIn) {
chunks.add(chunk); if (!load) {
if (chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
} }
continue;
} }
for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) {
provider.unloadChunk(chunk, true);
}
}
provider.c();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove(); 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) { if (mustSave) provider.c(); // TODO only the necessary chunks
for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) {
chunk = provider.getChunkAt(chunk.locX, chunk.locZ, null, false); File unloadedRegion = null;
if (chunk != null) { if (load && !net.minecraft.server.v1_11_R1.RegionFileCache.a.isEmpty()) {
sendChunk(chunk, 0); Map<File, net.minecraft.server.v1_11_R1.RegionFile> map = net.minecraft.server.v1_11_R1.RegionFileCache.a;
Iterator<Map.Entry<File, net.minecraft.server.v1_11_R1.RegionFile>> iter = map.entrySet().iterator();
String requiredPath = world.getName() + File.separator + "region";
while (iter.hasNext()) {
Map.Entry<File, net.minecraft.server.v1_11_R1.RegionFile> 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;
} }
} }
} }
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<Object>() {
@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);
}
}
}
});
}
}
}
}
});
}
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} }
}); });
return true; return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} }
@Override @Override
@ -474,7 +530,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
public void sendChunk(int x, int z, int bitMask) { public void sendChunk(int x, int z, int bitMask) {
net.minecraft.server.v1_11_R1.Chunk chunk = getCachedChunk(getWorld(), x, z); net.minecraft.server.v1_11_R1.Chunk chunk = getCachedChunk(getWorld(), x, z);
if (chunk != null) { if (chunk != null) {
sendChunk(chunk, bitMask); sendChunk(getPlayerChunk((net.minecraft.server.v1_11_R1.WorldServer) chunk.getWorld(), chunk.locX, chunk.locZ), chunk, bitMask);
} }
} }
@ -534,30 +590,25 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
public void refreshChunk(FaweChunk fc) { public void refreshChunk(FaweChunk fc) {
net.minecraft.server.v1_11_R1.Chunk chunk = getCachedChunk(getWorld(), fc.getX(), fc.getZ()); net.minecraft.server.v1_11_R1.Chunk chunk = getCachedChunk(getWorld(), fc.getX(), fc.getZ());
if (chunk != null) { if (chunk != null) {
sendChunk(chunk, fc.getBitMask()); sendChunk(fc.getX(), fc.getZ(), fc.getBitMask());
} }
} }
public void sendChunk(net.minecraft.server.v1_11_R1.Chunk nmsChunk, int mask) { public boolean sendChunk(net.minecraft.server.v1_11_R1.PlayerChunk playerChunk, net.minecraft.server.v1_11_R1.Chunk nmsChunk, int mask) {
WorldServer w = (WorldServer) nmsChunk.getWorld(); net.minecraft.server.v1_11_R1.WorldServer w = (net.minecraft.server.v1_11_R1.WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(nmsChunk.locX, nmsChunk.locZ);
if (playerChunk == null) { if (playerChunk == null) {
return; return false;
}
if (playerChunk.c.isEmpty()) {
return;
} }
if (mask == 0) { if (mask == 0) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65535); net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk packet = new net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk(nmsChunk, 65535);
for (EntityPlayer player : playerChunk.c) { for (net.minecraft.server.v1_11_R1.EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }
return; return true;
} }
// Send chunks // Send chunks
boolean empty = false; boolean empty = false;
ChunkSection[] sections = nmsChunk.getSections(); net.minecraft.server.v1_11_R1.ChunkSection[] sections = nmsChunk.getSections();
for (int i = 0; i < sections.length; i++) { for (int i = 0; i < sections.length; i++) {
if (sections[i] == null) { if (sections[i] == null) {
sections[i] = emptySection; sections[i] = emptySection;
@ -565,14 +616,14 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
} }
} }
if (mask == 0 || mask == 65535 && hasEntities(nmsChunk)) { if (mask == 0 || mask == 65535 && hasEntities(nmsChunk)) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65280); net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk packet = new net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk(nmsChunk, 65280);
for (EntityPlayer player : playerChunk.c) { for (net.minecraft.server.v1_11_R1.EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }
mask = 255; mask = 255;
} }
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, mask); net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk packet = new net.minecraft.server.v1_11_R1.PacketPlayOutMapChunk(nmsChunk, mask);
for (EntityPlayer player : playerChunk.c) { for (net.minecraft.server.v1_11_R1.EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }
if (empty) { if (empty) {
@ -582,6 +633,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
} }
} }
} }
return true;
} }
public boolean hasEntities(net.minecraft.server.v1_11_R1.Chunk nmsChunk) { public boolean hasEntities(net.minecraft.server.v1_11_R1.Chunk nmsChunk) {

View File

@ -10,13 +10,11 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.VisualChunk; import com.boydti.fawe.object.brush.visualization.VisualChunk;
import com.boydti.fawe.object.collection.PrimitiveList;
import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.task.TaskBuilder;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
@ -28,7 +26,6 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -36,17 +33,13 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import net.minecraft.server.v1_12_R1.BiomeBase; import net.minecraft.server.v1_12_R1.BiomeBase;
import net.minecraft.server.v1_12_R1.BiomeCache; import net.minecraft.server.v1_12_R1.BiomeCache;
import net.minecraft.server.v1_12_R1.Block; import net.minecraft.server.v1_12_R1.Block;
import net.minecraft.server.v1_12_R1.BlockPosition; import net.minecraft.server.v1_12_R1.BlockPosition;
import net.minecraft.server.v1_12_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_12_R1.ChunkProviderGenerate; import net.minecraft.server.v1_12_R1.ChunkProviderGenerate;
import net.minecraft.server.v1_12_R1.ChunkProviderServer; import net.minecraft.server.v1_12_R1.ChunkProviderServer;
import net.minecraft.server.v1_12_R1.ChunkRegionLoader;
import net.minecraft.server.v1_12_R1.ChunkSection; import net.minecraft.server.v1_12_R1.ChunkSection;
import net.minecraft.server.v1_12_R1.DataPaletteBlock; import net.minecraft.server.v1_12_R1.DataPaletteBlock;
import net.minecraft.server.v1_12_R1.Entity; import net.minecraft.server.v1_12_R1.Entity;
@ -57,7 +50,6 @@ import net.minecraft.server.v1_12_R1.EnumDifficulty;
import net.minecraft.server.v1_12_R1.EnumGamemode; import net.minecraft.server.v1_12_R1.EnumGamemode;
import net.minecraft.server.v1_12_R1.EnumSkyBlock; import net.minecraft.server.v1_12_R1.EnumSkyBlock;
import net.minecraft.server.v1_12_R1.IBlockData; import net.minecraft.server.v1_12_R1.IBlockData;
import net.minecraft.server.v1_12_R1.IChunkLoader;
import net.minecraft.server.v1_12_R1.IDataManager; import net.minecraft.server.v1_12_R1.IDataManager;
import net.minecraft.server.v1_12_R1.MinecraftServer; import net.minecraft.server.v1_12_R1.MinecraftServer;
import net.minecraft.server.v1_12_R1.NBTTagCompound; import net.minecraft.server.v1_12_R1.NBTTagCompound;
@ -273,153 +265,119 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
return super.regenerateChunk(world, x, z, biome, seed); return super.regenerateChunk(world, x, z, biome, seed);
} }
private void unload(RegionWrapper allowed, PrimitiveList<Long> chunks) {
ArrayDeque<net.minecraft.server.v1_12_R1.Chunk> queue = TaskManager.IMP.sync(new RunnableVal<ArrayDeque<net.minecraft.server.v1_12_R1.Chunk>>() {
@Override @Override
public void run(ArrayDeque<net.minecraft.server.v1_12_R1.Chunk> value) { public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
this.value = new ArrayDeque<>(); TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
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(); ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
int bcx = (allowed.minX >> 9) << 5;
int bcz = (allowed.minZ >> 9) << 5; boolean mustSave = false;
int tcx = 31 + (allowed.maxX >> 9) << 5; boolean[][] chunksUnloaded = null;
int tcz = 31 + (allowed.maxZ >> 9) << 5; { // Unload chunks
Iterator<net.minecraft.server.v1_12_R1.Chunk> iter = provider.a().iterator(); Iterator<net.minecraft.server.v1_12_R1.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
net.minecraft.server.v1_12_R1.Chunk chunk = iter.next(); net.minecraft.server.v1_12_R1.Chunk chunk = iter.next();
int cx = chunk.locX; if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) {
int cz = chunk.locZ; boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { if (isIn) {
this.value.add(chunk); if (!load) {
if (getPlayerChunk(nmsWorld, cx, cz) != null) { if (chunk.a(false)) {
chunks.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)); 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
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 File unloadedRegion = null;
public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) { if (load && !RegionFileCache.a.isEmpty()) {
try {
Object lock = new Object();
ForkJoinPool pool = new ForkJoinPool();
PrimitiveList<Long> chunks = new PrimitiveList<Long>(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<Object>() {
@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<File, RegionFile> map = RegionFileCache.a; Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator(); Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
String requiredPath = world.getName() + File.separator + "region";
while (iter.hasNext()) { while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next(); Map.Entry<File, RegionFile> 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(); RegionFile regionFile = entry.getValue();
pool.submit(new Runnable() { iter.remove();
@Override
public void run() {
try { try {
regionFile.c(); regionFile.c();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
}); break;
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) { 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<Object>() {
@Override @Override
public Object exec(Object previous) { public void run(Object value1) {
ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); net.minecraft.server.v1_12_R1.Chunk chunk = provider.getChunkAt(cx, cz, null, false);
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) { if (chunk != null) {
PlayerChunk pc = getPlayerChunk(nmsWorld, x, z); PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz);
if (pc != null) {
sendChunk(pc, chunk, 0); sendChunk(pc, chunk, 0);
} }
split();
} }
return null;
} }
}).buildAsync(); });
}
}
}
}
});
}
}
}
}
});
return true; return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} }
@Override @Override

View File

@ -15,8 +15,8 @@ import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.Chunk;
import net.minecraft.server.v1_7_R4.ChunkCoordIntPair; import net.minecraft.server.v1_7_R4.ChunkCoordIntPair;
import net.minecraft.server.v1_7_R4.ChunkPosition; 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.ChunkSection;
import net.minecraft.server.v1_7_R4.Entity; import net.minecraft.server.v1_7_R4.Entity;
import net.minecraft.server.v1_7_R4.EntityPlayer; 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.NibbleArray;
import net.minecraft.server.v1_7_R4.PacketPlayOutMapChunk; import net.minecraft.server.v1_7_R4.PacketPlayOutMapChunk;
import net.minecraft.server.v1_7_R4.PlayerChunkMap; 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.ServerNBTManager;
import net.minecraft.server.v1_7_R4.TileEntity; import net.minecraft.server.v1_7_R4.TileEntity;
import net.minecraft.server.v1_7_R4.WorldManager; import net.minecraft.server.v1_7_R4.WorldManager;
@ -133,71 +130,117 @@ public class BukkitQueue17 extends BukkitQueue_0<net.minecraft.server.v1_7_R4.Ch
} }
@Override @Override
public boolean setMCA(Runnable whileLocked, RegionWrapper allowed, boolean unload) { public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@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;
boolean mustSave = false;
boolean[][] chunksUnloaded = null;
{ // Unload chunks
Iterator<net.minecraft.server.v1_7_R4.Chunk> 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;
}
}
}
}
if (mustSave) provider.c(); // TODO only the necessary chunks
File unloadedRegion = null;
if (load && !net.minecraft.server.v1_7_R4.RegionFileCache.a.isEmpty()) {
Map<File, net.minecraft.server.v1_7_R4.RegionFile> map = net.minecraft.server.v1_7_R4.RegionFileCache.a;
Iterator<Map.Entry<File, net.minecraft.server.v1_7_R4.RegionFile>> iter = map.entrySet().iterator();
String requiredPath = world.getName() + File.separator + "region";
while (iter.hasNext()) {
Map.Entry<File, net.minecraft.server.v1_7_R4.RegionFile> 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 { 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<Object>() { TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override @Override
public void run(Object value) { public void run(Object value1) {
try { net.minecraft.server.v1_7_R4.Chunk chunk = provider.getChunkAt(cx, cz, null);
synchronized (RegionFileCache.class) {
ArrayDeque<Chunk> 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<Chunk> 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();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove();
}
}
whileLocked.run();
// Load the chunks again
if (unload) {
for (Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) { if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); if (nmsWorld.getPlayerChunkMap().isChunkInUse(cx, cz)) {
sendChunk(chunk, 0); sendChunk(chunk, 0);
} }
} }
} }
});
}
}
}
}
});
}
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} }
}); });
return true; return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} }
@Override @Override

View File

@ -15,8 +15,8 @@ import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.Block;
import net.minecraft.server.v1_8_R3.BlockPosition; import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.Chunk; 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.ChunkSection;
import net.minecraft.server.v1_8_R3.Entity; import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer; 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.Packet;
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk; import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
import net.minecraft.server.v1_8_R3.PlayerChunkMap; 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.ServerNBTManager;
import net.minecraft.server.v1_8_R3.TileEntity; import net.minecraft.server.v1_8_R3.TileEntity;
import net.minecraft.server.v1_8_R3.WorldData; import net.minecraft.server.v1_8_R3.WorldData;
@ -132,71 +128,117 @@ public class BukkitQueue18R3 extends BukkitQueue_0<net.minecraft.server.v1_8_R3.
} }
@Override @Override
public boolean setMCA(Runnable whileLocked, RegionWrapper allowed, boolean unload) { public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@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;
boolean mustSave = false;
boolean[][] chunksUnloaded = null;
{ // Unload chunks
Iterator<net.minecraft.server.v1_8_R3.Chunk> 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;
}
}
}
}
if (mustSave) provider.c(); // TODO only the necessary chunks
File unloadedRegion = null;
if (load && !net.minecraft.server.v1_8_R3.RegionFileCache.a.isEmpty()) {
Map<File, net.minecraft.server.v1_8_R3.RegionFile> map = net.minecraft.server.v1_8_R3.RegionFileCache.a;
Iterator<Map.Entry<File, net.minecraft.server.v1_8_R3.RegionFile>> iter = map.entrySet().iterator();
String requiredPath = world.getName() + File.separator + "region";
while (iter.hasNext()) {
Map.Entry<File, net.minecraft.server.v1_8_R3.RegionFile> 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 { 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<Object>() { TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override @Override
public void run(Object value) { public void run(Object value1) {
try { net.minecraft.server.v1_8_R3.Chunk chunk = provider.getChunkAt(cx, cz, null);
synchronized (RegionFileCache.class) {
ArrayDeque<Chunk> 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<Chunk> 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();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove();
}
}
whileLocked.run();
// Load the chunks again
if (unload) {
for (Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) { if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); if (nmsWorld.getPlayerChunkMap().isChunkInUse(cx, cz)) {
sendChunk(chunk, 0); sendChunk(chunk, 0);
} }
} }
} }
});
}
}
}
}
});
}
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} }
}); });
return true; return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} }
@Override @Override

View File

@ -10,7 +10,6 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.VisualChunk; 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.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
@ -26,7 +25,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -35,10 +33,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; 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.Block;
import net.minecraft.server.v1_9_R2.BlockPosition; 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.ChunkSection;
import net.minecraft.server.v1_9_R2.DataBits; import net.minecraft.server.v1_9_R2.DataBits;
import net.minecraft.server.v1_9_R2.DataPaletteBlock; 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.PacketPlayOutMultiBlockChange;
import net.minecraft.server.v1_9_R2.PlayerChunk; import net.minecraft.server.v1_9_R2.PlayerChunk;
import net.minecraft.server.v1_9_R2.PlayerChunkMap; 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.ServerNBTManager;
import net.minecraft.server.v1_9_R2.TileEntity; import net.minecraft.server.v1_9_R2.TileEntity;
import net.minecraft.server.v1_9_R2.WorldData; import net.minecraft.server.v1_9_R2.WorldData;
@ -268,74 +263,122 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<net.minecraft.server.v1_9_
} }
@Override @Override
public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) { public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
try { TaskManager.IMP.sync(new RunnableVal<Boolean>() {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override @Override
public void run(Object value) { public void run(Boolean value) {
try { long start = System.currentTimeMillis();
synchronized (RegionFileCache.class) { long last = start;
ArrayDeque<net.minecraft.server.v1_9_R2.Chunk> chunks = new ArrayDeque<>(); synchronized (net.minecraft.server.v1_9_R2.RegionFileCache.class) {
World world = getWorld(); World world = getWorld();
world.setKeepSpawnInMemory(false); if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); net.minecraft.server.v1_9_R2.ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
if (unload) { // Unload chunks
int bcx = (allowed.minX >> 9) << 5; boolean mustSave = false;
int bcz = (allowed.minZ >> 9) << 5; boolean[][] chunksUnloaded = null;
int tcx = 31 + (allowed.maxX >> 9) << 5; { // Unload chunks
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<net.minecraft.server.v1_9_R2.Chunk> iter = provider.a().iterator(); Iterator<net.minecraft.server.v1_9_R2.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
net.minecraft.server.v1_9_R2.Chunk chunk = iter.next(); net.minecraft.server.v1_9_R2.Chunk chunk = iter.next();
int cx = chunk.locX; if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) {
int cz = chunk.locZ; boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { if (isIn) {
chunks.add(chunk); if (!load) {
if (chunk.a(false)) {
mustSave = true;
provider.saveChunk(chunk);
provider.saveChunkNOP(chunk);
} }
continue;
} }
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();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove(); 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) { if (mustSave) provider.c(); // TODO only the necessary chunks
for (net.minecraft.server.v1_9_R2.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ); File unloadedRegion = null;
if (load && !net.minecraft.server.v1_9_R2.RegionFileCache.a.isEmpty()) {
Map<File, net.minecraft.server.v1_9_R2.RegionFile> map = net.minecraft.server.v1_9_R2.RegionFileCache.a;
Iterator<Map.Entry<File, net.minecraft.server.v1_9_R2.RegionFile>> iter = map.entrySet().iterator();
String requiredPath = world.getName() + File.separator + "region";
while (iter.hasNext()) {
Map.Entry<File, net.minecraft.server.v1_9_R2.RegionFile> 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;
}
}
}
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<Object>() {
@Override
public void run(Object value1) {
net.minecraft.server.v1_9_R2.Chunk chunk = provider.getChunkAt(cx, cz, null, false);
if (chunk != null) { if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk); net.minecraft.server.v1_9_R2.PlayerChunk pc = nmsWorld.getPlayerChunkMap().getChunk(cx, cz);
if (pc != null && !pc.c.isEmpty()) {
sendChunk(chunk, 0); sendChunk(chunk, 0);
} }
} }
} }
});
}
}
}
}
});
}
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} }
}); });
return true; return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} }
@Override @Override

View File

@ -12,6 +12,10 @@ commands:
description: (FAWE) Cancel your edit description: (FAWE) Cancel your edit
aliases: [fawecancel,/fcancel,/cancel,/fawecancel] aliases: [fawecancel,/fcancel,/cancel,/fawecancel]
permissions: permissions:
fawe.plotsquared:
default: true
fawe.plotme:
default: true
fawe.bypass: fawe.bypass:
default: false default: false
fawe.tips: fawe.tips:

View File

@ -45,7 +45,6 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.ArrayList; import java.util.ArrayList;
@ -93,9 +92,9 @@ public class AnvilCommands {
copy = true; copy = true;
} }
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false); FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky()); MCAQueue queue = new MCAQueue(defaultQueue);
if (copy) { if (copy) {
return queue.filterCopy(filter, true); return queue.filterCopy(filter, RegionWrapper.GLOBAL());
} else { } else {
return queue.filterWorld(filter); return queue.filterWorld(filter);
} }
@ -122,17 +121,8 @@ public class AnvilCommands {
RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint()); RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
String worldName = Fawe.imp().getWorldName(editSession.getWorld()); String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false); FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
File folder = tmp.getSaveFolder(); MCAQueue queue = new MCAQueue(tmp);
MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky()); queue.filterCopy(filter, wrappedRegion);
player.print(BBC.getPrefix() + "Safely unloading regions...");
tmp.setMCA(new Runnable() {
@Override
public void run() {
player.print(BBC.getPrefix() + "Performing operation...");
queue.filterRegion(filter, wrappedRegion);
player.print(BBC.getPrefix() + "Safely loading regions...");
}
}, wrappedRegion, true);
return filter; return filter;
} }
@ -147,7 +137,7 @@ public class AnvilCommands {
max = 4 max = 4
) )
@CommandPermissions("worldedit.anvil.replaceall") @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; final FaweBlockMatcher matchFrom;
if (from == null) { if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR; matchFrom = FaweBlockMatcher.NOT_AIR;
@ -159,7 +149,7 @@ public class AnvilCommands {
} }
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true)); final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo); 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())); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
} }
@ -172,7 +162,7 @@ public class AnvilCommands {
max = 1 max = 1
) )
@CommandPermissions("worldedit.anvil.remapall") @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 mapper;
ClipboardRemapper.RemapPlatform from; ClipboardRemapper.RemapPlatform from;
ClipboardRemapper.RemapPlatform to; ClipboardRemapper.RemapPlatform to;
@ -184,7 +174,7 @@ public class AnvilCommands {
to = ClipboardRemapper.RemapPlatform.PC; to = ClipboardRemapper.RemapPlatform.PC;
} }
RemapFilter filter = new RemapFilter(from, to); 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())); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
} }
@ -201,10 +191,10 @@ public class AnvilCommands {
max = 3 max = 3
) )
@CommandPermissions("worldedit.anvil.deleteallunvisited") @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 long chunkInactivityMillis = fileDurationMillis; // Use same value for now
DeleteUninhabitedFilter filter = new DeleteUninhabitedFilter(fileDurationMillis, inhabitedTicks, chunkInactivityMillis); 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())); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
} }
@ -220,10 +210,10 @@ public class AnvilCommands {
max = 3 max = 3
) )
@CommandPermissions("worldedit.anvil.deletealloldregions") @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; long duration = MainUtil.timeToSec(time) * 1000l;
DeleteOldFilter filter = new DeleteOldFilter(duration); 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())); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
} }
@ -253,7 +243,7 @@ public class AnvilCommands {
max = 4 max = 4
) )
@CommandPermissions("worldedit.anvil.replaceall") @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; MCAFilterCounter filter;
if (useMap) { if (useMap) {
if (to instanceof RandomPattern) { if (to instanceof RandomPattern) {
@ -272,7 +262,7 @@ public class AnvilCommands {
} }
filter = new ReplacePatternFilter(matchFrom, to); 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())); if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
} }
@ -285,7 +275,7 @@ public class AnvilCommands {
max = 3 max = 3
) )
@CommandPermissions("worldedit.anvil.countall") @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<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true); Set<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true);
MCAFilterCounter filter; MCAFilterCounter filter;
if (useData || arg.contains(":")) { // Optimize for both cases if (useData || arg.contains(":")) { // Optimize for both cases
@ -297,7 +287,7 @@ public class AnvilCommands {
searchBlocks.forEach(counter::addBlock); searchBlocks.forEach(counter::addBlock);
filter = counter; 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())); if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal()));
} }
@ -539,8 +529,7 @@ public class AnvilCommands {
CuboidRegion cuboid = (CuboidRegion) selection; CuboidRegion cuboid = (CuboidRegion) selection;
String worldName = Fawe.imp().getWorldName(editSession.getWorld()); String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false); FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
File folder = tmp.getSaveFolder(); MCAQueue queue = new MCAQueue(tmp);
MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky());
Vector origin = session.getPlacementPosition(player); Vector origin = session.getPlacementPosition(player);
MCAClipboard clipboard = new MCAClipboard(queue, cuboid, origin); MCAClipboard clipboard = new MCAClipboard(queue, cuboid, origin);
FawePlayer fp = FawePlayer.wrap(player); FawePlayer fp = FawePlayer.wrap(player);
@ -558,7 +547,7 @@ public class AnvilCommands {
) )
@CommandPermissions("worldedit.anvil.pastechunks") @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); FawePlayer fp = FawePlayer.wrap(player);
MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD); MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD);
if (clipboard == null) { 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); RegionWrapper pasteRegion = new RegionWrapper(copyRegion.minX + oX, copyRegion.maxX + oX, copyRegion.minZ + oZ, copyRegion.maxZ + oZ);
String pasteWorldName = Fawe.imp().getWorldName(editSession.getWorld()); String pasteWorldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmpTo = SetQueue.IMP.getNewQueue(pasteWorldName, true, false); 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 copyQueue = clipboard.getQueue();
MCAQueue pasteQueue = new MCAQueue(pasteWorldName, folder, tmpTo.hasSky()); MCAQueue pasteQueue = new MCAQueue(tmpTo);
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); pasteQueue.pasteRegion(copyQueue, copyRegion, offset);
player.print(BBC.getPrefix() + "Safely loading regions..."); BBC.COMMAND_PASTE.send(player, player.getPosition());
} catch (Throwable e) {
e.printStackTrace();
}
}
}, copyRegion, false);
}
}, pasteRegion, true);
} }
} }

View File

@ -460,6 +460,11 @@ public class MCAFile {
public void flush(ForkJoinPool pool) { public void flush(ForkJoinPool pool) {
synchronized (raf) { synchronized (raf) {
if (isDeleted()) {
clear();
file.delete();
return;
}
boolean wait; boolean wait;
if (pool == null) { if (pool == null) {
wait = true; wait = true;
@ -500,6 +505,7 @@ public class MCAFile {
} }
} }
if (modified) { if (modified) {
file.setLastModified(now);
forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() { forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override @Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) { public void run(Integer cx, Integer cz, Integer offset, Integer size) {

View File

@ -4,6 +4,8 @@ import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ForkJoinPool;
/** /**
* MCAQueue.filterWorld(MCAFilter)<br> * MCAQueue.filterWorld(MCAFilter)<br>
@ -11,6 +13,10 @@ import java.nio.file.attribute.BasicFileAttributes;
*/ */
public class MCAFilter<T> extends IterableThreadLocal<T> { public class MCAFilter<T> extends IterableThreadLocal<T> {
public void withPool(ForkJoinPool pool, MCAQueue queue) {
return;
}
public boolean appliesFile(Path path, BasicFileAttributes attr) { public boolean appliesFile(Path path, BasicFileAttributes attr) {
return true; return true;
} }

View File

@ -9,7 +9,6 @@ import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.RunnableVal4; import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.collection.IterableThreadLocal;
@ -21,6 +20,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection; import java.util.Collection;
@ -146,6 +146,12 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
return changed; return changed;
} }
@Override
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean unload) {
if (parent != null) return parent.setMCA(mcaX, mcaZ, region, whileLocked, unload);
return super.setMCA(mcaX, mcaZ, region, whileLocked, unload);
}
public void pasteRegion(MCAQueue from, final RegionWrapper regionFrom, Vector offset) throws IOException { public void pasteRegion(MCAQueue from, final RegionWrapper regionFrom, Vector offset) throws IOException {
int oX = offset.getBlockX(); int oX = offset.getBlockX();
int oZ = offset.getBlockZ(); int oZ = offset.getBlockZ();
@ -153,23 +159,23 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
int oCX = oX >> 4; int oCX = oX >> 4;
int oCZ = oZ >> 4; int oCZ = oZ >> 4;
RegionWrapper regionTo = new RegionWrapper(regionFrom.minX + oX, regionFrom.maxX + oX, regionFrom.minZ + oZ, regionFrom.maxZ + oZ); RegionWrapper regionTo = new RegionWrapper(regionFrom.minX + oX, regionFrom.maxX + oX, regionFrom.minZ + oZ, regionFrom.maxZ + oZ);
File folder = getSaveFolder(); File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
int bMcaX = (regionTo.minX >> 9); int bMcaX = (regionTo.minX >> 9);
int bMcaZ = (regionTo.minZ >> 9); int bMcaZ = (regionTo.minZ >> 9);
int tMcaX = (regionTo.maxX >> 9); int tMcaX = (regionTo.maxX >> 9);
int tMcaZ = (regionTo.maxZ >> 9); int tMcaZ = (regionTo.maxZ >> 9);
for (int mcaZ = bMcaZ; mcaZ <= tMcaZ; mcaZ++) {
for (int mcaX = bMcaX; mcaX <= tMcaX; mcaX++) { 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 bcx = Math.max(mcaX << 5, regionTo.minX >> 4);
int bcz = Math.max(mcaZ << 5, regionTo.minZ >> 4); int bcz = Math.max(mcaZ << 5, regionTo.minZ >> 4);
int tcx = Math.min((mcaX << 5) + 31, regionTo.maxX >> 4); int tcx = Math.min((mcaX << 5) + 31, regionTo.maxX >> 4);
int tcz = Math.min((mcaZ << 5) + 31, regionTo.maxZ >> 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(); mcaFile.init();
final long heapSize = Runtime.getRuntime().totalMemory(); final long heapSize = Runtime.getRuntime().totalMemory();
@ -199,7 +205,7 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
} else { } else {
MCAChunk newChunk = mcaFile.getChunk(cx, cz); MCAChunk newChunk = mcaFile.getChunk(cx, cz);
if (newChunk == null) { if (newChunk == null) {
newChunk = new MCAChunk(this, cx, cz); newChunk = new MCAChunk(MCAQueue.this, cx, cz);
mcaFile.setChunk(newChunk); mcaFile.setChunk(newChunk);
} else { } else {
newChunk.setModified(); newChunk.setModified();
@ -225,7 +231,7 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
MCAChunk newChunk = mcaFile.getChunk(cx, cz); MCAChunk newChunk = mcaFile.getChunk(cx, cz);
boolean created; boolean created;
if (newChunk == null) { if (newChunk == null) {
newChunk = new MCAChunk(this, cx, cz); newChunk = new MCAChunk(MCAQueue.this, cx, cz);
created = true; created = true;
} else { } else {
created = false; created = false;
@ -263,86 +269,102 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
} }
} }
} }
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
mcaFile.close(pool);
from.clear(); from.clear();
}
}
from.clear();
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
pool.shutdown();
}
public <G, T extends MCAFilter<G>> T filterCopy(final T filter, boolean deleteOnCopyFail) {
this.filterWorld(new DelegateMCAFilter<G>(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();
}
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();
}
}
}
} else {
copy.clear();
if (!copyDest.delete()) {
copyDest.deleteOnExit();
}
}
}
return result;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
}
return null; return null;
} }
}, regionTo);
from.clear();
} }
}, true, new RunnableVal<MCAFile>() {
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);
}
}
}
copy.clear();
copyFile.delete();
}
public <G, T extends MCAFilter<G>> T filterCopy(final T filter, RegionWrapper region) {
DelegateMCAFilter<G> delegate = new DelegateMCAFilter<G>(filter) {
MCAFile original;
MCAFile copy;
ForkJoinPool pool;
@Override @Override
public void run(MCAFile value) { public void withPool(ForkJoinPool pool, MCAQueue queue) {
if (deleteOnCopyFail) { this.pool = pool;
File file = value.getFile();
boolean result = file.delete();
if (!result) {
file.deleteOnExit();
} }
Fawe.debug("Deleted " + file + " = " + result);
@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; return filter;
} }
public <G, T extends MCAFilter<G>> T filterRegion(final T filter, final RegionWrapper region) { public <G, T extends MCAFilter<G>> T filterRegion(final T filter, final RegionWrapper region) {
this.filterWorld(new DelegateMCAFilter<G>(filter) { DelegateMCAFilter<G> delegate = new DelegateMCAFilter<G>(filter) {
@Override @Override
public boolean appliesFile(Path path, BasicFileAttributes attr) { public boolean appliesFile(Path path, BasicFileAttributes attr) {
String name = path.getFileName().toString(); String name = path.toString();
String[] split = name.split("\\."); int[] coords = MainUtil.regionNameToCoords(name);
final int mcaX = Integer.parseInt(split[1]); final int mcaX = coords[0];
final int mcaZ = Integer.parseInt(split[2]); final int mcaZ = coords[1];
return region.isInMCA(mcaX, mcaZ) && filter.appliesFile(path, attr); return region.isInMCA(mcaX, mcaZ) && filter.appliesFile(path, attr);
} }
@ -403,7 +425,34 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
} }
return null; return null;
} }
};
final int minMCAX = region.minX >> 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<Path, RunnableVal2<Path, BasicFileAttributes>>() {
@Override
public void run(Path root, RunnableVal2<Path, BasicFileAttributes> 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; return filter;
} }
@ -420,14 +469,8 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
return filter; return filter;
} }
public <G, T extends MCAFilter<G>> T filterWorld(final T filter) { private <G, T extends MCAFilter<G>> RunnableVal2<Path, BasicFileAttributes> filterFunction(final T filter, ForkJoinPool pool) {
return filterWorld(filter, false, null); return new RunnableVal2<Path, BasicFileAttributes>() {
}
private <G, T extends MCAFilter<G>> T filterWorld(final T filter, boolean replaceOriginalOnCopy, RunnableVal<MCAFile> onReplaceFail) {
File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
MainUtil.traverse(folder.toPath(), new RunnableVal2<Path, BasicFileAttributes>() {
@Override @Override
public void run(Path path, BasicFileAttributes attr) { public void run(Path path, BasicFileAttributes attr) {
try { try {
@ -443,7 +486,6 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
final int mcaZ = Integer.parseInt(split[2]); final int mcaZ = Integer.parseInt(split[2]);
if (filter.appliesFile(mcaX, mcaZ)) { if (filter.appliesFile(mcaX, mcaZ)) {
File file = path.toFile(); File file = path.toFile();
Fawe.debug("Apply file " + file);
final MCAFile original = new MCAFile(MCAQueue.this, file); final MCAFile original = new MCAFile(MCAQueue.this, file);
final MCAFile finalFile = filter.applyFile(original); final MCAFile finalFile = filter.applyFile(original);
if (finalFile != null && !finalFile.isDeleted()) { if (finalFile != null && !finalFile.isDeleted()) {
@ -519,37 +561,20 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
if (original.isDeleted()) { if (original.isDeleted()) {
file.delete(); file.delete();
} }
if (finalFile != null) {
if (original != finalFile) {
if (finalFile.isModified()) {
finalFile.close(pool);
if (finalFile.isDeleted()) {
finalFile.getFile().delete();
if (replaceOriginalOnCopy && file.exists()) {
file.delete();
}
} else if (replaceOriginalOnCopy) {
File from = finalFile.getFile();
file.delete();
if (!from.renameTo(file)) {
Fawe.debug("Could not rename " + from + "to " + file + ".");
if (onReplaceFail != null) {
onReplaceFail.run(finalFile);
}
}
}
} else if (replaceOriginalOnCopy) {
finalFile.clear();
finalFile.getFile().delete();
}
}
}
} }
} catch (Throwable ignore) { } catch (Throwable ignore) {
ignore.printStackTrace(); ignore.printStackTrace();
} }
} }
}); };
}
private <G, T extends MCAFilter<G>> T filterWorld(final T filter, RunnableVal2<Path, RunnableVal2<Path, BasicFileAttributes>> traverser) {
File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
filter.withPool(pool, this);
RunnableVal2<Path, BasicFileAttributes> task = filterFunction(filter, pool);
traverser.run(folder.toPath(), task);
pool.shutdown(); pool.shutdown();
try { try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
@ -559,6 +584,15 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
return filter; return filter;
} }
public <G, T extends MCAFilter<G>> T filterWorld(final T filter) {
return filterWorld(filter, new RunnableVal2<Path, RunnableVal2<Path, BasicFileAttributes>>() {
@Override
public void run(Path value1, RunnableVal2<Path, BasicFileAttributes> value2) {
MainUtil.traverse(value1, value2);
}
});
}
@Override @Override
public void relight(int x, int y, int z) { public void relight(int x, int y, int z) {
throw new UnsupportedOperationException("Not supported"); throw new UnsupportedOperationException("Not supported");

View File

@ -5,9 +5,11 @@ import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.example.NullFaweChunk; import com.boydti.fawe.example.NullFaweChunk;
import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.SetQueue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@ -49,6 +51,7 @@ public class MCAQueueMap implements IFaweQueueMap {
lastFile = tmp = mcaFileMap.get(pair); lastFile = tmp = mcaFileMap.get(pair);
if (lastFile == null) { if (lastFile == null) {
try { try {
queue.setMCA(lastFileX, lastFileZ, RegionWrapper.GLOBAL(), null, false);
lastFile = tmp = new MCAFile(queue, lastFileX, lastFileZ); lastFile = tmp = new MCAFile(queue, lastFileX, lastFileZ);
} catch (FaweException.FaweChunkLoadException ignore) { } catch (FaweException.FaweChunkLoadException ignore) {
lastFile = null; lastFile = null;
@ -93,7 +96,8 @@ public class MCAQueueMap implements IFaweQueueMap {
lastX = cx; lastX = cx;
lastZ = cz; lastZ = cz;
if (isHybridQueue) { if (isHybridQueue) {
lastChunk = ((MappedFaweQueue) queue).getFaweQueueMap().getCachedFaweChunk(cx, cz); MappedFaweQueue mfq = ((MappedFaweQueue) queue);
lastChunk = mfq.getFaweQueueMap().getCachedFaweChunk(cx, cz);
if (lastChunk != null) { if (lastChunk != null) {
return lastChunk; return lastChunk;
} }
@ -175,7 +179,12 @@ public class MCAQueueMap implements IFaweQueueMap {
if (result = iter.hasNext()) { if (result = iter.hasNext()) {
MCAFile file = iter.next().getValue(); MCAFile file = iter.next().getValue();
iter.remove(); 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 { } else {
break; break;
} }

View File

@ -3,15 +3,22 @@ package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFile; import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.jnbt.anvil.MCAFilter; import com.boydti.fawe.jnbt.anvil.MCAFilter;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer; import java.util.function.Consumer;
public class DelegateMCAFilter<T> extends MCAFilter<T> { public class DelegateMCAFilter<T> extends MCAFilter<T> {
private final MCAFilter<T> filter; private final MCAFilter<T> filter;
@Override
public void withPool(ForkJoinPool pool, MCAQueue queue) {
filter.withPool(pool, queue);
}
@Override @Override
public boolean appliesFile(Path path, BasicFileAttributes attr) { public boolean appliesFile(Path path, BasicFileAttributes attr) {
return filter.appliesFile(path, attr); return filter.appliesFile(path, attr);

View File

@ -42,6 +42,18 @@ public class FaweInputStream extends DataInputStream {
return nbtIn.readNamedTag(); 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 @Override
public void close() throws IOException { public void close() throws IOException {
if (nbtIn != null) { if (nbtIn != null) {

View File

@ -41,7 +41,6 @@ public class FaweOutputStream extends DataOutputStream {
this.writeByte(i & 127 | 128); this.writeByte(i & 127 | 128);
i >>>= 7; i >>>= 7;
} }
this.writeByte(i); this.writeByte(i);
} }

View File

@ -285,8 +285,9 @@ public abstract class FaweQueue implements HasFaweQueue, Extent {
public abstract Collection<FaweChunk> getFaweChunks(); public abstract Collection<FaweChunk> getFaweChunks();
public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) { public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean load) {
return false; if (whileLocked != null) whileLocked.run();
return true;
} }
public abstract void setChunk(final FaweChunk chunk); public abstract void setChunk(final FaweChunk chunk);

View File

@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.ExtentTraverser;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -17,6 +18,17 @@ public class ResettableExtent extends AbstractDelegateExtent implements Serializ
super(parent); 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) { public ResettableExtent setExtent(Extent extent) {
checkNotNull(extent); checkNotNull(extent);
Extent next = getExtent(); Extent next = getExtent();

View File

@ -28,6 +28,21 @@ public class TransformExtent extends BlockTransformExtent {
return super.setExtent(extent); 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) { public void setOrigin(Vector pos) {
this.min = pos; this.min = pos;
} }

View File

@ -45,8 +45,8 @@ public class DelegateFaweQueue extends FaweQueue {
} }
@Override @Override
public boolean setMCA(Runnable whileLocked, RegionWrapper region, boolean unload) { public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean load) {
return parent.setMCA(whileLocked, region, unload); return parent.setMCA(mcaX, mcaZ, region, whileLocked, load);
} }
@Override @Override

View File

@ -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) { public static boolean isInSubDirectory(File dir, File file) {
if (file == null) return false; if (file == null) return false;
if (file.equals(dir)) return true; if (file.equals(dir)) return true;

View File

@ -700,6 +700,19 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
} }
} }
public @Nullable ResettableExtent getTransform() {
ExtentTraverser<AbstractDelegateExtent> 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. * Set a mask.
* *
@ -957,10 +970,6 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
@Override @Override
public BaseBlock getLazyBlock(final Vector position) { 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()); return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ());
} }
@ -974,10 +983,6 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
@Override @Override
public BaseBlock getBlock(final Vector position) { 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()); return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ());
} }
@ -1275,8 +1280,8 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
@Override @Override
public Vector getMinimumPoint() { public Vector getMinimumPoint() {
if (getWorld() != null) { if (extent != null) {
return this.getWorld().getMinimumPoint(); return this.extent.getMinimumPoint();
} else { } else {
return new Vector(-30000000, 0, -30000000); return new Vector(-30000000, 0, -30000000);
} }
@ -1284,8 +1289,8 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
@Override @Override
public Vector getMaximumPoint() { public Vector getMaximumPoint() {
if (getWorld() != null) { if (extent != null) {
return this.getWorld().getMaximumPoint(); return this.extent.getMaximumPoint();
} else { } else {
return new Vector(30000000, 255, 30000000); return new Vector(30000000, 255, 30000000);
} }
@ -1471,9 +1476,9 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
checkNotNull(pattern); checkNotNull(pattern);
checkArgument(radius >= 0, "radius >= 0"); checkArgument(radius >= 0, "radius >= 0");
checkArgument(depth >= 1, "depth >= 1"); 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( 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 // Want to replace blocks
final BlockReplace replace = new BlockReplace(EditSession.this, pattern); final BlockReplace replace = new BlockReplace(EditSession.this, pattern);

View File

@ -53,9 +53,7 @@ public class DownwardVisitor extends RecursiveVisitor {
public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) { public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) {
super(mask, function, depth, hasFaweQueue); super(mask, function, depth, hasFaweQueue);
checkNotNull(mask); checkNotNull(mask);
this.baseY = baseY; this.baseY = baseY;
final Collection<Vector> directions = this.getDirections(); final Collection<Vector> directions = this.getDirections();
directions.clear(); directions.clear();
directions.add(new Vector(1, 0, 0)); directions.add(new Vector(1, 0, 0));
@ -68,7 +66,7 @@ public class DownwardVisitor extends RecursiveVisitor {
@Override @Override
public boolean isVisitable(final Vector from, final Vector to) { public boolean isVisitable(final Vector from, final Vector to) {
final int fromY = from.getBlockY(); 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() { public static Class<?> inject() {