mirror of
https://github.com/BentoBoxWorld/Level.git
synced 2024-09-20 18:11:05 +02:00
Uses better approach #313
This uses CompleteableFutures instead of a recurring Bukkit task to check if collections have been removed. This is a much more reliable way to do it because it will complete when all the tasks are done and not before.
This commit is contained in:
parent
7a241f898d
commit
c3e03a4f59
@ -89,34 +89,34 @@ public class IslandLevelCalculator {
|
|||||||
* @param zeroIsland - true if the calculation is due to an island zeroing
|
* @param zeroIsland - true if the calculation is due to an island zeroing
|
||||||
*/
|
*/
|
||||||
public IslandLevelCalculator(Level addon, Island island, CompletableFuture<Results> r, boolean zeroIsland) {
|
public IslandLevelCalculator(Level addon, Island island, CompletableFuture<Results> r, boolean zeroIsland) {
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
this.island = island;
|
this.island = island;
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.zeroIsland = zeroIsland;
|
this.zeroIsland = zeroIsland;
|
||||||
results = new Results();
|
results = new Results();
|
||||||
duration = System.currentTimeMillis();
|
duration = System.currentTimeMillis();
|
||||||
chunksToCheck = getChunksToScan(island);
|
chunksToCheck = getChunksToScan(island);
|
||||||
this.limitCount = new EnumMap<>(addon.getBlockConfig().getBlockLimits());
|
this.limitCount = new EnumMap<>(addon.getBlockConfig().getBlockLimits());
|
||||||
// Get the initial island level
|
// Get the initial island level
|
||||||
results.initialLevel.set(addon.getInitialIslandLevel(island));
|
results.initialLevel.set(addon.getInitialIslandLevel(island));
|
||||||
// Set up the worlds
|
// Set up the worlds
|
||||||
worlds.put(Environment.NORMAL, Util.getWorld(island.getWorld()));
|
worlds.put(Environment.NORMAL, Util.getWorld(island.getWorld()));
|
||||||
// Nether
|
// Nether
|
||||||
if (addon.getSettings().isNether()) {
|
if (addon.getSettings().isNether()) {
|
||||||
World nether = addon.getPlugin().getIWM().getNetherWorld(island.getWorld());
|
World nether = addon.getPlugin().getIWM().getNetherWorld(island.getWorld());
|
||||||
if (nether != null) {
|
if (nether != null) {
|
||||||
worlds.put(Environment.NETHER, nether);
|
worlds.put(Environment.NETHER, nether);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End
|
// End
|
||||||
if (addon.getSettings().isEnd()) {
|
if (addon.getSettings().isEnd()) {
|
||||||
World end = addon.getPlugin().getIWM().getEndWorld(island.getWorld());
|
World end = addon.getPlugin().getIWM().getEndWorld(island.getWorld());
|
||||||
if (end != null) {
|
if (end != null) {
|
||||||
worlds.put(Environment.THE_END, end);
|
worlds.put(Environment.THE_END, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sea Height
|
// Sea Height
|
||||||
seaHeight = addon.getPlugin().getIWM().getSeaHeight(island.getWorld());
|
seaHeight = addon.getPlugin().getIWM().getSeaHeight(island.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,14 +148,14 @@ public class IslandLevelCalculator {
|
|||||||
* @param belowSeaLevel - true if below sea level
|
* @param belowSeaLevel - true if below sea level
|
||||||
*/
|
*/
|
||||||
private void checkBlock(Material mat, boolean belowSeaLevel) {
|
private void checkBlock(Material mat, boolean belowSeaLevel) {
|
||||||
int count = limitCount(mat);
|
int count = limitCount(mat);
|
||||||
if (belowSeaLevel) {
|
if (belowSeaLevel) {
|
||||||
results.underWaterBlockCount.addAndGet(count);
|
results.underWaterBlockCount.addAndGet(count);
|
||||||
results.uwCount.add(mat);
|
results.uwCount.add(mat);
|
||||||
} else {
|
} else {
|
||||||
results.rawBlockCount.addAndGet(count);
|
results.rawBlockCount.addAndGet(count);
|
||||||
results.mdCount.add(mat);
|
results.mdCount.add(mat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,7 +180,7 @@ public class IslandLevelCalculator {
|
|||||||
* @return the island
|
* @return the island
|
||||||
*/
|
*/
|
||||||
public Island getIsland() {
|
public Island getIsland() {
|
||||||
return island;
|
return island;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -189,7 +189,7 @@ public class IslandLevelCalculator {
|
|||||||
* @return the r
|
* @return the r
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<Results> getR() {
|
public CompletableFuture<Results> getR() {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -258,7 +258,7 @@ public class IslandLevelCalculator {
|
|||||||
* @return the results
|
* @return the results
|
||||||
*/
|
*/
|
||||||
public Results getResults() {
|
public Results getResults() {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,13 +268,13 @@ public class IslandLevelCalculator {
|
|||||||
* @return value of a material
|
* @return value of a material
|
||||||
*/
|
*/
|
||||||
private int getValue(Material md) {
|
private int getValue(Material md) {
|
||||||
Integer value = addon.getBlockConfig().getValue(island.getWorld(), md);
|
Integer value = addon.getBlockConfig().getValue(island.getWorld(), md);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
// Not in config
|
// Not in config
|
||||||
results.ncCount.add(md);
|
results.ncCount.add(md);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,44 +286,44 @@ public class IslandLevelCalculator {
|
|||||||
* there is no island nether
|
* there is no island nether
|
||||||
*/
|
*/
|
||||||
private CompletableFuture<List<Chunk>> getWorldChunk(Environment env, Queue<Pair<Integer, Integer>> pairList) {
|
private CompletableFuture<List<Chunk>> getWorldChunk(Environment env, Queue<Pair<Integer, Integer>> pairList) {
|
||||||
if (worlds.containsKey(env)) {
|
if (worlds.containsKey(env)) {
|
||||||
CompletableFuture<List<Chunk>> r2 = new CompletableFuture<>();
|
CompletableFuture<List<Chunk>> r2 = new CompletableFuture<>();
|
||||||
List<Chunk> chunkList = new ArrayList<>();
|
List<Chunk> chunkList = new ArrayList<>();
|
||||||
World world = worlds.get(env);
|
World world = worlds.get(env);
|
||||||
// Get the chunk, and then coincidentally check the RoseStacker
|
// Get the chunk, and then coincidentally check the RoseStacker
|
||||||
loadChunks(r2, world, pairList, chunkList);
|
loadChunks(r2, world, pairList, chunkList);
|
||||||
return r2;
|
return r2;
|
||||||
}
|
}
|
||||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadChunks(CompletableFuture<List<Chunk>> r2, World world, Queue<Pair<Integer, Integer>> pairList,
|
private void loadChunks(CompletableFuture<List<Chunk>> r2, World world, Queue<Pair<Integer, Integer>> pairList,
|
||||||
List<Chunk> chunkList) {
|
List<Chunk> chunkList) {
|
||||||
if (pairList.isEmpty()) {
|
if (pairList.isEmpty()) {
|
||||||
r2.complete(chunkList);
|
r2.complete(chunkList);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Pair<Integer, Integer> p = pairList.poll();
|
Pair<Integer, Integer> p = pairList.poll();
|
||||||
Util.getChunkAtAsync(world, p.x, p.z, world.getEnvironment().equals(Environment.NETHER)).thenAccept(chunk -> {
|
Util.getChunkAtAsync(world, p.x, p.z, world.getEnvironment().equals(Environment.NETHER)).thenAccept(chunk -> {
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
chunkList.add(chunk);
|
chunkList.add(chunk);
|
||||||
roseStackerCheck(chunk);
|
roseStackerCheck(chunk);
|
||||||
}
|
}
|
||||||
loadChunks(r2, world, pairList, chunkList); // Iteration
|
loadChunks(r2, world, pairList, chunkList); // Iteration
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void roseStackerCheck(Chunk chunk) {
|
private void roseStackerCheck(Chunk chunk) {
|
||||||
if (addon.isRoseStackersEnabled()) {
|
if (addon.isRoseStackersEnabled()) {
|
||||||
RoseStackerAPI.getInstance().getStackedBlocks(Collections.singletonList(chunk)).forEach(e -> {
|
RoseStackerAPI.getInstance().getStackedBlocks(Collections.singletonList(chunk)).forEach(e -> {
|
||||||
// Blocks below sea level can be scored differently
|
// Blocks below sea level can be scored differently
|
||||||
boolean belowSeaLevel = seaHeight > 0 && e.getLocation().getY() <= seaHeight;
|
boolean belowSeaLevel = seaHeight > 0 && e.getLocation().getY() <= seaHeight;
|
||||||
// Check block once because the base block will be counted in the chunk snapshot
|
// Check block once because the base block will be counted in the chunk snapshot
|
||||||
for (int _x = 0; _x < e.getStackSize() - 1; _x++) {
|
for (int _x = 0; _x < e.getStackSize() - 1; _x++) {
|
||||||
checkBlock(e.getBlock().getType(), belowSeaLevel);
|
checkBlock(e.getBlock().getType(), belowSeaLevel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -334,17 +334,17 @@ public class IslandLevelCalculator {
|
|||||||
* @return value of the block if can be counted
|
* @return value of the block if can be counted
|
||||||
*/
|
*/
|
||||||
private int limitCount(Material md) {
|
private int limitCount(Material md) {
|
||||||
if (limitCount.containsKey(md)) {
|
if (limitCount.containsKey(md)) {
|
||||||
int count = limitCount.get(md);
|
int count = limitCount.get(md);
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
limitCount.put(md, --count);
|
limitCount.put(md, --count);
|
||||||
return getValue(md);
|
return getValue(md);
|
||||||
} else {
|
} else {
|
||||||
results.ofCount.add(md);
|
results.ofCount.add(md);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getValue(md);
|
return getValue(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -579,7 +579,7 @@ public class IslandLevelCalculator {
|
|||||||
* @return the zeroIsland
|
* @return the zeroIsland
|
||||||
*/
|
*/
|
||||||
boolean isNotZeroIsland() {
|
boolean isNotZeroIsland() {
|
||||||
return !zeroIsland;
|
return !zeroIsland;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scanIsland(Pipeliner pipeliner) {
|
public void scanIsland(Pipeliner pipeliner) {
|
||||||
@ -608,67 +608,52 @@ public class IslandLevelCalculator {
|
|||||||
// Done
|
// Done
|
||||||
pipeliner.getInProcessQueue().remove(this);
|
pipeliner.getInProcessQueue().remove(this);
|
||||||
// Chunk finished
|
// Chunk finished
|
||||||
// This was the last chunk
|
// This was the last chunk. Handle stacked blocks, then chests and exit
|
||||||
handleStackedBlocks();
|
handleStackedBlocks().thenCompose(v -> handleChests()).thenRun(() -> {
|
||||||
handleChests();
|
this.tidyUp();
|
||||||
long checkTime = System.currentTimeMillis();
|
this.getR().complete(getResults());
|
||||||
finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
|
});
|
||||||
// Check every half second if all the chests and stacks have been cleared
|
|
||||||
if ((chestBlocks.isEmpty() && stackedBlocks.isEmpty())
|
|
||||||
|| System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
|
|
||||||
this.tidyUp();
|
|
||||||
this.getR().complete(getResults());
|
|
||||||
finishTask.cancel();
|
|
||||||
}
|
|
||||||
}, 0, 10L);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleChests() {
|
private CompletableFuture<Void> handleChests() {
|
||||||
Iterator<Chunk> it = chestBlocks.iterator();
|
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||||
while (it.hasNext()) {
|
for (Chunk v : chestBlocks) {
|
||||||
Chunk v = it.next();
|
CompletableFuture<Void> future = Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
|
||||||
Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
|
|
||||||
scanChests(c);
|
scanChests(c);
|
||||||
it.remove();
|
|
||||||
});
|
});
|
||||||
|
futures.add(future);
|
||||||
}
|
}
|
||||||
|
// Return a CompletableFuture that completes when all futures are done
|
||||||
|
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleStackedBlocks() {
|
private CompletableFuture<Void> handleStackedBlocks() {
|
||||||
// Deal with any stacked blocks
|
// Deal with any stacked blocks
|
||||||
List<Location> toRemove = new ArrayList<>();
|
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||||
Iterator<Location> it = stackedBlocks.iterator();
|
for (Location v : stackedBlocks) {
|
||||||
while (it.hasNext()) {
|
CompletableFuture<Void> future = Util.getChunkAtAsync(v).thenAccept(c -> {
|
||||||
Location v = it.next();
|
Block stackedBlock = v.getBlock();
|
||||||
Util.getChunkAtAsync(v).thenAccept(c -> {
|
boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
|
||||||
Block stackedBlock = v.getBlock();
|
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
|
||||||
boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
|
StackedBarrel barrel = WildStackerAPI.getStackedBarrel(stackedBlock);
|
||||||
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
|
int barrelAmt = WildStackerAPI.getBarrelAmount(stackedBlock);
|
||||||
StackedBarrel barrel = WildStackerAPI.getStackedBarrel(stackedBlock);
|
for (int _x = 0; _x < barrelAmt; _x++) {
|
||||||
int barrelAmt = WildStackerAPI.getBarrelAmount(stackedBlock);
|
checkBlock(barrel.getType(), belowSeaLevel);
|
||||||
for (int _x = 0; _x < barrelAmt; _x++) {
|
}
|
||||||
checkBlock(barrel.getType(), belowSeaLevel);
|
} else if (WildStackerAPI.getWildStacker().getSystemManager().isStackedSpawner(stackedBlock)) {
|
||||||
|
int spawnerAmt = WildStackerAPI.getSpawnersAmount((CreatureSpawner) stackedBlock.getState());
|
||||||
|
for (int _x = 0; _x < spawnerAmt; _x++) {
|
||||||
|
checkBlock(stackedBlock.getType(), belowSeaLevel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (WildStackerAPI.getWildStacker().getSystemManager().isStackedSpawner(stackedBlock)) {
|
});
|
||||||
int spawnerAmt = WildStackerAPI.getSpawnersAmount((CreatureSpawner) stackedBlock.getState());
|
futures.add(future);
|
||||||
for (int _x = 0; _x < spawnerAmt; _x++) {
|
}
|
||||||
checkBlock(stackedBlock.getType(), belowSeaLevel);
|
// Return a CompletableFuture that completes when all futures are done
|
||||||
}
|
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||||
}
|
|
||||||
synchronized (toRemove) {
|
|
||||||
toRemove.add(v);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all asynchronous tasks to complete before removing elements
|
|
||||||
// Remove the elements collected in toRemove
|
|
||||||
synchronized (toRemove) {
|
|
||||||
stackedBlocks.removeAll(toRemove);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user