package com.intellectualcrafters.plot.util;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.object.ChunkLoc;
import com.intellectualcrafters.plot.object.Location;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.RegionWrapper;
import com.intellectualcrafters.plot.object.RunnableVal;
import com.intellectualcrafters.plot.util.block.GlobalBlockQueue;
import com.intellectualcrafters.plot.util.block.LocalBlockQueue;
import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public abstract class ChunkManager {
public static ChunkManager manager = null;
private static RunnableVal<ScopedLocalBlockQueue> CURRENT_FORCE_CHUNK;
private static RunnableVal<ScopedLocalBlockQueue> CURRENT_ADD_CHUNK;
public static ChunkLoc getChunkChunk(Location location) {
int x = location.getX() >> 9;
int z = location.getZ() >> 9;
return new ChunkLoc(x, z);
}
public static void setChunkInPlotArea(RunnableVal<ScopedLocalBlockQueue> force, RunnableVal<ScopedLocalBlockQueue> add, String world, ChunkLoc loc) {
LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(world, false);
if (PS.get().isAugmented(world)) {
int bx = loc.x << 4;
int bz = loc.z << 4;
ScopedLocalBlockQueue scoped = new ScopedLocalBlockQueue(queue, new Location(world, bx, 0, bz), new Location(world, bx + 15, 255, bz + 15));
if (force != null) {
force.run(scoped);
} else {
scoped.regenChunk(loc.x, loc.z);
if (add != null) {
add.run(scoped);
}
}
queue.flush();
} else {
CURRENT_FORCE_CHUNK = force;
CURRENT_ADD_CHUNK = add;
queue.regenChunk(loc.x, loc.z);
CURRENT_FORCE_CHUNK = null;
CURRENT_ADD_CHUNK = null;
}
}
public static boolean preProcessChunk(ScopedLocalBlockQueue queue) {
if (CURRENT_FORCE_CHUNK != null) {
CURRENT_FORCE_CHUNK.run(queue);
CURRENT_FORCE_CHUNK = null;
return true;
}
return false;
}
public static boolean postProcessChunk(ScopedLocalBlockQueue queue) {
if (CURRENT_ADD_CHUNK != null) {
CURRENT_ADD_CHUNK.run(queue);
CURRENT_ADD_CHUNK = null;
return true;
}
return false;
}
public static void largeRegionTask(final String world, final RegionWrapper region, final RunnableVal<ChunkLoc> task, final Runnable whenDone) {
TaskManager.runTaskAsync(new Runnable() {
@Override
public void run() {
HashSet<ChunkLoc> chunks = new HashSet<>();
Set<ChunkLoc> mcrs = manager.getChunkChunks(world);
for (ChunkLoc mcr : mcrs) {
int bx = mcr.x << 9;
int bz = mcr.z << 9;
int tx = bx + 511;
int tz = bz + 511;
if (bx <= region.maxX && tx >= region.minX && bz <= region.maxZ && tz >= region.minZ) {
for (int x = bx >> 4; x <= (tx >> 4); x++) {
int cbx = x << 4;
int ctx = cbx + 15;
if (cbx <= region.maxX && ctx >= region.minX) {
for (int z = bz >> 4; z <= (tz >> 4); z++) {
int cbz = z << 4;
int ctz = cbz + 15;
if (cbz <= region.maxZ && ctz >= region.minZ) {
chunks.add(new ChunkLoc(x, z));
}
}
}
}
}
}
TaskManager.objectTask(chunks, new RunnableVal<ChunkLoc>() {
@Override
public void run(ChunkLoc value) {
if (manager.loadChunk(world, value, false)) {
task.run(value);
}
}
}, whenDone);
}
});
}
public static void chunkTask(final Plot plot, final RunnableVal<int[]> task, final Runnable whenDone, final int allocate) {
final ArrayList<RegionWrapper> regions = new ArrayList<>(plot.getRegions());
Runnable smallTask = new Runnable() {
@Override
public void run() {
if (regions.isEmpty()) {
TaskManager.runTask(whenDone);
return;
}
RegionWrapper value = regions.remove(0);
Location pos1 = new Location(plot.getWorldName(), value.minX, 0, value.minZ);
Location pos2 = new Location(plot.getWorldName(), value.maxX, 0, value.maxZ);
chunkTask(pos1, pos2, task, this, allocate);
}
};
smallTask.run();
}
/**
* The int[] will be in the form: [chunkx, chunkz, pos1x, pos1z, pos2x, pos2z, isedge] and will represent the bottom and top parts of the chunk
* @param pos1
* @param pos2
* @param task
* @param whenDone
*/
public static void chunkTask(Location pos1, Location pos2, final RunnableVal<int[]> task, final Runnable whenDone, final int allocate) {
final int p1x = pos1.getX();
final int p1z = pos1.getZ();
final int p2x = pos2.getX();
final int p2z = pos2.getZ();
final int bcx = p1x >> 4;
final int bcz = p1z >> 4;
final int tcx = p2x >> 4;
final int tcz = p2z >> 4;
final ArrayList<ChunkLoc> chunks = new ArrayList<>();
for (int x = bcx; x <= tcx; x++) {
for (int z = bcz; z <= tcz; z++) {
chunks.add(new ChunkLoc(x, z));
}
}
TaskManager.runTask(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
while (!chunks.isEmpty() && ((System.currentTimeMillis() - start) < allocate)) {
ChunkLoc chunk = chunks.remove(0);
task.value = new int[7];
task.value[0] = chunk.x;
task.value[1] = chunk.z;
task.value[2] = task.value[0] << 4;
task.value[3] = task.value[1] << 4;
task.value[4] = task.value[2] + 15;
task.value[5] = task.value[3] + 15;
if (task.value[0] == bcx) {
task.value[2] = p1x;
task.value[6] = 1;
}
if (task.value[0] == tcx) {
task.value[4] = p2x;
task.value[6] = 1;
}
if (task.value[1] == bcz) {
task.value[3] = p1z;
task.value[6] = 1;
}
if (task.value[1] == tcz) {
task.value[5] = p2z;
task.value[6] = 1;
}
task.run();
}
if (!chunks.isEmpty()) {
TaskManager.runTaskLater(this, 1);
} else {
TaskManager.runTask(whenDone);
}
}
});
}
/**
* 0 = Entity
* 1 = Animal
* 2 = Monster
* 3 = Mob
* 4 = Boat
* 5 = Misc
* @param plot
* @return
*/
public abstract int[] countEntities(Plot plot);
public abstract boolean loadChunk(String world, ChunkLoc loc, boolean force);
public abstract void unloadChunk(String world, ChunkLoc loc, boolean save, boolean safe);
public Set<ChunkLoc> getChunkChunks(String world) {
File folder = new File(PS.get().IMP.getWorldContainer(), world + File.separator + "region");
File[] regionFiles = folder.listFiles();
HashSet<ChunkLoc> chunks = new HashSet<>();
if (regionFiles == null) {
throw new RuntimeException("Could not find worlds folder: " + folder + " ? (no read access?)");
}
for (File file : regionFiles) {
String name = file.getName();
if (name.endsWith("mca")) {
String[] split = name.split("\\.");
try {
int x = Integer.parseInt(split[1]);
int z = Integer.parseInt(split[2]);
ChunkLoc loc = new ChunkLoc(x, z);
chunks.add(loc);
} catch (NumberFormatException ignored) {}
}
}
return chunks;
}
public void deleteRegionFiles(String world, Collection<ChunkLoc> chunks) {
deleteRegionFiles(world, chunks, null);
}
public void deleteRegionFiles(final String world, final Collection<ChunkLoc> chunks, final Runnable whenDone) {
TaskManager.runTaskAsync(new Runnable() {
@Override
public void run() {
for (ChunkLoc loc : chunks) {
String directory = world + File.separator + "region" + File.separator + "r." + loc.x + "." + loc.z + ".mca";
File file = new File(PS.get().IMP.getWorldContainer(), directory);
PS.log("&6 - Deleting file: " + file.getName() + " (max 1024 chunks)");
if (file.exists()) {
file.delete();
}
}
TaskManager.runTask(whenDone);
}
});
}
public Plot hasPlot(String world, ChunkLoc chunk) {
int x1 = chunk.x << 4;
int z1 = chunk.z << 4;
int x2 = x1 + 15;
int z2 = z1 + 15;
Location bot = new Location(world, x1, 0, z1);
Plot plot = bot.getOwnedPlotAbs();
if (plot != null) {
return plot;
}
Location top = new Location(world, x2, 0, z2);
plot = top.getOwnedPlotAbs();
if (plot != null) {
return plot;
}
return null;
}
/**
* Copy a region to a new location (in the same world)
*/
public abstract boolean copyRegion(Location pos1, Location pos2, Location newPos, Runnable whenDone);
/**
* Assumptions:<br>
* - pos1 and pos2 are in the same plot<br>
* It can be harmful to the world if parameters outside this scope are provided
* @param pos1
* @param pos2
* @param whenDone
* @return
*/
public abstract boolean regenerateRegion(Location pos1, Location pos2, boolean ignoreAugment, Runnable whenDone);
public abstract void clearAllEntities(Location pos1, Location pos2);
public abstract void swap(Location bot1, Location top1, Location bot2, Location top2, Runnable whenDone);
}