mirror of
https://github.com/BentoBoxWorld/Level.git
synced 2024-11-25 20:25:28 +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 -->
|
<!-- Non-minecraft related dependencies -->
|
||||||
<powermock.version>2.0.9</powermock.version>
|
<powermock.version>2.0.9</powermock.version>
|
||||||
<!-- More visible way how to change dependency versions -->
|
<!-- 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>
|
<bentobox.version>2.0.0-SNAPSHOT</bentobox.version>
|
||||||
<!-- Warps addon version -->
|
<!-- Warps addon version -->
|
||||||
<warps.version>1.12.0</warps.version>
|
<warps.version>1.12.0</warps.version>
|
||||||
@ -410,6 +410,8 @@
|
|||||||
<!-- This is required to prevent Jacoco from adding
|
<!-- This is required to prevent Jacoco from adding
|
||||||
synthetic fields to a JavaBean class (causes errors in testing) -->
|
synthetic fields to a JavaBean class (causes errors in testing) -->
|
||||||
<exclude>**/*Names*</exclude>
|
<exclude>**/*Names*</exclude>
|
||||||
|
<!-- Prevents the Material is too large to mock error -->
|
||||||
|
<exclude>org/bukkit/Material*</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<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;
|
package world.bentobox.level.calculators;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -16,9 +16,6 @@ import java.util.UUID;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
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.Bukkit;
|
||||||
import org.bukkit.Chunk;
|
import org.bukkit.Chunk;
|
||||||
import org.bukkit.ChunkSnapshot;
|
import org.bukkit.ChunkSnapshot;
|
||||||
@ -27,7 +24,11 @@ import org.bukkit.Material;
|
|||||||
import org.bukkit.Tag;
|
import org.bukkit.Tag;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.World.Environment;
|
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.BlockData;
|
||||||
import org.bukkit.block.data.type.Slab;
|
import org.bukkit.block.data.type.Slab;
|
||||||
import org.bukkit.inventory.ItemStack;
|
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;
|
||||||
import com.google.common.collect.Multiset.Entry;
|
import com.google.common.collect.Multiset.Entry;
|
||||||
import com.google.common.collect.Multisets;
|
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 dev.rosewood.rosestacker.api.RoseStackerAPI;
|
||||||
import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI;
|
import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI;
|
||||||
@ -54,121 +58,21 @@ import world.bentobox.level.calculators.Results.Result;
|
|||||||
public class IslandLevelCalculator {
|
public class IslandLevelCalculator {
|
||||||
private static final String LINE_BREAK = "==================================";
|
private static final String LINE_BREAK = "==================================";
|
||||||
public static final long MAX_AMOUNT = 10000000;
|
public static final long MAX_AMOUNT = 10000000;
|
||||||
private static final List<Material> CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART, Material.TRAPPED_CHEST,
|
private static final List<Material> CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART,
|
||||||
Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX, Material.BROWN_SHULKER_BOX,
|
Material.TRAPPED_CHEST, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
|
||||||
Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX,
|
Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX,
|
||||||
Material.LIGHT_GRAY_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_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.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.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL,
|
||||||
Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
|
Material.DISPENSER, Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
|
||||||
private static final int CHUNKS_TO_SCAN = 100;
|
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 Level addon;
|
||||||
private final Queue<Pair<Integer, Integer>> chunksToCheck;
|
private final Queue<Pair<Integer, Integer>> chunksToCheck;
|
||||||
private final Island island;
|
private final Island island;
|
||||||
private final Map<Material, Integer> limitCount;
|
private final Map<Material, Integer> limitCount;
|
||||||
private final CompletableFuture<Results> r;
|
private final CompletableFuture<Results> r;
|
||||||
|
|
||||||
|
|
||||||
private final Results results;
|
private final Results results;
|
||||||
private long duration;
|
private long duration;
|
||||||
private final boolean zeroIsland;
|
private final boolean zeroIsland;
|
||||||
@ -178,12 +82,13 @@ public class IslandLevelCalculator {
|
|||||||
private final Set<Chunk> chestBlocks = new HashSet<>();
|
private final Set<Chunk> chestBlocks = new HashSet<>();
|
||||||
private BukkitTask finishTask;
|
private BukkitTask finishTask;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to get the level for an island
|
* Constructor to get the level for an island
|
||||||
|
*
|
||||||
* @param addon - Level addon
|
* @param addon - Level addon
|
||||||
* @param island - the island to scan
|
* @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
|
* @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) {
|
||||||
@ -219,25 +124,29 @@ public class IslandLevelCalculator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the level based on the raw points
|
* Calculate the level based on the raw points
|
||||||
|
*
|
||||||
* @param blockAndDeathPoints - raw points counted on island
|
* @param blockAndDeathPoints - raw points counted on island
|
||||||
* @return level of island
|
* @return level of island
|
||||||
*/
|
*/
|
||||||
private long calculateLevel(long blockAndDeathPoints) {
|
private long calculateLevel(long blockAndDeathPoints) {
|
||||||
String calcString = addon.getSettings().getLevelCalc();
|
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;
|
long evalWithValues;
|
||||||
try {
|
try {
|
||||||
evalWithValues = (long)eval(withValues);
|
evalWithValues = (long) EquationEvaluator.eval(withValues);
|
||||||
return evalWithValues - (addon.getSettings().isZeroNewIslandLevels() ? results.initialLevel.get() : 0);
|
return evalWithValues - (addon.getSettings().isZeroNewIslandLevels() ? results.initialLevel.get() : 0);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (ParseException e) {
|
||||||
addon.getPlugin().logStacktrace(e);
|
addon.getPlugin().logStacktrace(e);
|
||||||
return 0L;
|
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 mat - material of the block
|
||||||
* @param belowSeaLevel - true if below sea level
|
* @param belowSeaLevel - true if below sea level
|
||||||
*/
|
*/
|
||||||
@ -254,20 +163,22 @@ public class IslandLevelCalculator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a set of all the chunks in island
|
* Get a set of all the chunks in island
|
||||||
|
*
|
||||||
* @param island - island
|
* @param island - island
|
||||||
* @return - set of pairs of x,z coordinates to check
|
* @return - set of pairs of x,z coordinates to check
|
||||||
*/
|
*/
|
||||||
private Queue<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
private Queue<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
||||||
Queue<Pair<Integer, Integer>> chunkQueue = new ConcurrentLinkedQueue<>();
|
Queue<Pair<Integer, Integer>> chunkQueue = new ConcurrentLinkedQueue<>();
|
||||||
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
|
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2
|
||||||
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
|
+ 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));
|
chunkQueue.add(new Pair<>(x >> 4, z >> 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chunkQueue;
|
return chunkQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the island
|
* @return the island
|
||||||
*/
|
*/
|
||||||
@ -277,6 +188,7 @@ public class IslandLevelCalculator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the completable result for this calculation
|
* Get the completable result for this calculation
|
||||||
|
*
|
||||||
* @return the r
|
* @return the r
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<Results> getR() {
|
public CompletableFuture<Results> getR() {
|
||||||
@ -285,12 +197,14 @@ public class IslandLevelCalculator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the full analysis report
|
* Get the full analysis report
|
||||||
|
*
|
||||||
* @return a list of lines
|
* @return a list of lines
|
||||||
*/
|
*/
|
||||||
private List<String> getReport() {
|
private List<String> getReport() {
|
||||||
List<String> reportLines = new ArrayList<>();
|
List<String> reportLines = new ArrayList<>();
|
||||||
// provide counts
|
// 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("Island owner UUID = " + island.getOwner());
|
||||||
reportLines.add("Total block value count = " + String.format("%,d", results.rawBlockCount.get()));
|
reportLines.add("Total block value count = " + String.format("%,d", results.rawBlockCount.get()));
|
||||||
reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
|
reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
|
||||||
@ -304,7 +218,8 @@ public class IslandLevelCalculator {
|
|||||||
reportLines.add(LINE_BREAK);
|
reportLines.add(LINE_BREAK);
|
||||||
int total = 0;
|
int total = 0;
|
||||||
if (!results.uwCount.isEmpty()) {
|
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.add("Total number of underwater blocks = " + String.format("%,d", results.uwCount.size()));
|
||||||
reportLines.addAll(sortedReport(total, results.uwCount));
|
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.add("Total number of blocks = " + String.format("%,d", results.mdCount.size()));
|
||||||
reportLines.addAll(sortedReport(total, results.mdCount));
|
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();
|
Iterable<Multiset.Entry<Material>> entriesSortedByCount = results.ofCount.entrySet();
|
||||||
Iterator<Entry<Material>> it = entriesSortedByCount.iterator();
|
Iterator<Entry<Material>> it = entriesSortedByCount.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@ -324,7 +240,8 @@ public class IslandLevelCalculator {
|
|||||||
limit = addon.getBlockConfig().getBlockLimits().get(generic);
|
limit = addon.getBlockConfig().getBlockLimits().get(generic);
|
||||||
explain = " - All types)";
|
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(LINE_BREAK);
|
||||||
reportLines.add("Blocks on island that are not in config.yml");
|
reportLines.add("Blocks on island that are not in config.yml");
|
||||||
@ -346,9 +263,10 @@ public class IslandLevelCalculator {
|
|||||||
public Results getResults() {
|
public Results getResults() {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get value of a material
|
* Get value of a material World blocks trump regular block values
|
||||||
* World blocks trump regular block values
|
*
|
||||||
* @param md - Material to check
|
* @param md - Material to check
|
||||||
* @return value of a material
|
* @return value of a material
|
||||||
*/
|
*/
|
||||||
@ -364,9 +282,11 @@ public class IslandLevelCalculator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a chunk async
|
* Get a chunk async
|
||||||
|
*
|
||||||
* @param env - the environment
|
* @param env - the environment
|
||||||
* @param pairList - chunk coordinate
|
* @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) {
|
private CompletableFuture<List<Chunk>> getWorldChunk(Environment env, Queue<Pair<Integer, Integer>> pairList) {
|
||||||
if (worlds.containsKey(env)) {
|
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
|
* @param md Material
|
||||||
* @return value of the block if can be counted
|
* @return value of the block if can be counted
|
||||||
*/
|
*/
|
||||||
@ -428,9 +350,9 @@ public class IslandLevelCalculator {
|
|||||||
return getValue(md);
|
return getValue(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan all containers in a chunk and count their blocks
|
* Scan all containers in a chunk and count their blocks
|
||||||
|
*
|
||||||
* @param chunk - the chunk to scan
|
* @param chunk - the chunk to scan
|
||||||
*/
|
*/
|
||||||
private void scanChests(Chunk chunk) {
|
private void scanChests(Chunk chunk) {
|
||||||
@ -455,7 +377,8 @@ public class IslandLevelCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void countItemStack(ItemStack i) {
|
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++) {
|
for (int c = 0; c < i.getAmount(); c++) {
|
||||||
if (addon.getSettings().isIncludeShulkersInChest()
|
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
|
* Scan the chunk chests and count the blocks. Note that the chunks are a list
|
||||||
* in a particular world, so the memory usage is high, but I think most servers can handle it.
|
* 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
|
* @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) {
|
private CompletableFuture<Boolean> scanChunk(List<Chunk> chunks) {
|
||||||
// If the chunk hasn't been generated, return
|
// If the chunk hasn't been generated, return
|
||||||
@ -482,11 +408,13 @@ public class IslandLevelCalculator {
|
|||||||
// Count blocks in chunk
|
// Count blocks in chunk
|
||||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
/*
|
/*
|
||||||
* At this point, we need to grab a snapshot of each chunk and then scan it async.
|
* At this point, we need to grab a snapshot of each chunk and then scan it
|
||||||
* At the end, we make the CompletableFuture true to show it is done.
|
* async. At the end, we make the CompletableFuture true to show it is done. I'm
|
||||||
* I'm not sure how much lag this will cause, but as all the chunks are loaded, maybe not that much.
|
* 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(), () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
|
||||||
preLoad.forEach(this::scanAsync);
|
preLoad.forEach(this::scanAsync);
|
||||||
// Once they are all done, return to the main thread.
|
// Once they are all done, return to the main thread.
|
||||||
@ -495,21 +423,27 @@ public class IslandLevelCalculator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {}
|
record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the blocks on the island
|
* Count the blocks on the island
|
||||||
|
*
|
||||||
* @param cp chunk to scan
|
* @param cp chunk to scan
|
||||||
*/
|
*/
|
||||||
private void scanAsync(ChunkPair cp) {
|
private void scanAsync(ChunkPair cp) {
|
||||||
for (int x = 0; x < 16; x++) {
|
for (int x = 0; x < 16; x++) {
|
||||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
// Check if the block coordinate is inside the protection zone and if not, don't
|
||||||
if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
// count it
|
||||||
|
if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16
|
||||||
|
+ x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int z = 0; z < 16; z++) {
|
for (int z = 0; z < 16; z++) {
|
||||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
// Check if the block coordinate is inside the protection zone and if not, don't
|
||||||
if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
// count it
|
||||||
|
if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16
|
||||||
|
+ z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Only count to the highest block in the world for some optimization
|
// Only count to the highest block in the world for some optimization
|
||||||
@ -523,16 +457,20 @@ public class IslandLevelCalculator {
|
|||||||
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real chunk
|
// Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
|
||||||
if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON) || blockData.getMaterial().equals(Material.SPAWNER))) {
|
// chunk
|
||||||
stackedBlocks.add(new Location(cp.world, (double)x + cp.chunkSnapshot.getX() * 16, y, (double)z + cp.chunkSnapshot.getZ() * 16));
|
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);
|
Block block = cp.chunk.getBlock(x, y, z);
|
||||||
|
|
||||||
if (addon.isUltimateStackerEnabled()) {
|
if (addon.isUltimateStackerEnabled()) {
|
||||||
if (!blockData.getMaterial().equals(Material.AIR)) {
|
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) {
|
if (stack != null) {
|
||||||
int value = limitCount(blockData.getMaterial());
|
int value = limitCount(blockData.getMaterial());
|
||||||
if (belowSeaLevel) {
|
if (belowSeaLevel) {
|
||||||
@ -559,7 +497,9 @@ public class IslandLevelCalculator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan the next chunk on the island
|
* 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() {
|
public CompletableFuture<Boolean> scanNextChunk() {
|
||||||
if (chunksToCheck.isEmpty()) {
|
if (chunksToCheck.isEmpty()) {
|
||||||
@ -579,31 +519,28 @@ public class IslandLevelCalculator {
|
|||||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
// Get chunks and scan
|
// Get chunks and scan
|
||||||
// Get chunks and scan
|
// Get chunks and scan
|
||||||
getWorldChunk(Environment.THE_END, endPairList).thenAccept(endChunks ->
|
getWorldChunk(Environment.THE_END, endPairList).thenAccept(
|
||||||
scanChunk(endChunks).thenAccept(b ->
|
endChunks -> scanChunk(endChunks).thenAccept(b -> getWorldChunk(Environment.NETHER, netherPairList)
|
||||||
getWorldChunk(Environment.NETHER, netherPairList).thenAccept(netherChunks ->
|
.thenAccept(netherChunks -> scanChunk(netherChunks)
|
||||||
scanChunk(netherChunks).thenAccept(b2 ->
|
.thenAccept(b2 -> getWorldChunk(Environment.NORMAL, pairList)
|
||||||
getWorldChunk(Environment.NORMAL, pairList).thenAccept(normalChunks ->
|
.thenAccept(normalChunks -> scanChunk(normalChunks).thenAccept(b3 ->
|
||||||
scanChunk(normalChunks).thenAccept(b3 ->
|
|
||||||
// Complete the result now that all chunks have been scanned
|
// Complete the result now that all chunks have been scanned
|
||||||
result.complete(!chunksToCheck.isEmpty()))))
|
result.complete(!chunksToCheck.isEmpty())))))));
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<String> sortedReport(int total, Multiset<Material> materialCount) {
|
private Collection<String> sortedReport(int total, Multiset<Material> materialCount) {
|
||||||
Collection<String> result = new ArrayList<>();
|
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) {
|
for (Entry<Material> en : entriesSortedByCount) {
|
||||||
Material type = en.getElement();
|
Material type = en.getElement();
|
||||||
|
|
||||||
int value = getValue(type);
|
int value = getValue(type);
|
||||||
|
|
||||||
result.add(type.toString() + ":"
|
result.add(type.toString() + ":" + String.format("%,d", en.getCount()) + " blocks x " + value + " = "
|
||||||
+ String.format("%,d", en.getCount()) + " blocks x " + value + " = " + (value * en.getCount()));
|
+ (value * en.getCount()));
|
||||||
total += (value * en.getCount());
|
total += (value * en.getCount());
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -612,34 +549,29 @@ public class IslandLevelCalculator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalizes the calculations and makes the report
|
* Finalizes the calculations and makes the report
|
||||||
*/
|
*/
|
||||||
public void tidyUp() {
|
public void tidyUp() {
|
||||||
// Finalize calculations
|
// 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
|
// Set the death penalty
|
||||||
if (this.addon.getSettings().isSumTeamDeaths())
|
if (this.addon.getSettings().isSumTeamDeaths()) {
|
||||||
{
|
for (UUID uuid : this.island.getMemberSet()) {
|
||||||
for (UUID uuid : this.island.getMemberSet())
|
|
||||||
{
|
|
||||||
this.results.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(island.getWorld(), uuid));
|
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.
|
// At this point, it may be that the island has become unowned.
|
||||||
this.results.deathHandicap.set(this.island.getOwner() == null ? 0 :
|
this.results.deathHandicap.set(this.island.getOwner() == null ? 0
|
||||||
this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
|
: this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
|
||||||
}
|
}
|
||||||
|
|
||||||
long blockAndDeathPoints = this.results.rawBlockCount.get();
|
long blockAndDeathPoints = this.results.rawBlockCount.get();
|
||||||
this.results.totalPoints.set(blockAndDeathPoints);
|
this.results.totalPoints.set(blockAndDeathPoints);
|
||||||
|
|
||||||
if (this.addon.getSettings().getDeathPenalty() > 0)
|
if (this.addon.getSettings().getDeathPenalty() > 0) {
|
||||||
{
|
|
||||||
// Proper death penalty calculation.
|
// Proper death penalty calculation.
|
||||||
blockAndDeathPoints -= this.results.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
|
blockAndDeathPoints -= this.results.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
|
||||||
}
|
}
|
||||||
@ -674,11 +606,13 @@ public class IslandLevelCalculator {
|
|||||||
addon.getPlugin().logError("scanChunk not on Primary Thread!");
|
addon.getPlugin().logError("scanChunk not on Primary Thread!");
|
||||||
}
|
}
|
||||||
// Timeout check
|
// 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
|
// Done
|
||||||
pipeliner.getInProcessQueue().remove(this);
|
pipeliner.getInProcessQueue().remove(this);
|
||||||
getR().complete(new Results(Result.TIMEOUT));
|
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()) {
|
if (!isNotZeroIsland()) {
|
||||||
addon.logError("Island level was being zeroed.");
|
addon.logError("Island level was being zeroed.");
|
||||||
}
|
}
|
||||||
@ -697,7 +631,8 @@ public class IslandLevelCalculator {
|
|||||||
long checkTime = System.currentTimeMillis();
|
long checkTime = System.currentTimeMillis();
|
||||||
finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
|
finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
|
||||||
// Check every half second if all the chests and stacks have been cleared
|
// 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.tidyUp();
|
||||||
this.getR().complete(getResults());
|
this.getR().complete(getResults());
|
||||||
finishTask.cancel();
|
finishTask.cancel();
|
||||||
|
Loading…
Reference in New Issue
Block a user