diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index b10469f..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Description** -A clear and concise description of what the bug is. - -**Steps to reproduce the behavior:** -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Server Information:** - -[Please complete the following information:] - - Database being used (YAML, JSON, MySQL, MongoDB): [] - - OS: [e.g. iOS] - - Java Version: [e.g. Java 8] - - BentoBox version: [e.g. 1.7.2.21] - - Addons installed? [Do '/bentobox version' and copy/paste from the console] - - Other plugins? [Do '/plugins' and copy/paste from the console] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 066b2d9..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 3078cd3..0c558c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,87 @@ -/target/ -/.project -/.classpath -/.DS_Store + # Git +*.orig +!.gitignore +/.settings/ + + # Windows +Thumbs.db +ehthumbs.db +ehthumbs_vista.db +*.stackdump +[Dd]esktop.ini +$RECYCLE.BIN/ +*.lnk + + # Linux +*~ +.fuse_hidden* +.directory +.Trash-* +.nfs* + + # MacOS +.DS_Store +.AppleDouble +.LSOverride +._* + + # Java +*.class +*.log +*.ctxt +.mtj.tmp/ +*.jar +*.war +*.nar +*.ear +hs_err_pid* + + # Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties + + # Intellij +*.iml +*.java___jb_tmp___ +.idea/* +*.ipr +*.iws +/out/ +.idea_modules/ + + # Eclipse +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.project +.externalToolBuilders/ +*.launch +.cproject +.classpath +.buildpath +.target + + # NetBeans +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml +.nb-gradle/ diff --git a/README.md b/README.md index 2ff2a20..c2b75a7 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ for game modes listed in the config.yml. 1. Read the release notes carefully, but you may have to delete the old config.yml to use a new one. -## Permissons -Permissions are given automatically to players as listed below for BSkyBlock, AcidIsland and CaveBlock. If your permissions plugin strips permissions then you may have to allocate these manually. Note that if a player doesn't have the intopten permission, they will not be listed in the top ten. +## Permissions +Permissions are given automatically to players as listed below for BSkyBlock, AcidIsland and CaveBlock. If your permissions plugin strips permissions then you may have to allocate these manually. Note that if a player doesn't have the `intopten` permission, they will not be listed in the top ten. ``` permissions: @@ -61,7 +61,7 @@ This section defines a number of overall settings for the add-on. * Underwater block multiplier - default value = 1. If this value is > 1 then blocks below sea level will have a greater value. * Level cost - Value of one island level. Default 100. Minimum value is 1. -* Level wait - Cooldown between level requests in seconds +* Level wait - Cool down between level requests in seconds * Death penalty - How many block values a player will lose per death. Default value of 100 means that for every death, the player will lose 1 level (if levelcost is 100) * Sum Team Deaths - if true, all the team member deaths are summed. If false, only the leader's deaths counts. * Max deaths - If player dies more than this, it doesn't count anymore. diff --git a/pom.xml b/pom.xml index 3054232..036adc1 100644 --- a/pom.xml +++ b/pom.xml @@ -59,13 +59,13 @@ 2.0.2 1.14.4-R0.1-SNAPSHOT - 1.8.0 + 1.9.0-SNAPSHOT ${build.version}-SNAPSHOT -LOCAL - 1.8.0 + 1.9.0 diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java index 181faf5..4710e11 100644 --- a/src/main/java/world/bentobox/level/Level.java +++ b/src/main/java/world/bentobox/level/Level.java @@ -1,5 +1,6 @@ package world.bentobox.level; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -26,7 +27,6 @@ import world.bentobox.level.placeholders.TopTenPlaceholder; import world.bentobox.level.requests.LevelRequestHandler; import world.bentobox.level.requests.TopTenRequestHandler; - /** * Addon to BSkyBlock/AcidIsland that enables island level scoring and top ten functionality * @author tastybento @@ -143,12 +143,49 @@ public class Level extends Addon { }); // Register placeholders if (getPlugin().getPlaceholdersManager() != null) { + // DEPRECATED PLACEHOLDERS - remove in an upcoming version + getPlugin().getPlaceholdersManager().registerPlaceholder(this, gm.getDescription().getName().toLowerCase() + "-island-level", new LevelPlaceholder(this, gm)); // Top Ten for (int i = 1; i < 11; i++) { getPlugin().getPlaceholdersManager().registerPlaceholder(this, gm.getDescription().getName().toLowerCase() + "-island-level-top-value-" + i, new TopTenPlaceholder(this, gm, i)); getPlugin().getPlaceholdersManager().registerPlaceholder(this, gm.getDescription().getName().toLowerCase() + "-island-level-top-name-" + i, new TopTenNamePlaceholder(this, gm, i)); } + + // --------------------- + + // Island Level + getPlugin().getPlaceholdersManager().registerPlaceholder(this, + gm.getDescription().getName().toLowerCase() + "_island_level", + user -> getLevelPresenter().getLevelString(getIslandLevel(gm.getOverWorld(), user.getUniqueId()))); + + // Visited Island Level + getPlugin().getPlaceholdersManager().registerPlaceholder(this, + gm.getDescription().getName().toLowerCase() + "_visited_island_level", + user -> getPlugin().getIslands().getIslandAt(user.getLocation()) + .map(island -> getIslandLevel(gm.getOverWorld(), island.getOwner())) + .map(level -> getLevelPresenter().getLevelString(level)) + .orElse("0")); + + // Top Ten + for (int i = 1; i <= 10; i++) { + final int rank = i; + // Value + getPlugin().getPlaceholdersManager().registerPlaceholder(this, + gm.getDescription().getName().toLowerCase() + "_top_value_" + rank, + user -> { + Collection values = getTopTen().getTopTenList(gm.getOverWorld()).getTopTen().values(); + return values.size() < rank ? "" : values.stream().skip(rank).findFirst().map(String::valueOf).orElse(""); + }); + + // Name + getPlugin().getPlaceholdersManager().registerPlaceholder(this, + gm.getDescription().getName().toLowerCase() + "_top_name_" + rank, + user -> { + Collection values = getTopTen().getTopTenList(gm.getOverWorld()).getTopTen().keySet(); + return values.size() < rank ? "" : getPlayers().getName(values.stream().skip(rank).findFirst().orElse(null)); + }); + } } }); diff --git a/src/main/java/world/bentobox/level/TopTen.java b/src/main/java/world/bentobox/level/TopTen.java index 63044b9..6cc1375 100644 --- a/src/main/java/world/bentobox/level/TopTen.java +++ b/src/main/java/world/bentobox/level/TopTen.java @@ -13,6 +13,7 @@ import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.Listener; +import org.eclipse.jdt.annotation.NonNull; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; @@ -28,11 +29,11 @@ import world.bentobox.level.objects.TopTenData; * */ public class TopTen implements Listener { - private Level addon; + private final Level addon; // Top ten list of players private Map topTenList; private final int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25}; - private Database handler; + private final Database handler; public TopTen(Level addon) { this.addon = addon; @@ -107,6 +108,8 @@ public class TopTen implements Listener { if (!entry.hasPermission(permPrefix + "intopten")) { it.remove(); show = false; + // Remove from Top Ten completely + topTenList.get(world).remove(topTenUUID); } } if (show) { @@ -150,6 +153,7 @@ public class TopTen implements Listener { * @param world - world * @return top ten data object */ + @NonNull public TopTenData getTopTenList(World world) { topTenList.putIfAbsent(world, new TopTenData()); return topTenList.get(world); diff --git a/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java b/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java index 3971fab..e64b31d 100644 --- a/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java +++ b/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java @@ -1,22 +1,17 @@ package world.bentobox.level.calculators; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.ChunkSnapshot; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Slab; -import org.bukkit.scheduler.BukkitTask; import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; @@ -31,11 +26,9 @@ import world.bentobox.level.Level; public class CalcIslandLevel { - private final int maxChunks; - private final long speed; private static final String LINE_BREAK = "=================================="; - private boolean checking; - private final BukkitTask task; + + public static final long MAX_AMOUNT = 10000; private final Level addon; @@ -46,9 +39,14 @@ public class CalcIslandLevel { // Copy the limits hash map private final HashMap limitCount; - private final World world; private final List worlds; + private final World world; + private AtomicInteger count; + + private int total; + private Queue q; + private int queueid; /** * Calculate the island's level @@ -61,75 +59,69 @@ public class CalcIslandLevel { this.addon = addon; this.island = island; this.world = island.getWorld(); - this.worlds = new ArrayList<>(); - this.worlds.add(world); - if (addon.getSettings().isNether()) { - World netherWorld = addon.getPlugin().getIWM().getNetherWorld(world); - if (netherWorld != null) this.worlds.add(netherWorld); - } - if (addon.getSettings().isEnd()) { - World endWorld = addon.getPlugin().getIWM().getEndWorld(world); - if (endWorld != null) this.worlds.add(endWorld); - } this.limitCount = new HashMap<>(addon.getSettings().getBlockLimits()); this.onExit = onExit; + this.worlds = new ArrayList<>(); + this.worlds.add(world); + q = new LinkedList<>(); // Results go here result = new Results(); // Set the initial island handicap - result.initialLevel = addon.getInitialIslandLevel(island); - - speed = addon.getSettings().getUpdateTickDelay(); - maxChunks = addon.getSettings().getChunksPerTick(); + result.setInitialLevel(addon.getInitialIslandLevel(island)); // Get chunks to scan chunksToScan = getChunksToScan(island); - - // Start checking - checking = true; - - // Start a recurring task until done or cancelled - task = addon.getServer().getScheduler().runTaskTimer(addon.getPlugin(), () -> { - Set chunkSnapshot = new HashSet<>(); - if (checking) { - Iterator> it = chunksToScan.iterator(); - if (!it.hasNext()) { - // Nothing left - tidyUp(); - return; - } - // Add chunk snapshots to the list - while (it.hasNext() && chunkSnapshot.size() < maxChunks) { - Pair pair = it.next(); - for (World worldToScan : worlds) { - if (!worldToScan.isChunkLoaded(pair.x, pair.z)) { - worldToScan.loadChunk(pair.x, pair.z); - chunkSnapshot.add(worldToScan.getChunkAt(pair.x, pair.z).getChunkSnapshot()); - worldToScan.unloadChunk(pair.x, pair.z); - } else { - chunkSnapshot.add(worldToScan.getChunkAt(pair.x, pair.z).getChunkSnapshot()); - } - } - it.remove(); - } - // Move to next step - checking = false; - checkChunksAsync(chunkSnapshot); + count = new AtomicInteger(); + // Total number of chunks to scan + total = chunksToScan.size(); + // Add nether world scanning + if (addon.getSettings().isNether()) { + World netherWorld = addon.getPlugin().getIWM().getNetherWorld(world); + if (netherWorld != null) { + this.worlds.add(netherWorld); + total += chunksToScan.size(); } - }, 0L, speed); + } + // Add End world scanning + if (addon.getSettings().isEnd()) { + World endWorld = addon.getPlugin().getIWM().getEndWorld(world); + if (endWorld != null) { + this.worlds.add(endWorld); + total += chunksToScan.size(); + } + } + queueid = Bukkit.getScheduler().scheduleSyncRepeatingTask(addon.getPlugin(), new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) { + if (q.size() == 0) { + return; + } + Chunk c = q.remove(); + getChunk(c); + } + } + }, 1L, 1L); + chunksToScan.forEach(c -> worlds.forEach(w -> Util.getChunkAtAsync(w, c.x, c.z).thenAccept(this::addChunkQueue))); + } - private void checkChunksAsync(final Set chunkSnapshot) { - // Run async task to scan chunks - addon.getServer().getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> { - for (ChunkSnapshot chunk: chunkSnapshot) { - scanChunk(chunk); - } - // Nothing happened, change state - checking = true; - }); + private void addChunkQueue(Chunk ch) { + q.add(ch); + } + private void getChunk(Chunk ch) { + ChunkSnapshot snapShot = ch.getChunkSnapshot(); + + Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> { + this.scanChunk(snapShot); + count.getAndIncrement(); + if (count.get() == total) { + Bukkit.getScheduler().cancelTask(queueid); + this.tidyUp(); + } + }); } private void scanChunk(ChunkSnapshot chunk) { @@ -168,10 +160,10 @@ public class CalcIslandLevel { private void checkBlock(BlockData bd, boolean belowSeaLevel) { int count = limitCount(bd.getMaterial()); if (belowSeaLevel) { - result.underWaterBlockCount += count; + result.underWaterBlockCount.addAndGet(count); result.uwCount.add(bd.getMaterial()); } else { - result.rawBlockCount += count; + result.rawBlockCount.addAndGet(count); result.mdCount.add(bd.getMaterial()); } } @@ -231,40 +223,40 @@ public class CalcIslandLevel { } private void tidyUp() { - // Cancel - task.cancel(); // Finalize calculations - result.rawBlockCount += (long)(result.underWaterBlockCount * addon.getSettings().getUnderWaterMultiplier()); + result.rawBlockCount.addAndGet((long)(result.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier())); // Set the death penalty if (this.addon.getSettings().isSumTeamDeaths()) { for (UUID uuid : this.island.getMemberSet()) { - this.result.deathHandicap += this.addon.getPlayers().getDeaths(this.world, uuid); + this.result.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(this.world, uuid)); } } else { // At this point, it may be that the island has become unowned. - this.result.deathHandicap = this.island.getOwner() == null ? 0 : - this.addon.getPlayers().getDeaths(this.world, this.island.getOwner()); + this.result.deathHandicap.set(this.island.getOwner() == null ? 0 : + this.addon.getPlayers().getDeaths(this.world, this.island.getOwner())); } - long blockAndDeathPoints = this.result.rawBlockCount; + long blockAndDeathPoints = this.result.rawBlockCount.get(); if (this.addon.getSettings().getDeathPenalty() > 0) { // Proper death penalty calculation. - blockAndDeathPoints -= this.result.deathHandicap * this.addon.getSettings().getDeathPenalty(); + blockAndDeathPoints -= this.result.deathHandicap.get() * this.addon.getSettings().getDeathPenalty(); } - - this.result.level = blockAndDeathPoints / this.addon.getSettings().getLevelCost() - this.island.getLevelHandicap() - result.initialLevel; - + this.result.level.set(calculateLevel(blockAndDeathPoints)); // Calculate how many points are required to get to the next level - this.result.pointsToNextLevel = this.addon.getSettings().getLevelCost() - - (blockAndDeathPoints % this.addon.getSettings().getLevelCost()); + long nextLevel = this.result.level.get(); + long blocks = blockAndDeathPoints; + while (nextLevel < this.result.level.get() + 1 && blocks - blockAndDeathPoints < MAX_AMOUNT) { + nextLevel = calculateLevel(++blocks); + } + this.result.pointsToNextLevel.set(blocks - blockAndDeathPoints); // Report result.report = getReport(); @@ -275,16 +267,23 @@ public class CalcIslandLevel { } + 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())); + return (long)eval(withValues) - this.island.getLevelHandicap() - result.initialLevel.get(); + } + private List getReport() { List 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("Island owner UUID = " + island.getOwner()); - reportLines.add("Total block value count = " + String.format("%,d",result.rawBlockCount)); + reportLines.add("Total block value count = " + String.format("%,d",result.rawBlockCount.get())); + reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc()); reportLines.add("Level cost = " + addon.getSettings().getLevelCost()); - reportLines.add("Deaths handicap = " + result.deathHandicap); - reportLines.add("Initial island level = " + (0L - result.initialLevel)); - reportLines.add("Level calculated = " + result.level); + reportLines.add("Deaths handicap = " + result.deathHandicap.get()); + reportLines.add("Initial island level = " + (0L - result.initialLevel.get())); + reportLines.add("Level calculated = " + result.level.get()); reportLines.add(LINE_BREAK); int total = 0; if (!result.uwCount.isEmpty()) { @@ -359,18 +358,19 @@ public class CalcIslandLevel { private final Multiset uwCount = HashMultiset.create(); private final Multiset ncCount = HashMultiset.create(); private final Multiset ofCount = HashMultiset.create(); - private long rawBlockCount = 0; - private long underWaterBlockCount = 0; - private long level = 0; - private int deathHandicap = 0; - private long pointsToNextLevel = 0; - private long initialLevel = 0; + // AtomicLong and AtomicInteger must be used because they are changed by multiple concurrent threads + private AtomicLong rawBlockCount = new AtomicLong(0); + private AtomicLong underWaterBlockCount = new AtomicLong(0); + private AtomicLong level = new AtomicLong(0); + private AtomicInteger deathHandicap = new AtomicInteger(0); + private AtomicLong pointsToNextLevel = new AtomicLong(0); + private AtomicLong initialLevel = new AtomicLong(0); /** * @return the deathHandicap */ public int getDeathHandicap() { - return deathHandicap; + return deathHandicap.get(); } /** @@ -384,27 +384,27 @@ public class CalcIslandLevel { * @param level - level */ public void setLevel(int level) { - this.level = level; + this.level.set(level); } /** * @return the level */ public long getLevel() { - return level; + return level.get(); } /** * @return the pointsToNextLevel */ public long getPointsToNextLevel() { - return pointsToNextLevel; + return pointsToNextLevel.get(); } public long getInitialLevel() { - return initialLevel; + return initialLevel.get(); } public void setInitialLevel(long initialLevel) { - this.initialLevel = initialLevel; + this.initialLevel.set(initialLevel); } /* (non-Javadoc) @@ -419,4 +419,95 @@ public class CalcIslandLevel { } } + + private static double eval(final String str) { + return new Object() { + int pos = -1, ch; + + void nextChar() { + ch = (++pos < str.length()) ? str.charAt(pos) : -1; + } + + boolean eat(int charToEat) { + while (ch == ' ') nextChar(); + if (ch == charToEat) { + nextChar(); + return true; + } + return false; + } + + double parse() { + nextChar(); + double x = parseExpression(); + if (pos < str.length()) throw new RuntimeException("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() { + double x = parseTerm(); + for (;;) { + if (eat('+')) x += parseTerm(); // addition + else if (eat('-')) x -= parseTerm(); // subtraction + else return x; + } + } + + double parseTerm() { + double x = parseFactor(); + for (;;) { + if (eat('*')) x *= parseFactor(); // multiplication + else if (eat('/')) x /= parseFactor(); // division + else return x; + } + } + + double parseFactor() { + 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; + default: + throw new RuntimeException("Unknown function: " + func); + } + } else { + throw new RuntimeException("Unexpected: " + (char)ch); + } + + if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation + + return x; + } + }.parse(); + } } diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java new file mode 100644 index 0000000..5293f3b --- /dev/null +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -0,0 +1,10 @@ +package world.bentobox.level.calculators; + +public interface IslandLevelCalculator { + + /** + * @return the results of the island calculation + */ + Results getResult(); + +} diff --git a/src/main/java/world/bentobox/level/calculators/PlayerLevel.java b/src/main/java/world/bentobox/level/calculators/PlayerLevel.java index 68a71e4..90e3f3f 100644 --- a/src/main/java/world/bentobox/level/calculators/PlayerLevel.java +++ b/src/main/java/world/bentobox/level/calculators/PlayerLevel.java @@ -90,7 +90,7 @@ public class PlayerLevel { asker.sendMessage("island.level.deaths", "[number]", String.valueOf(results.getDeathHandicap())); } // Send player how many points are required to reach next island level - if (results.getPointsToNextLevel() >= 0) { + if (results.getPointsToNextLevel() >= 0 && results.getPointsToNextLevel() < CalcIslandLevel.MAX_AMOUNT) { asker.sendMessage("island.level.required-points-to-next-level", "[points]", String.valueOf(results.getPointsToNextLevel())); } // Tell other team members diff --git a/src/main/java/world/bentobox/level/calculators/Results.java b/src/main/java/world/bentobox/level/calculators/Results.java new file mode 100644 index 0000000..28b70b9 --- /dev/null +++ b/src/main/java/world/bentobox/level/calculators/Results.java @@ -0,0 +1,173 @@ +package world.bentobox.level.calculators; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; + +/** + * Results class + * + */ +public class Results { + private int deathHandicap = 0; + private long initialLevel = 0; + private long level = 0; + private final Multiset mdCount = HashMultiset.create(); + private final Multiset ncCount = HashMultiset.create(); + private final Multiset ofCount = HashMultiset.create(); + private long pointsToNextLevel = 0; + private long rawBlockCount = 0; + private List report = new ArrayList<>(); + private long underWaterBlockCount = 0; + private final Multiset uwCount = HashMultiset.create(); + + /** + * @return the deathHandicap + */ + public int getDeathHandicap() { + return deathHandicap; + } + + public long getInitialLevel() { + return initialLevel; + } + /** + * @return the level + */ + public long getLevel() { + return level; + } + /** + * @return the mdCount + */ + public Multiset getMdCount() { + return mdCount; + } + /** + * @return the ncCount + */ + public Multiset getNcCount() { + return ncCount; + } + + /** + * @return the ofCount + */ + public Multiset getOfCount() { + return ofCount; + } + + /** + * @return the pointsToNextLevel + */ + public long getPointsToNextLevel() { + return pointsToNextLevel; + } + + /** + * @return the rawBlockCount + */ + public long getRawBlockCount() { + return rawBlockCount; + } + + /** + * @return the report + */ + public List getReport() { + return report; + } + + /** + * @return the underWaterBlockCount + */ + public long getUnderWaterBlockCount() { + return underWaterBlockCount; + } + + /** + * @return the uwCount + */ + public Multiset getUwCount() { + return uwCount; + } + + /** + * @param deathHandicap the deathHandicap to set + */ + public void setDeathHandicap(int deathHandicap) { + this.deathHandicap = deathHandicap; + } + + public void setInitialLevel(long initialLevel) { + this.initialLevel = initialLevel; + } + + /** + * Set level + * @param level - level + */ + public void setLevel(int level) { + this.level = level; + } + + /** + * @param level the level to set + */ + public void setLevel(long level) { + this.level = level; + } + + /** + * @param pointsToNextLevel the pointsToNextLevel to set + */ + public void setPointsToNextLevel(long pointsToNextLevel) { + this.pointsToNextLevel = pointsToNextLevel; + } + + /** + * @param rawBlockCount the rawBlockCount to set + */ + public void setRawBlockCount(long rawBlockCount) { + this.rawBlockCount = rawBlockCount; + } + + /** + * @param report the report to set + */ + public void setReport(List report) { + this.report = report; + } + + /** + * @param underWaterBlockCount the underWaterBlockCount to set + */ + public void setUnderWaterBlockCount(long underWaterBlockCount) { + this.underWaterBlockCount = underWaterBlockCount; + } + + /** + * Add to death handicap + * @param deaths - number to add + */ + public void addToDeathHandicap(int deaths) { + this.deathHandicap += deaths; + + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Results [report=" + report + ", mdCount=" + mdCount + ", uwCount=" + getUwCount() + ", ncCount=" + + ncCount + ", ofCount=" + ofCount + ", rawBlockCount=" + rawBlockCount + ", underWaterBlockCount=" + + getUnderWaterBlockCount() + ", level=" + level + ", deathHandicap=" + deathHandicap + + ", pointsToNextLevel=" + pointsToNextLevel + ", initialLevel=" + initialLevel + "]"; + } + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/level/commands/admin/AdminTopCommand.java b/src/main/java/world/bentobox/level/commands/admin/AdminTopCommand.java index 30598e7..b04aa0e 100644 --- a/src/main/java/world/bentobox/level/commands/admin/AdminTopCommand.java +++ b/src/main/java/world/bentobox/level/commands/admin/AdminTopCommand.java @@ -16,6 +16,7 @@ public class AdminTopCommand extends CompositeCommand { public AdminTopCommand(Level levelPlugin, CompositeCommand parent) { super(parent, "top", "topten"); this.levelPlugin = levelPlugin; + new AdminTopRemoveCommand(levelPlugin, this); } @Override diff --git a/src/main/java/world/bentobox/level/commands/admin/AdminTopRemoveCommand.java b/src/main/java/world/bentobox/level/commands/admin/AdminTopRemoveCommand.java new file mode 100644 index 0000000..4778903 --- /dev/null +++ b/src/main/java/world/bentobox/level/commands/admin/AdminTopRemoveCommand.java @@ -0,0 +1,55 @@ +package world.bentobox.level.commands.admin; + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.level.Level; + +/** + * Removes a player from the top ten + * @author tastybento + * + */ +public class AdminTopRemoveCommand extends CompositeCommand { + + private final Level addon; + private User target; + + public AdminTopRemoveCommand(Level addon, CompositeCommand parent) { + super(parent, "remove", "delete"); + this.addon = addon; + } + + @Override + public void setup() { + this.setPermission("admin.top.remove"); + this.setOnlyPlayer(false); + this.setParametersHelp("admin.top.remove.parameters"); + this.setDescription("admin.top.remove.description"); + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean canExecute(User user, String label, List args) { + if (args.size() != 1) { + this.showHelp(this, user); + return false; + } + target = getPlayers().getUser(args.get(0)); + if (target == null) { + user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); + return false; + } + return true; + } + @Override + public boolean execute(User user, String label, List args) { + addon.getTopTen().getTopTenList(getWorld()).remove(target.getUniqueId()); + user.sendMessage("general.success"); + return true; + } +} diff --git a/src/main/java/world/bentobox/level/commands/island/IslandValueCommand.java b/src/main/java/world/bentobox/level/commands/island/IslandValueCommand.java index 2610a56..51b04bf 100644 --- a/src/main/java/world/bentobox/level/commands/island/IslandValueCommand.java +++ b/src/main/java/world/bentobox/level/commands/island/IslandValueCommand.java @@ -34,7 +34,7 @@ public class IslandValueCommand extends CompositeCommand { int value = plugin.getConfig().getInt("blocks." + material.toString()); user.sendMessage("island.value.success", "[value]", value + ""); if (plugin.getConfig().get("underwater") != null) { - Double underWater = plugin.getConfig().getDouble("underwater"); + double underWater = plugin.getConfig().getDouble("underwater"); if (underWater > 1.0) { user.sendMessage("island.value.success-underwater", "[value]", (underWater * value) + ""); } diff --git a/src/main/java/world/bentobox/level/config/Settings.java b/src/main/java/world/bentobox/level/config/Settings.java index a0e1e31..4bd5a4c 100644 --- a/src/main/java/world/bentobox/level/config/Settings.java +++ b/src/main/java/world/bentobox/level/config/Settings.java @@ -3,6 +3,7 @@ package world.bentobox.level.config; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -13,7 +14,7 @@ import world.bentobox.level.Level; public class Settings { - private Level level; + private final Level level; private boolean sumTeamDeaths; private Map blockLimits = new HashMap<>(); private Map blockValues = new HashMap<>(); @@ -23,16 +24,6 @@ public class Settings { private long levelCost; private int levelWait; - /** - * Stores number of chunks that can be updated in single tick. - */ - private int chunksPerTick; - - /** - * Stores number of tick delay between each chunk loading. - */ - private long updateTickDelay; - private List gameModes; @@ -43,22 +34,6 @@ public class Settings { // GameModes gameModes = level.getConfig().getStringList("game-modes"); - // Level calculation chunk load speed - this.setUpdateTickDelay(level.getConfig().getLong("updatetickdelay", 1)); - - if (this.getUpdateTickDelay() <= 0) - { - this.setUpdateTickDelay(1); - } - - // Level calculation chunk count per update - this.setChunksPerTick(level.getConfig().getInt("chunkspertick", 200)); - - if (this.getChunksPerTick() <= 0) - { - this.setChunksPerTick(200); - } - setLevelWait(level.getConfig().getInt("levelwait", 60)); if (getLevelWait() < 0) { setLevelWait(0); @@ -74,7 +49,7 @@ public class Settings { if (level.getConfig().isSet("limits")) { HashMap bl = new HashMap<>(); - for (String material : level.getConfig().getConfigurationSection("limits").getKeys(false)) { + for (String material : Objects.requireNonNull(level.getConfig().getConfigurationSection("limits")).getKeys(false)) { try { Material mat = Material.valueOf(material); bl.put(mat, level.getConfig().getInt("limits." + material, 0)); @@ -86,7 +61,7 @@ public class Settings { } if (level.getConfig().isSet("blocks")) { Map bv = new HashMap<>(); - for (String material : level.getConfig().getConfigurationSection("blocks").getKeys(false)) { + for (String material : Objects.requireNonNull(level.getConfig().getConfigurationSection("blocks")).getKeys(false)) { try { Material mat = Material.valueOf(material); @@ -102,11 +77,11 @@ public class Settings { // Worlds if (level.getConfig().isSet("worlds")) { ConfigurationSection worlds = level.getConfig().getConfigurationSection("worlds"); - for (String world : worlds.getKeys(false)) { + for (String world : Objects.requireNonNull(worlds).getKeys(false)) { World bWorld = Bukkit.getWorld(world); if (bWorld != null) { ConfigurationSection worldValues = worlds.getConfigurationSection(world); - for (String material : worldValues.getKeys(false)) { + for (String material : Objects.requireNonNull(worldValues).getKeys(false)) { Material mat = Material.valueOf(material); Map values = worldBlockValues.getOrDefault(bWorld, new HashMap<>()); values.put(mat, worldValues.getInt(material)); @@ -246,45 +221,11 @@ public class Settings { return level.getConfig().getBoolean("shorthand"); } - /** - * This method returns the number of chunks that can be processed at single tick. - * @return the value of chunksPerTick. + * @return the formula to calculate island levels */ - public int getChunksPerTick() - { - return this.chunksPerTick; + public String getLevelCalc() { + return level.getConfig().getString("level-calc", "blocks / level_cost"); } - - /** - * This method sets the chunksPerTick value. - * @param chunksPerTick the chunksPerTick new value. - * - */ - public void setChunksPerTick(int chunksPerTick) - { - this.chunksPerTick = chunksPerTick; - } - - - /** - * This method returns the delay between each update call. - * @return the value of updateTickDelay. - */ - public long getUpdateTickDelay() - { - return this.updateTickDelay; - } - - - /** - * This method sets the updateTickDelay value. - * @param updateTickDelay the updateTickDelay new value. - * - */ - public void setUpdateTickDelay(long updateTickDelay) - { - this.updateTickDelay = updateTickDelay; - } } diff --git a/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java b/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java index 4bf27ee..928e20a 100644 --- a/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java +++ b/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java @@ -8,11 +8,13 @@ import world.bentobox.level.Level; /** * @author tastybento * + * @deprecated As of 1.9.0, for removal. */ +@Deprecated public class LevelPlaceholder implements PlaceholderReplacer { - private Level addon; - private GameModeAddon gm; + private final Level addon; + private final GameModeAddon gm; /** * Provides placeholder support @@ -29,6 +31,9 @@ public class LevelPlaceholder implements PlaceholderReplacer { */ @Override public String onReplace(User user) { + addon.logWarning("You are using a deprecated placeholder."); + addon.log("Please replace any occurrence of 'Level_" + gm.getDescription().getName().toLowerCase() + "-island-level'"); + addon.log("by 'Level_" + gm.getDescription().getName().toLowerCase() + "_island_level'"); return addon.getLevelPresenter().getLevelString(addon.getIslandLevel(gm.getOverWorld(), user.getUniqueId())); } diff --git a/src/main/java/world/bentobox/level/placeholders/TopTenNamePlaceholder.java b/src/main/java/world/bentobox/level/placeholders/TopTenNamePlaceholder.java index b0e7c67..3e43d47 100644 --- a/src/main/java/world/bentobox/level/placeholders/TopTenNamePlaceholder.java +++ b/src/main/java/world/bentobox/level/placeholders/TopTenNamePlaceholder.java @@ -10,8 +10,9 @@ import world.bentobox.level.Level; /** * @author tastybento - * + * @deprecated As of 1.9.0, for removal. */ +@Deprecated public class TopTenNamePlaceholder implements PlaceholderReplacer { private final Level level; @@ -29,6 +30,9 @@ public class TopTenNamePlaceholder implements PlaceholderReplacer { */ @Override public String onReplace(User user) { + level.logWarning("You are using a deprecated placeholder."); + level.log("Please replace any occurrence of 'Level_" + gm.getDescription().getName().toLowerCase() + "-island-level-top-name-#'"); + level.log("by 'Level_" + gm.getDescription().getName().toLowerCase() + "_top_name_#'"); Collection values = level.getTopTen().getTopTenList(gm.getOverWorld()).getTopTen().keySet(); return values.size() < i ? "" : level.getPlayers().getName(values.stream().skip(i).findFirst().orElse(null)); } diff --git a/src/main/java/world/bentobox/level/placeholders/TopTenPlaceholder.java b/src/main/java/world/bentobox/level/placeholders/TopTenPlaceholder.java index 0da18d3..7fbd9b6 100644 --- a/src/main/java/world/bentobox/level/placeholders/TopTenPlaceholder.java +++ b/src/main/java/world/bentobox/level/placeholders/TopTenPlaceholder.java @@ -10,8 +10,9 @@ import world.bentobox.level.Level; /** * Provides the level values to placeholders * @author tastybento - * + * @deprecated As of 1.9.0, for removal. */ +@Deprecated public class TopTenPlaceholder implements PlaceholderReplacer { private final Level level; @@ -29,6 +30,9 @@ public class TopTenPlaceholder implements PlaceholderReplacer { */ @Override public String onReplace(User user) { + level.logWarning("You are using a deprecated placeholder."); + level.log("Please replace any occurrence of 'Level_" + gm.getDescription().getName().toLowerCase() + "-island-level-top-value-#'"); + level.log("by 'Level_" + gm.getDescription().getName().toLowerCase() + "_top_value_#'"); Collection values = level.getTopTen().getTopTenList(gm.getOverWorld()).getTopTen().values(); return values.size() < i ? "" : values.stream().skip(i).findFirst().map(String::valueOf).orElse(""); } diff --git a/src/main/java/world/bentobox/level/requests/LevelRequestHandler.java b/src/main/java/world/bentobox/level/requests/LevelRequestHandler.java index 747bda0..2798e67 100644 --- a/src/main/java/world/bentobox/level/requests/LevelRequestHandler.java +++ b/src/main/java/world/bentobox/level/requests/LevelRequestHandler.java @@ -10,7 +10,7 @@ import world.bentobox.level.Level; public class LevelRequestHandler extends AddonRequestHandler { - private Level addon; + private final Level addon; public LevelRequestHandler(Level addon) { super("island-level"); diff --git a/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java b/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java index 556434c..6c92f4c 100644 --- a/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java +++ b/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java @@ -20,7 +20,7 @@ public class TopTenRequestHandler extends AddonRequestHandler { /** * The level addon field. */ - private Level addon; + private final Level addon; /** * This constructor creates a new TopTenRequestHandler instance. @@ -33,7 +33,7 @@ public class TopTenRequestHandler extends AddonRequestHandler { } /** - * @see {@link AddonRequestHandler#handle(Map)} + * See {@link AddonRequestHandler#handle(Map)} */ @Override public Object handle(Map map) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ad79e4a..ae1f500 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -37,17 +37,17 @@ underwater: 1.0 # Value of one island level. Default 100. Minimum value is 1. levelcost: 100 +# Island level calculation formula +# blocks - the sum total of all block values, less any death penalty +# level_cost - in a linear equation, the value of one level +# This formula can include +,=,*,/,sqrt,^,sin,cos,tan. Result will always be rounded to a long integer +# for example, an alternative non-linear option could be: 3 * sqrt(blocks / level_cost) +level-calc: blocks / level_cost + + # Cooldown between level requests in seconds levelwait: 60 -# Delay between each task that loads chunks for calculating island level. -# Increasing this will increase time to calculate island level. -updatetickdelay: 1 - -# Number of chunks that will be processed at the same tick. -# Decreasing this will increase time to calculate island level. -chunkspertick: 200 - # Death penalty # How many block values a player will lose per death. # Default value of 100 means that for every death, the player will lose 1 level (if levelcost is 100) diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml new file mode 100644 index 0000000..950b8c0 --- /dev/null +++ b/src/main/resources/locales/de.yml @@ -0,0 +1,34 @@ +--- +admin: + level: + parameters: "" + description: Berechne das Insel Level für den Spieler + top: + remove: + description: entferne Spieler von Top-10 + parameters: "" + description: Zeige die Top-10 Liste + unknown-world: "&cUnbekannte Welt!" + display: "&f[rank]. &a[name] &7- &b[level]" +island: + level: + parameters: "[Spieler]" + description: Berechne dein Insel Level oder zeige das Level von [Spieler] + required-points-to-next-level: "&a[points] Punkte werden für das nächste Level + benötigt" + calculating: "&aBerechne Level..." + island-level-is: "&aInsel Level: &b[level]" + deaths: "&c([number] Tode)" + cooldown: "&cDu musst &b[time] &csekunden warten bevor du das erneut machen kannst." + value: + description: Zeige den Wert jedes Blockes + success-underwater: "&7Wert des Blockes Unterwasser: &e[value]" + success: "&7Wert: &e[value]" + empty-hand: "&cDu hast keinen Block in der Hand" + no-value: "&cDas Item hat kein wert!" + top: + description: Zeige die Top-10 + gui-title: "&aTop Zehn" + gui-heading: "&6[name]: &B[rank]" + island-level: "&BLevel [level]" + warp-to: "&ATeleportiere zu [name]'s Insel" diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 2d9cf43..dcae349 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -11,6 +11,9 @@ admin: description: "show the top ten list" unknown-world: "&cUnknown world!" display: "&f[rank]. &a[name] &7- &b[level]" + remove: + description: "remove player from Top Ten" + parameters: "" island: level: diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml index 740436c..3f906c0 100644 --- a/src/main/resources/locales/fr.yml +++ b/src/main/resources/locales/fr.yml @@ -1,41 +1,37 @@ -########################################################################################### -# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # -# the one at http://yaml-online-parser.appspot.com # -########################################################################################### - +--- +admin: + level: + description: calcule le niveau d'île d'un joueur + parameters: "" + top: + description: affiche le top 10 des îles + display: "&f[rank]. &a[name] &7- &b[level]" + unknown-world: "&cMonde inconnu." + remove: + description: retire le joueur du top 10 + parameters: "" +island: + level: + calculating: "&aCalcul du niveau en cours..." + deaths: "&c([number] morts)" + description: calcule le niveau de votre île ou affiche le niveau d'un [joueur] + island-level-is: "&aLe niveau d'île est &b[level]" + parameters: "[joueur]" + required-points-to-next-level: "&a[points] points avant le prochain niveau" + cooldown: "&cVous devez attendre &b[time] &csecondes avant de pouvoir refaire + cette action" + top: + description: affiche le top 10 + gui-heading: "&6[name]: &B[rank]" + gui-title: "&aTop 10" + island-level: "&BNiveau [level]" + warp-to: "&ATéléportation vers l'île de [name]" + value: + description: affiche la valeur d'un bloc + success: "&7Valeur de ce bloc : &e[value]" + success-underwater: "&7Valeur de ce bloc en dessous du niveau de la mer : &e[value]" + empty-hand: "&cIl n'y a aucun bloc dans votre main" + no-value: "&cCet objet n'a pas de valeur." meta: authors: - plagoutte - -admin: - level: - parameters: "" - description: "calcule le niveau d'île d'un joueur" - top: - description: "affiche le top 10 des îles" - unknown-world: "&cMonde inconnu." - display: "&f[rank]. &a[name] &7- &b[level]" - -island: - level: - parameters: "[joueur]" - description: "calcule le niveau de votre île ou affiche le niveau d'un [joueur]" - calculating: "&aCalcul du niveau en cours..." - island-level-is: "&aLe niveau d'île est &b[level]" - required-points-to-next-level: "&a[points] points avant le prochain niveau" - deaths: "&c([number] morts)" - cooldown: "&cVous devez attendre &b[time] &csecondes avant de pouvoir re-faire cette action" - - top: - description: "affiche le top 10" - gui-title: "&aTop 10" - gui-heading: "&6[name]: &B[rank]" - island-level: "&BNiveau [level]" - warp-to: "&ATéléportation vers l'île de [name]" - - value: - description: "affiche la valeur d'un bloc" - success: "§7Valeur de ce bloc : §e[value]" - success-underwater: "§7Valeur de ce bloc en dessous du niveau de la mer : §e[value]" - empty-hand: "§cIl n'y a aucun bloc dans votre main" - no-value: "§cCet objet n'a pas de valeur." diff --git a/src/main/resources/locales/hu.yml b/src/main/resources/locales/hu.yml new file mode 100644 index 0000000..f8131c8 --- /dev/null +++ b/src/main/resources/locales/hu.yml @@ -0,0 +1,40 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +admin: + level: + parameters: "" + description: "Egy játékos sziget szintjének kiszámítása" + top: + description: "Top Tíz lista megtekintése" + unknown-world: "&cIsmeretlen világ!" + display: "&f[rank]. &a[name] &7- &b[level]" + +island: + level: + parameters: "[player]" + description: "A saját vagy más játékos sziget szintjének kiszámítása" + calculating: "&aSziget szint kiszámítása..." + island-level-is: "&aA sziget szint: &b[level]" + required-points-to-next-level: "&a[points] pont szükséges a következő szinthez." + deaths: "&c([number] halál)" + cooldown: "&cVárnod kell &b[time] &cmásodpercet, hogy újra használhasd." + + top: + description: "Top Tíz lista megtekintése" + gui-title: "&aTop Tíz" + gui-heading: "&6[name]: &B[rank]" + island-level: "&BLevel [level]" + warp-to: "&ATeleportálás [name] szigetére." + remove: + description: "játékos törlése a Top Tízből" + parameters: "" + + value: + description: "Bármely blokk értékét mutatja" + success: "&7Ennek a blokknak az értéke: &e[value]" + success-underwater: "&7Ennek a blokknak a tengerszint alatti értéke: &e[value]" + empty-hand: "&cNincsenek blokkok a kezedben" + no-value: "&cEnnek nincs értéke." diff --git a/src/main/resources/locales/id.yml b/src/main/resources/locales/id.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/locales/id.yml @@ -0,0 +1 @@ + diff --git a/src/main/resources/locales/ro.yml b/src/main/resources/locales/ro.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/locales/ro.yml @@ -0,0 +1 @@ + diff --git a/src/test/java/world/bentobox/level/TopTenTest.java b/src/test/java/world/bentobox/level/TopTenTest.java index 8dd3807..d0a78d9 100644 --- a/src/test/java/world/bentobox/level/TopTenTest.java +++ b/src/test/java/world/bentobox/level/TopTenTest.java @@ -2,7 +2,6 @@ package world.bentobox.level; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -59,7 +58,6 @@ public class TopTenTest { private BentoBox plugin; @Mock private static AbstractDatabaseHandler handler; - private List topTen; @Mock private IslandsManager im; @Mock @@ -106,7 +104,7 @@ public class TopTenTest { // Fill the top ten TopTenData ttd = new TopTenData(); ttd.setUniqueId("world"); - topTen = new ArrayList<>(); + List topTen = new ArrayList<>(); for (long i = -100; i < 100; i ++) { ttd.addLevel(UUID.randomUUID(), i); topTen.add(ttd); @@ -184,7 +182,7 @@ public class TopTenTest { TopTen tt = new TopTen(addon); UUID ownerUUID = UUID.randomUUID(); tt.addEntry(world, ownerUUID, 200L); - assertTrue(tt.getTopTenList(world).getTopTen().get(ownerUUID) == 200L); + assertEquals(200L, (long) tt.getTopTenList(world).getTopTen().get(ownerUUID)); } @Test @@ -264,7 +262,7 @@ public class TopTenTest { TopTen tt = new TopTen(addon); UUID ownerUUID = UUID.randomUUID(); tt.addEntry(world, ownerUUID, 200L); - assertTrue(tt.getTopTenList(world).getTopTen().get(ownerUUID) == 200L); + assertEquals(200L, (long) tt.getTopTenList(world).getTopTen().get(ownerUUID)); // Remove it tt.removeEntry(world, ownerUUID); assertNull(tt.getTopTenList(world).getTopTen().get(ownerUUID)); diff --git a/src/test/java/world/bentobox/level/commands/admin/AdminTopRemoveCommandTest.java b/src/test/java/world/bentobox/level/commands/admin/AdminTopRemoveCommandTest.java new file mode 100644 index 0000000..bebad5c --- /dev/null +++ b/src/test/java/world/bentobox/level/commands/admin/AdminTopRemoveCommandTest.java @@ -0,0 +1,182 @@ +package world.bentobox.level.commands.admin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.level.Level; +import world.bentobox.level.TopTen; +import world.bentobox.level.objects.TopTenData; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class}) +public class AdminTopRemoveCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private Level addon; + @Mock + private World world; + @Mock + private IslandWorldManager iwm; + @Mock + private GameModeAddon gameModeAddon; + @Mock + private Player p; + @Mock + private LocalesManager lm; + @Mock + private PlayersManager pm; + + private AdminTopRemoveCommand atrc; + @Mock + private TopTen tt; + @Mock + private TopTenData ttd; + + @Before + public void setUp() { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + // Addon + when(ic.getAddon()).thenReturn(addon); + when(ic.getPermissionPrefix()).thenReturn("bskyblock."); + when(ic.getLabel()).thenReturn("island"); + when(ic.getTopLabel()).thenReturn("island"); + when(ic.getWorld()).thenReturn(world); + when(ic.getTopLabel()).thenReturn("bsb"); + + + // IWM friendly name + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + + // World + when(world.toString()).thenReturn("world"); + when(world.getName()).thenReturn("BSkyBlock_world"); + + + // Player manager + when(plugin.getPlayers()).thenReturn(pm); + when(pm.getUser(anyString())).thenReturn(user); + // topTen + when(addon.getTopTen()).thenReturn(tt); + when(tt.getTopTenList(any())).thenReturn(ttd); + // User + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getTranslation(any())).thenAnswer(invocation -> invocation.getArgument(0, String.class)); + + atrc = new AdminTopRemoveCommand(addon, ic); + } + + @After + public void tearDown() { + User.clearUsers(); + } + + /** + * Test method for {@link world.bentobox.level.commands.admin.AdminTopRemoveCommand#AdminTopRemoveCommand(world.bentobox.level.Level, world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testAdminTopRemoveCommand() { + assertEquals("remove", atrc.getLabel()); + assertEquals("delete", atrc.getAliases().get(0)); + } + + /** + * Test method for {@link world.bentobox.level.commands.admin.AdminTopRemoveCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("bskyblock.admin.top.remove", atrc.getPermission()); + assertEquals("admin.top.remove.parameters", atrc.getParameters()); + assertEquals("admin.top.remove.description", atrc.getDescription()); + assertFalse(atrc.isOnlyPlayer()); + + } + + /** + * Test method for {@link world.bentobox.level.commands.admin.AdminTopRemoveCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteWrongArgs() { + assertFalse(atrc.canExecute(user, "delete", Collections.emptyList())); + verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); + } + + /** + * Test method for {@link world.bentobox.level.commands.admin.AdminTopRemoveCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteUnknown() { + when(pm.getUser(anyString())).thenReturn(null); + assertFalse(atrc.canExecute(user, "delete", Collections.singletonList("tastybento"))); + verify(user).sendMessage(eq("general.errors.unknown-player"), eq(TextVariables.NAME), eq("tastybento")); + } + + /** + * Test method for {@link world.bentobox.level.commands.admin.AdminTopRemoveCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteKnown() { + assertTrue(atrc.canExecute(user, "delete", Collections.singletonList("tastybento"))); + } + + /** + * Test method for {@link world.bentobox.level.commands.admin.AdminTopRemoveCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + testCanExecuteKnown(); + assertTrue(atrc.execute(user, "delete", Collections.singletonList("tastybento"))); + verify(ttd).remove(eq(uuid)); + verify(user).sendMessage(eq("general.success")); + } + +} diff --git a/src/test/java/world/bentobox/level/objects/TopTenDataTest.java b/src/test/java/world/bentobox/level/objects/TopTenDataTest.java index a0da168..e1d9cc6 100644 --- a/src/test/java/world/bentobox/level/objects/TopTenDataTest.java +++ b/src/test/java/world/bentobox/level/objects/TopTenDataTest.java @@ -7,7 +7,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; -import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -17,15 +16,12 @@ import org.junit.Test; */ public class TopTenDataTest { - private Map topTen = new LinkedHashMap<>(); + private final Map topTen = new LinkedHashMap<>(); private TopTenData ttd; - private UUID uuid = UUID.randomUUID(); + private final UUID uuid = UUID.randomUUID(); - /** - * @throws java.lang.Exception - */ @Before - public void setUp() throws Exception { + public void setUp() { // Create a top ten map for (long i = 0; i < 100; i++) { topTen.put(UUID.randomUUID(), i); @@ -39,13 +35,6 @@ public class TopTenDataTest { ttd = new TopTenData(); } - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - /** * Test method for {@link world.bentobox.level.objects.TopTenData#getTopTen()}. */ @@ -93,9 +82,7 @@ public class TopTenDataTest { @Test public void testAddAndGetLevel() { topTen.forEach(ttd::addLevel); - topTen.keySet().forEach(k -> { - assertTrue(topTen.get(k) == ttd.getLevel(k)); - }); + topTen.keySet().forEach(k -> assertEquals((long) topTen.get(k), ttd.getLevel(k))); } /**