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:
tastybento 2024-05-31 17:59:56 -07:00
parent 7a241f898d
commit c3e03a4f59

View File

@ -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);
} }
}
} }