Skip to content

Commit

Permalink
Improved recount code. Should be accurate.
Browse files Browse the repository at this point in the history
This uses the latest approach done by Level addon. This does not yet
include entity recounting!

#123
  • Loading branch information
tastybento committed Dec 26, 2021
1 parent 4cff598 commit 343d7bb
Show file tree
Hide file tree
Showing 9 changed files with 608 additions and 169 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@
<!-- Non-minecraft related dependencies -->
<powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.16.1-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.16.5-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.18.0-SNAPSHOT</bentobox.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.18.1</build.version>
<build.version>1.19.0</build.version>
<sonar.projectKey>BentoBoxWorld_Limits</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
Expand Down
151 changes: 151 additions & 0 deletions src/main/java/world/bentobox/limits/calculators/Pipeliner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package world.bentobox.limits.calculators;

import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;

import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.limits.Limits;
import world.bentobox.limits.calculators.Results.Result;

/**
* A pipeliner that will process one island at a time
* @author tastybento
*
*/
public class Pipeliner {

private static final int START_DURATION = 10; // 10 seconds
private static final int CONCURRENT_COUNTS = 1;
private final Queue<RecountCalculator> toProcessQueue;
private final Map<RecountCalculator, Long> inProcessQueue;
private final BukkitTask task;
private final Limits addon;
private long time;
private long count;

/**
* Construct the pipeliner
*/
public Pipeliner(Limits addon) {
this.addon = addon;
toProcessQueue = new ConcurrentLinkedQueue<>();
inProcessQueue = new HashMap<>();
// Loop continuously - check every tick if there is an island to scan
task = Bukkit.getScheduler().runTaskTimer(BentoBox.getInstance(), () -> {
if (!BentoBox.getInstance().isEnabled()) {
cancel();
return;
}
// Complete the current to Process queue first
if (!inProcessQueue.isEmpty() || toProcessQueue.isEmpty()) return;
for (int j = 0; j < CONCURRENT_COUNTS && !toProcessQueue.isEmpty(); j++) {
RecountCalculator iD = toProcessQueue.poll();
// Ignore deleted or unonwed islands
if (!iD.getIsland().isDeleted() && !iD.getIsland().isUnowned()) {
inProcessQueue.put(iD, System.currentTimeMillis());
// Start the scanning of a island with the first chunk
scanIsland(iD);
}
}
}, 1L, 10L);
}

private void cancel() {
task.cancel();
}

/**
* @return number of islands currently in the queue or in process
*/
public int getIslandsInQueue() {
return inProcessQueue.size() + toProcessQueue.size();
}

/**
* Scans one chunk of an island and adds the results to a results object
* @param iD
*/
private void scanIsland(RecountCalculator iD) {
if (iD.getIsland().isDeleted() || iD.getIsland().isUnowned() || task.isCancelled()) {
// Island is deleted, so finish early with nothing
inProcessQueue.remove(iD);
iD.getR().complete(null);
return;
}
iD.scanIsland(this);
}


/**
* Adds an island to the scanning queue but only if the island is not already in the queue
* @param island - the island to scan
* @return CompletableFuture of the results. Results will be null if the island is already in the queue
*/
public CompletableFuture<Results> addIsland(Island island) {
// Check if queue already contains island
if (inProcessQueue.keySet().parallelStream().map(RecountCalculator::getIsland).anyMatch(island::equals)
|| toProcessQueue.parallelStream().map(RecountCalculator::getIsland).anyMatch(island::equals)) {
return CompletableFuture.completedFuture(new Results(Result.IN_PROGRESS));
}
return addToQueue(island);
}

private CompletableFuture<Results> addToQueue(Island island) {
CompletableFuture<Results> r = new CompletableFuture<>();
toProcessQueue.add(new RecountCalculator(addon, island, r));
count++;
return r;
}

/**
* Get the average time it takes to run a level check
* @return the average time in seconds
*/
public int getTime() {
return time == 0 || count == 0 ? START_DURATION : (int)((double)time/count/1000);
}

/**
* Submit how long a level check took
* @param time the time to set
*/
public void setTime(long time) {
// Running average
this.time += time;
}

/**
* Stop the current queue.
*/
public void stop() {
addon.log("Stopping Level queue");
task.cancel();
this.inProcessQueue.clear();
this.toProcessQueue.clear();
}

/**
* @return the inProcessQueue
*/
protected Map<RecountCalculator, Long> getInProcessQueue() {
return inProcessQueue;
}

/**
* @return the task
*/
protected BukkitTask getTask() {
return task;
}




}
Loading

0 comments on commit 343d7bb

Please sign in to comment.