mirror of
https://github.com/BentoBoxWorld/Level.git
synced 2024-11-25 12:15:55 +01:00
Move to 1.20.4
Refactored the calculator code for clarity. Added Jacoco line to prvent issues with the bigger Material class.
This commit is contained in:
parent
cfb35909f0
commit
cec620162b
4
pom.xml
4
pom.xml
@ -58,7 +58,7 @@
|
||||
<!-- Non-minecraft related dependencies -->
|
||||
<powermock.version>2.0.9</powermock.version>
|
||||
<!-- More visible way how to change dependency versions -->
|
||||
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
|
||||
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
|
||||
<bentobox.version>2.0.0-SNAPSHOT</bentobox.version>
|
||||
<!-- Warps addon version -->
|
||||
<warps.version>1.12.0</warps.version>
|
||||
@ -410,6 +410,8 @@
|
||||
<!-- This is required to prevent Jacoco from adding
|
||||
synthetic fields to a JavaBean class (causes errors in testing) -->
|
||||
<exclude>**/*Names*</exclude>
|
||||
<!-- Prevents the Material is too large to mock error -->
|
||||
<exclude>org/bukkit/Material*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
|
@ -0,0 +1,121 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*/
|
||||
public class EquationEvaluator {
|
||||
|
||||
private static class Parser {
|
||||
private final String input;
|
||||
private int pos = -1;
|
||||
private int currentChar;
|
||||
|
||||
public Parser(String input) {
|
||||
this.input = input;
|
||||
moveToNextChar();
|
||||
}
|
||||
|
||||
private void moveToNextChar() {
|
||||
currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
|
||||
}
|
||||
|
||||
private boolean tryToEat(int charToEat) {
|
||||
while (currentChar == ' ')
|
||||
moveToNextChar();
|
||||
if (currentChar == charToEat) {
|
||||
moveToNextChar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public double evaluate() throws ParseException {
|
||||
double result = parseExpression();
|
||||
if (pos < input.length()) {
|
||||
throw new ParseException("Unexpected character: " + (char) currentChar, pos);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private double parseExpression() throws ParseException {
|
||||
double result = parseTerm();
|
||||
while (true) {
|
||||
if (tryToEat('+'))
|
||||
result += parseTerm();
|
||||
else if (tryToEat('-'))
|
||||
result -= parseTerm();
|
||||
else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private double parseFactor() throws ParseException {
|
||||
if (tryToEat('+'))
|
||||
return parseFactor(); // unary plus
|
||||
if (tryToEat('-'))
|
||||
return -parseFactor(); // unary minus
|
||||
|
||||
double x;
|
||||
int startPos = this.pos;
|
||||
if (tryToEat('(')) { // parentheses
|
||||
x = parseExpression();
|
||||
tryToEat(')');
|
||||
} else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
|
||||
while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.')
|
||||
moveToNextChar();
|
||||
x = Double.parseDouble(input.substring(startPos, this.pos));
|
||||
} else if (currentChar >= 'a' && currentChar <= 'z') { // functions
|
||||
while (currentChar >= 'a' && currentChar <= 'z')
|
||||
moveToNextChar();
|
||||
String func = input.substring(startPos, this.pos);
|
||||
x = parseFactor();
|
||||
switch (func) {
|
||||
case "sqrt":
|
||||
x = Math.sqrt(x);
|
||||
break;
|
||||
case "sin":
|
||||
x = Math.sin(Math.toRadians(x));
|
||||
break;
|
||||
case "cos":
|
||||
x = Math.cos(Math.toRadians(x));
|
||||
break;
|
||||
case "tan":
|
||||
x = Math.tan(Math.toRadians(x));
|
||||
break;
|
||||
case "log":
|
||||
x = Math.log(x);
|
||||
break;
|
||||
default:
|
||||
throw new ParseException("Unknown function: " + func, startPos);
|
||||
}
|
||||
} else {
|
||||
throw new ParseException("Unexpected: " + (char) currentChar, startPos);
|
||||
}
|
||||
|
||||
if (tryToEat('^'))
|
||||
x = Math.pow(x, parseFactor()); // exponentiation
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
private double parseTerm() throws ParseException {
|
||||
double x = parseFactor();
|
||||
for (;;) {
|
||||
if (tryToEat('*'))
|
||||
x *= parseFactor(); // multiplication
|
||||
else if (tryToEat('/'))
|
||||
x /= parseFactor(); // division
|
||||
else
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static double eval(final String equation) throws ParseException {
|
||||
return new Parser(equation).evaluate();
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -16,9 +16,6 @@ import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import com.songoda.ultimatestacker.UltimateStacker;
|
||||
import com.songoda.ultimatestacker.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.ultimatestacker.stackable.block.BlockStack;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
@ -27,7 +24,11 @@ import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.block.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Container;
|
||||
import org.bukkit.block.CreatureSpawner;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Slab;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -39,6 +40,9 @@ import com.bgsoftware.wildstacker.api.objects.StackedBarrel;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.Multiset.Entry;
|
||||
import com.google.common.collect.Multisets;
|
||||
import com.songoda.ultimatestacker.UltimateStacker;
|
||||
import com.songoda.ultimatestacker.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.ultimatestacker.stackable.block.BlockStack;
|
||||
|
||||
import dev.rosewood.rosestacker.api.RoseStackerAPI;
|
||||
import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI;
|
||||
@ -54,121 +58,21 @@ import world.bentobox.level.calculators.Results.Result;
|
||||
public class IslandLevelCalculator {
|
||||
private static final String LINE_BREAK = "==================================";
|
||||
public static final long MAX_AMOUNT = 10000000;
|
||||
private static final List<Material> CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART, Material.TRAPPED_CHEST,
|
||||
Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX, Material.BROWN_SHULKER_BOX,
|
||||
Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX,
|
||||
Material.LIGHT_GRAY_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
|
||||
private static final List<Material> CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART,
|
||||
Material.TRAPPED_CHEST, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
|
||||
Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX,
|
||||
Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX,
|
||||
Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
|
||||
Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.RED_SHULKER_BOX,
|
||||
Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL, Material.DISPENSER,
|
||||
Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
|
||||
Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL,
|
||||
Material.DISPENSER, Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
|
||||
private static final int CHUNKS_TO_SCAN = 100;
|
||||
|
||||
/**
|
||||
* Method to evaluate a mathematical equation
|
||||
* @param str - equation to evaluate
|
||||
* @return value of equation
|
||||
*/
|
||||
private static double eval(final String str) throws IOException {
|
||||
return new Object() {
|
||||
int pos = -1;
|
||||
int ch;
|
||||
|
||||
boolean eat(int charToEat) {
|
||||
while (ch == ' ') nextChar();
|
||||
if (ch == charToEat) {
|
||||
nextChar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void nextChar() {
|
||||
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
|
||||
}
|
||||
|
||||
double parse() throws IOException {
|
||||
nextChar();
|
||||
double x = parseExpression();
|
||||
if (pos < str.length()) throw new IOException("Unexpected: " + (char)ch);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Grammar:
|
||||
// expression = term | expression `+` term | expression `-` term
|
||||
// term = factor | term `*` factor | term `/` factor
|
||||
// factor = `+` factor | `-` factor | `(` expression `)`
|
||||
// | number | functionName factor | factor `^` factor
|
||||
|
||||
double parseExpression() throws IOException {
|
||||
double x = parseTerm();
|
||||
for (;;) {
|
||||
if (eat('+')) x += parseTerm(); // addition
|
||||
else if (eat('-')) x -= parseTerm(); // subtraction
|
||||
else return x;
|
||||
}
|
||||
}
|
||||
|
||||
double parseFactor() throws IOException {
|
||||
if (eat('+')) return parseFactor(); // unary plus
|
||||
if (eat('-')) return -parseFactor(); // unary minus
|
||||
|
||||
double x;
|
||||
int startPos = this.pos;
|
||||
if (eat('(')) { // parentheses
|
||||
x = parseExpression();
|
||||
eat(')');
|
||||
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
|
||||
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
|
||||
x = Double.parseDouble(str.substring(startPos, this.pos));
|
||||
} else if (ch >= 'a' && ch <= 'z') { // functions
|
||||
while (ch >= 'a' && ch <= 'z') nextChar();
|
||||
String func = str.substring(startPos, this.pos);
|
||||
x = parseFactor();
|
||||
switch (func) {
|
||||
case "sqrt":
|
||||
x = Math.sqrt(x);
|
||||
break;
|
||||
case "sin":
|
||||
x = Math.sin(Math.toRadians(x));
|
||||
break;
|
||||
case "cos":
|
||||
x = Math.cos(Math.toRadians(x));
|
||||
break;
|
||||
case "tan":
|
||||
x = Math.tan(Math.toRadians(x));
|
||||
break;
|
||||
case "log":
|
||||
x = Math.log(x);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown function: " + func);
|
||||
}
|
||||
} else {
|
||||
throw new IOException("Unexpected: " + (char)ch);
|
||||
}
|
||||
|
||||
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
double parseTerm() throws IOException {
|
||||
double x = parseFactor();
|
||||
for (;;) {
|
||||
if (eat('*')) x *= parseFactor(); // multiplication
|
||||
else if (eat('/')) x /= parseFactor(); // division
|
||||
else return x;
|
||||
}
|
||||
}
|
||||
}.parse();
|
||||
}
|
||||
private final Level addon;
|
||||
private final Queue<Pair<Integer, Integer>> chunksToCheck;
|
||||
private final Island island;
|
||||
private final Map<Material, Integer> limitCount;
|
||||
private final CompletableFuture<Results> r;
|
||||
|
||||
|
||||
private final Results results;
|
||||
private long duration;
|
||||
private final boolean zeroIsland;
|
||||
@ -178,12 +82,13 @@ public class IslandLevelCalculator {
|
||||
private final Set<Chunk> chestBlocks = new HashSet<>();
|
||||
private BukkitTask finishTask;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor to get the level for an island
|
||||
*
|
||||
* @param addon - Level addon
|
||||
* @param island - the island to scan
|
||||
* @param r - completable result that will be completed when the calculation is complete
|
||||
* @param r - completable result that will be completed when the
|
||||
* calculation is complete
|
||||
* @param zeroIsland - true if the calculation is due to an island zeroing
|
||||
*/
|
||||
public IslandLevelCalculator(Level addon, Island island, CompletableFuture<Results> r, boolean zeroIsland) {
|
||||
@ -219,25 +124,29 @@ public class IslandLevelCalculator {
|
||||
|
||||
/**
|
||||
* Calculate the level based on the raw points
|
||||
*
|
||||
* @param blockAndDeathPoints - raw points counted on island
|
||||
* @return level of island
|
||||
*/
|
||||
private long calculateLevel(long blockAndDeathPoints) {
|
||||
String calcString = addon.getSettings().getLevelCalc();
|
||||
String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost", String.valueOf(this.addon.getSettings().getLevelCost()));
|
||||
String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost",
|
||||
String.valueOf(this.addon.getSettings().getLevelCost()));
|
||||
long evalWithValues;
|
||||
try {
|
||||
evalWithValues = (long)eval(withValues);
|
||||
evalWithValues = (long) EquationEvaluator.eval(withValues);
|
||||
return evalWithValues - (addon.getSettings().isZeroNewIslandLevels() ? results.initialLevel.get() : 0);
|
||||
|
||||
} catch (IOException e) {
|
||||
} catch (ParseException e) {
|
||||
addon.getPlugin().logStacktrace(e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds value to the results based on the material and whether the block is below sea level or not
|
||||
* Adds value to the results based on the material and whether the block is
|
||||
* below sea level or not
|
||||
*
|
||||
* @param mat - material of the block
|
||||
* @param belowSeaLevel - true if below sea level
|
||||
*/
|
||||
@ -254,20 +163,22 @@ public class IslandLevelCalculator {
|
||||
|
||||
/**
|
||||
* Get a set of all the chunks in island
|
||||
*
|
||||
* @param island - island
|
||||
* @return - set of pairs of x,z coordinates to check
|
||||
*/
|
||||
private Queue<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
||||
Queue<Pair<Integer, Integer>> chunkQueue = new ConcurrentLinkedQueue<>();
|
||||
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
|
||||
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
|
||||
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2
|
||||
+ 16); x += 16) {
|
||||
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2
|
||||
+ 16); z += 16) {
|
||||
chunkQueue.add(new Pair<>(x >> 4, z >> 4));
|
||||
}
|
||||
}
|
||||
return chunkQueue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the island
|
||||
*/
|
||||
@ -277,6 +188,7 @@ public class IslandLevelCalculator {
|
||||
|
||||
/**
|
||||
* Get the completable result for this calculation
|
||||
*
|
||||
* @return the r
|
||||
*/
|
||||
public CompletableFuture<Results> getR() {
|
||||
@ -285,12 +197,14 @@ public class IslandLevelCalculator {
|
||||
|
||||
/**
|
||||
* Get the full analysis report
|
||||
*
|
||||
* @return a list of lines
|
||||
*/
|
||||
private List<String> getReport() {
|
||||
List<String> reportLines = new ArrayList<>();
|
||||
// provide counts
|
||||
reportLines.add("Level Log for island in " + addon.getPlugin().getIWM().getFriendlyName(island.getWorld()) + " at " + Util.xyz(island.getCenter().toVector()));
|
||||
reportLines.add("Level Log for island in " + addon.getPlugin().getIWM().getFriendlyName(island.getWorld())
|
||||
+ " at " + Util.xyz(island.getCenter().toVector()));
|
||||
reportLines.add("Island owner UUID = " + island.getOwner());
|
||||
reportLines.add("Total block value count = " + String.format("%,d", results.rawBlockCount.get()));
|
||||
reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
|
||||
@ -304,7 +218,8 @@ public class IslandLevelCalculator {
|
||||
reportLines.add(LINE_BREAK);
|
||||
int total = 0;
|
||||
if (!results.uwCount.isEmpty()) {
|
||||
reportLines.add("Underwater block count (Multiplier = x" + addon.getSettings().getUnderWaterMultiplier() + ") value");
|
||||
reportLines.add("Underwater block count (Multiplier = x" + addon.getSettings().getUnderWaterMultiplier()
|
||||
+ ") value");
|
||||
reportLines.add("Total number of underwater blocks = " + String.format("%,d", results.uwCount.size()));
|
||||
reportLines.addAll(sortedReport(total, results.uwCount));
|
||||
}
|
||||
@ -312,7 +227,8 @@ public class IslandLevelCalculator {
|
||||
reportLines.add("Total number of blocks = " + String.format("%,d", results.mdCount.size()));
|
||||
reportLines.addAll(sortedReport(total, results.mdCount));
|
||||
|
||||
reportLines.add("Blocks not counted because they exceeded limits: " + String.format("%,d",results.ofCount.size()));
|
||||
reportLines.add(
|
||||
"Blocks not counted because they exceeded limits: " + String.format("%,d", results.ofCount.size()));
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = results.ofCount.entrySet();
|
||||
Iterator<Entry<Material>> it = entriesSortedByCount.iterator();
|
||||
while (it.hasNext()) {
|
||||
@ -324,7 +240,8 @@ public class IslandLevelCalculator {
|
||||
limit = addon.getBlockConfig().getBlockLimits().get(generic);
|
||||
explain = " - All types)";
|
||||
}
|
||||
reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks (max " + limit + explain);
|
||||
reportLines.add(type.getElement().toString() + ": " + String.format("%,d", type.getCount())
|
||||
+ " blocks (max " + limit + explain);
|
||||
}
|
||||
reportLines.add(LINE_BREAK);
|
||||
reportLines.add("Blocks on island that are not in config.yml");
|
||||
@ -346,9 +263,10 @@ public class IslandLevelCalculator {
|
||||
public Results getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value of a material
|
||||
* World blocks trump regular block values
|
||||
* Get value of a material World blocks trump regular block values
|
||||
*
|
||||
* @param md - Material to check
|
||||
* @return value of a material
|
||||
*/
|
||||
@ -364,9 +282,11 @@ public class IslandLevelCalculator {
|
||||
|
||||
/**
|
||||
* Get a chunk async
|
||||
*
|
||||
* @param env - the environment
|
||||
* @param pairList - chunk coordinate
|
||||
* @return a future chunk or future null if there is no chunk to load, e.g., there is no island nether
|
||||
* @return a future chunk or future null if there is no chunk to load, e.g.,
|
||||
* there is no island nether
|
||||
*/
|
||||
private CompletableFuture<List<Chunk>> getWorldChunk(Environment env, Queue<Pair<Integer, Integer>> pairList) {
|
||||
if (worlds.containsKey(env)) {
|
||||
@ -410,7 +330,9 @@ public class IslandLevelCalculator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block has been limited or not and whether a block has any value or not
|
||||
* Checks if a block has been limited or not and whether a block has any value
|
||||
* or not
|
||||
*
|
||||
* @param md Material
|
||||
* @return value of the block if can be counted
|
||||
*/
|
||||
@ -428,9 +350,9 @@ public class IslandLevelCalculator {
|
||||
return getValue(md);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan all containers in a chunk and count their blocks
|
||||
*
|
||||
* @param chunk - the chunk to scan
|
||||
*/
|
||||
private void scanChests(Chunk chunk) {
|
||||
@ -455,7 +377,8 @@ public class IslandLevelCalculator {
|
||||
}
|
||||
|
||||
private void countItemStack(ItemStack i) {
|
||||
if (i == null || !i.getType().isBlock()) return;
|
||||
if (i == null || !i.getType().isBlock())
|
||||
return;
|
||||
|
||||
for (int c = 0; c < i.getAmount(); c++) {
|
||||
if (addon.getSettings().isIncludeShulkersInChest()
|
||||
@ -469,10 +392,13 @@ public class IslandLevelCalculator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the chunk chests and count the blocks. Note that the chunks are a list of all the island chunks
|
||||
* in a particular world, so the memory usage is high, but I think most servers can handle it.
|
||||
* Scan the chunk chests and count the blocks. Note that the chunks are a list
|
||||
* of all the island chunks in a particular world, so the memory usage is high,
|
||||
* but I think most servers can handle it.
|
||||
*
|
||||
* @param chunks - a list of chunks to scan
|
||||
* @return future that completes when the scan is done and supplies a boolean that will be true if the scan was successful, false if not
|
||||
* @return future that completes when the scan is done and supplies a boolean
|
||||
* that will be true if the scan was successful, false if not
|
||||
*/
|
||||
private CompletableFuture<Boolean> scanChunk(List<Chunk> chunks) {
|
||||
// If the chunk hasn't been generated, return
|
||||
@ -482,11 +408,13 @@ public class IslandLevelCalculator {
|
||||
// Count blocks in chunk
|
||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||
/*
|
||||
* At this point, we need to grab a snapshot of each chunk and then scan it async.
|
||||
* At the end, we make the CompletableFuture true to show it is done.
|
||||
* I'm not sure how much lag this will cause, but as all the chunks are loaded, maybe not that much.
|
||||
* At this point, we need to grab a snapshot of each chunk and then scan it
|
||||
* async. At the end, we make the CompletableFuture true to show it is done. I'm
|
||||
* not sure how much lag this will cause, but as all the chunks are loaded,
|
||||
* maybe not that much.
|
||||
*/
|
||||
List<ChunkPair> preLoad = chunks.stream().map(c -> new ChunkPair(c.getWorld(), c, c.getChunkSnapshot())).toList();
|
||||
List<ChunkPair> preLoad = chunks.stream().map(c -> new ChunkPair(c.getWorld(), c, c.getChunkSnapshot()))
|
||||
.toList();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
|
||||
preLoad.forEach(this::scanAsync);
|
||||
// Once they are all done, return to the main thread.
|
||||
@ -495,21 +423,27 @@ public class IslandLevelCalculator {
|
||||
return result;
|
||||
}
|
||||
|
||||
record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {}
|
||||
record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the blocks on the island
|
||||
*
|
||||
* @param cp chunk to scan
|
||||
*/
|
||||
private void scanAsync(ChunkPair cp) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||
if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't
|
||||
// count it
|
||||
if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16
|
||||
+ x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
||||
continue;
|
||||
}
|
||||
for (int z = 0; z < 16; z++) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||
if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't
|
||||
// count it
|
||||
if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16
|
||||
+ z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
||||
continue;
|
||||
}
|
||||
// Only count to the highest block in the world for some optimization
|
||||
@ -523,16 +457,20 @@ public class IslandLevelCalculator {
|
||||
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
// Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real chunk
|
||||
if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON) || blockData.getMaterial().equals(Material.SPAWNER))) {
|
||||
stackedBlocks.add(new Location(cp.world, (double)x + cp.chunkSnapshot.getX() * 16, y, (double)z + cp.chunkSnapshot.getZ() * 16));
|
||||
// Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
|
||||
// chunk
|
||||
if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON)
|
||||
|| blockData.getMaterial().equals(Material.SPAWNER))) {
|
||||
stackedBlocks.add(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y,
|
||||
(double) z + cp.chunkSnapshot.getZ() * 16));
|
||||
}
|
||||
|
||||
Block block = cp.chunk.getBlock(x, y, z);
|
||||
|
||||
if (addon.isUltimateStackerEnabled()) {
|
||||
if (!blockData.getMaterial().equals(Material.AIR)) {
|
||||
BlockStack stack = UltimateStacker.getInstance().getBlockStackManager().getBlock(block, CompatibleMaterial.getMaterial(block));
|
||||
BlockStack stack = UltimateStacker.getInstance().getBlockStackManager().getBlock(block,
|
||||
CompatibleMaterial.getMaterial(block));
|
||||
if (stack != null) {
|
||||
int value = limitCount(blockData.getMaterial());
|
||||
if (belowSeaLevel) {
|
||||
@ -559,7 +497,9 @@ public class IslandLevelCalculator {
|
||||
|
||||
/**
|
||||
* Scan the next chunk on the island
|
||||
* @return completable boolean future that will be true if more chunks are left to be scanned, and false if not
|
||||
*
|
||||
* @return completable boolean future that will be true if more chunks are left
|
||||
* to be scanned, and false if not
|
||||
*/
|
||||
public CompletableFuture<Boolean> scanNextChunk() {
|
||||
if (chunksToCheck.isEmpty()) {
|
||||
@ -579,31 +519,28 @@ public class IslandLevelCalculator {
|
||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||
// Get chunks and scan
|
||||
// Get chunks and scan
|
||||
getWorldChunk(Environment.THE_END, endPairList).thenAccept(endChunks ->
|
||||
scanChunk(endChunks).thenAccept(b ->
|
||||
getWorldChunk(Environment.NETHER, netherPairList).thenAccept(netherChunks ->
|
||||
scanChunk(netherChunks).thenAccept(b2 ->
|
||||
getWorldChunk(Environment.NORMAL, pairList).thenAccept(normalChunks ->
|
||||
scanChunk(normalChunks).thenAccept(b3 ->
|
||||
getWorldChunk(Environment.THE_END, endPairList).thenAccept(
|
||||
endChunks -> scanChunk(endChunks).thenAccept(b -> getWorldChunk(Environment.NETHER, netherPairList)
|
||||
.thenAccept(netherChunks -> scanChunk(netherChunks)
|
||||
.thenAccept(b2 -> getWorldChunk(Environment.NORMAL, pairList)
|
||||
.thenAccept(normalChunks -> scanChunk(normalChunks).thenAccept(b3 ->
|
||||
// Complete the result now that all chunks have been scanned
|
||||
result.complete(!chunksToCheck.isEmpty()))))
|
||||
)
|
||||
)
|
||||
);
|
||||
result.complete(!chunksToCheck.isEmpty())))))));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<String> sortedReport(int total, Multiset<Material> materialCount) {
|
||||
Collection<String> result = new ArrayList<>();
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = Multisets.copyHighestCountFirst(materialCount).entrySet();
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = Multisets.copyHighestCountFirst(materialCount)
|
||||
.entrySet();
|
||||
for (Entry<Material> en : entriesSortedByCount) {
|
||||
Material type = en.getElement();
|
||||
|
||||
int value = getValue(type);
|
||||
|
||||
result.add(type.toString() + ":"
|
||||
+ String.format("%,d", en.getCount()) + " blocks x " + value + " = " + (value * en.getCount()));
|
||||
result.add(type.toString() + ":" + String.format("%,d", en.getCount()) + " blocks x " + value + " = "
|
||||
+ (value * en.getCount()));
|
||||
total += (value * en.getCount());
|
||||
|
||||
}
|
||||
@ -612,34 +549,29 @@ public class IslandLevelCalculator {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finalizes the calculations and makes the report
|
||||
*/
|
||||
public void tidyUp() {
|
||||
// Finalize calculations
|
||||
results.rawBlockCount.addAndGet((long)(results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
|
||||
results.rawBlockCount
|
||||
.addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
|
||||
|
||||
// Set the death penalty
|
||||
if (this.addon.getSettings().isSumTeamDeaths())
|
||||
{
|
||||
for (UUID uuid : this.island.getMemberSet())
|
||||
{
|
||||
if (this.addon.getSettings().isSumTeamDeaths()) {
|
||||
for (UUID uuid : this.island.getMemberSet()) {
|
||||
this.results.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(island.getWorld(), uuid));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// At this point, it may be that the island has become unowned.
|
||||
this.results.deathHandicap.set(this.island.getOwner() == null ? 0 :
|
||||
this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
|
||||
this.results.deathHandicap.set(this.island.getOwner() == null ? 0
|
||||
: this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
|
||||
}
|
||||
|
||||
long blockAndDeathPoints = this.results.rawBlockCount.get();
|
||||
this.results.totalPoints.set(blockAndDeathPoints);
|
||||
|
||||
if (this.addon.getSettings().getDeathPenalty() > 0)
|
||||
{
|
||||
if (this.addon.getSettings().getDeathPenalty() > 0) {
|
||||
// Proper death penalty calculation.
|
||||
blockAndDeathPoints -= this.results.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
|
||||
}
|
||||
@ -674,11 +606,13 @@ public class IslandLevelCalculator {
|
||||
addon.getPlugin().logError("scanChunk not on Primary Thread!");
|
||||
}
|
||||
// Timeout check
|
||||
if (System.currentTimeMillis() - pipeliner.getInProcessQueue().get(this) > addon.getSettings().getCalculationTimeout() * 60000) {
|
||||
if (System.currentTimeMillis()
|
||||
- pipeliner.getInProcessQueue().get(this) > addon.getSettings().getCalculationTimeout() * 60000) {
|
||||
// Done
|
||||
pipeliner.getInProcessQueue().remove(this);
|
||||
getR().complete(new Results(Result.TIMEOUT));
|
||||
addon.logError("Level calculation timed out after " + addon.getSettings().getCalculationTimeout() + "m for island: " + getIsland());
|
||||
addon.logError("Level calculation timed out after " + addon.getSettings().getCalculationTimeout()
|
||||
+ "m for island: " + getIsland());
|
||||
if (!isNotZeroIsland()) {
|
||||
addon.logError("Island level was being zeroed.");
|
||||
}
|
||||
@ -697,7 +631,8 @@ public class IslandLevelCalculator {
|
||||
long checkTime = System.currentTimeMillis();
|
||||
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) {
|
||||
if ((chestBlocks.isEmpty() && stackedBlocks.isEmpty())
|
||||
|| System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
|
||||
this.tidyUp();
|
||||
this.getR().complete(getResults());
|
||||
finishTask.cancel();
|
||||
|
Loading…
Reference in New Issue
Block a user