diff --git a/pom.xml b/pom.xml index 036adc1..9530fa4 100644 --- a/pom.xml +++ b/pom.xml @@ -59,13 +59,13 @@ 2.0.2 1.14.4-R0.1-SNAPSHOT - 1.9.0-SNAPSHOT + 1.9.0 ${build.version}-SNAPSHOT -LOCAL - 1.9.0 + 1.9.3 @@ -147,6 +147,11 @@ codemc-public https://repo.codemc.org/repository/maven-public/ + + + jitpack.io + https://jitpack.io + @@ -182,6 +187,12 @@ ${bentobox.version} provided + + + com.github.OmerBenGera + WildStackerAPI + b17 + @@ -242,9 +253,11 @@ maven-javadoc-plugin 3.0.1 - public + private false -Xdoclint:none + ${java.home}/bin/javadoc + 8 diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java index 4710e11..c2ef35e 100644 --- a/src/main/java/world/bentobox/level/Level.java +++ b/src/main/java/world/bentobox/level/Level.java @@ -6,12 +6,14 @@ import java.util.Map; import java.util.UUID; import org.bukkit.World; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.level.calculators.CalcIslandLevel; import world.bentobox.level.commands.admin.AdminLevelCommand; import world.bentobox.level.commands.admin.AdminTopCommand; import world.bentobox.level.commands.island.IslandLevelCommand; @@ -55,16 +57,17 @@ public class Level extends Addon { * @param user - the user who is asking, or null if none * @param playerUUID - the target island member's UUID */ - public void calculateIslandLevel(World world, @Nullable User user, UUID playerUUID) { + public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) { levelPresenter.calculateIslandLevel(world, user, playerUUID); } /** - * Get level from cache for a player - * @param targetPlayer - target player - * @return Level of player + * Get level from cache for a player. + * @param targetPlayer - target player UUID + * @return Level of player or zero if player is unknown or UUID is null */ - public long getIslandLevel(World world, UUID targetPlayer) { + public long getIslandLevel(World world, @Nullable UUID targetPlayer) { + if (targetPlayer == null) return 0L; LevelsData ld = getLevelsData(targetPlayer); return ld == null ? 0L : ld.getLevel(world); } @@ -74,7 +77,7 @@ public class Level extends Addon { * @param targetPlayer - UUID of target player * @return LevelsData object or null if not found */ - public LevelsData getLevelsData(UUID targetPlayer) { + public LevelsData getLevelsData(@NonNull UUID targetPlayer) { // Get from database if not in cache if (!levelsCache.containsKey(targetPlayer) && handler.objectExists(targetPlayer.toString())) { levelsCache.put(targetPlayer, handler.loadObject(targetPlayer.toString())); @@ -163,9 +166,9 @@ public class Level extends Addon { 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")); + .map(island -> getIslandLevel(gm.getOverWorld(), island.getOwner())) + .map(level -> getLevelPresenter().getLevelString(level)) + .orElse("0")); // Top Ten for (int i = 1; i <= 10; i++) { @@ -175,7 +178,7 @@ public class Level extends Addon { 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(""); + return values.size() < rank ? "" : values.stream().skip(rank - 1).findFirst().map(String::valueOf).orElse(""); }); // Name @@ -183,7 +186,7 @@ public class Level extends Addon { 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)); + return values.size() < rank ? "" : getPlayers().getName(values.stream().skip(rank - 1).findFirst().orElse(null)); }); } } @@ -197,6 +200,13 @@ public class Level extends Addon { registerRequestHandler(new LevelRequestHandler(this)); registerRequestHandler(new TopTenRequestHandler(this)); + // Check if WildStackers is enabled on the server + if (getPlugin().getServer().getPluginManager().getPlugin("WildStacker") != null) { + // I only added support for counting blocks into the island level + // Someone else can PR if they want spawners added to the Leveling system :) + CalcIslandLevel.stackersEnabled = true; + } else CalcIslandLevel.stackersEnabled = false; + // Done } @@ -236,7 +246,7 @@ public class Level extends Addon { * @param island - island * @param level - initial calculated island level */ - public void setInitialIslandLevel(Island island, long level) { + public void setInitialIslandLevel(@NonNull Island island, long level) { if (island.getWorld() == null || island.getOwner() == null) { this.logError("Level: request to store a null (initial) " + island.getWorld() + " " + island.getOwner()); return; @@ -250,15 +260,19 @@ public class Level extends Addon { * @param island - island * @return level or 0 by default */ - public long getInitialIslandLevel(Island island) { + public long getInitialIslandLevel(@NonNull Island island) { return levelsCache.containsKey(island.getOwner()) ? levelsCache.get(island.getOwner()).getInitialLevel(island.getWorld()) : 0L; } + /** + * @return database handler + */ + @Nullable public Database getHandler() { return handler; } - public void uncachePlayer(UUID uniqueId) { + public void uncachePlayer(@Nullable UUID uniqueId) { if (levelsCache.containsKey(uniqueId) && levelsCache.get(uniqueId) != null) { handler.saveObject(levelsCache.get(uniqueId)); } diff --git a/src/main/java/world/bentobox/level/LevelPresenter.java b/src/main/java/world/bentobox/level/LevelPresenter.java index c4a5bd3..4c3c304 100644 --- a/src/main/java/world/bentobox/level/LevelPresenter.java +++ b/src/main/java/world/bentobox/level/LevelPresenter.java @@ -82,7 +82,7 @@ public class LevelPresenter { } /** - * Get the string representation of the level. May be converted to shorthand notation, e.g., 104556 -> 10.5k + * Get the string representation of the level. May be converted to shorthand notation, e.g., 104556 = 10.5k * @param lvl - long value to represent * @return string of the level. */ diff --git a/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java b/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java index e64b31d..f21e1b6 100644 --- a/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java +++ b/src/main/java/world/bentobox/level/calculators/CalcIslandLevel.java @@ -1,15 +1,27 @@ package world.bentobox.level.calculators; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import com.bgsoftware.wildstacker.api.WildStackerAPI; +import com.bgsoftware.wildstacker.api.objects.StackedBarrel; 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.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Slab; @@ -28,7 +40,8 @@ public class CalcIslandLevel { private static final String LINE_BREAK = "=================================="; - public static final long MAX_AMOUNT = 10000; + public static final long MAX_AMOUNT = 10000; + public static Boolean stackersEnabled; private final Level addon; @@ -92,17 +105,15 @@ public class CalcIslandLevel { 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); + queueid = Bukkit.getScheduler().scheduleSyncRepeatingTask(addon.getPlugin(), () -> { + for (int i = 0; i < addon.getSettings().getChunks(); i++) { + if (q.size() == 0) { + return; } + Chunk c = q.remove(); + getChunk(c); } - }, 1L, 1L); + }, addon.getSettings().getTickDelay(), addon.getSettings().getTickDelay()); chunksToScan.forEach(c -> worlds.forEach(w -> Util.getChunkAtAsync(w, c.x, c.z).thenAccept(this::addChunkQueue))); } @@ -115,7 +126,7 @@ public class CalcIslandLevel { ChunkSnapshot snapShot = ch.getChunkSnapshot(); Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> { - this.scanChunk(snapShot); + this.scanChunk(snapShot, ch); count.getAndIncrement(); if (count.get() == total) { Bukkit.getScheduler().cancelTask(queueid); @@ -124,7 +135,7 @@ public class CalcIslandLevel { }); } - private void scanChunk(ChunkSnapshot chunk) { + private void scanChunk(ChunkSnapshot chunk, Chunk physicalChunk) { World chunkWorld = Bukkit.getWorld(chunk.getWorldName()); if (chunkWorld == null) return; int maxHeight = chunkWorld.getMaxHeight(); @@ -148,23 +159,37 @@ public class CalcIslandLevel { if (Tag.SLABS.isTagged(blockData.getMaterial())) { Slab slab = (Slab)blockData; if (slab.getType().equals(Slab.Type.DOUBLE)) { - checkBlock(blockData, belowSeaLevel); + checkBlock(blockData.getMaterial(), belowSeaLevel); } } - checkBlock(blockData, belowSeaLevel); + + // Hook for Wild Stackers (Blocks Only) + if (stackersEnabled && blockData.getMaterial() == Material.CAULDRON) { + Block cauldronBlock = physicalChunk.getBlock(x, y, z); + if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(cauldronBlock)) { + StackedBarrel barrel = WildStackerAPI.getStackedBarrel(cauldronBlock); + int barrelAmt = WildStackerAPI.getBarrelAmount(cauldronBlock); + for (int _x = 0; _x < barrelAmt; _x++) { + checkBlock(barrel.getType(), belowSeaLevel); + } + } + } + + checkBlock(blockData.getMaterial(), belowSeaLevel); } } } } - private void checkBlock(BlockData bd, boolean belowSeaLevel) { - int count = limitCount(bd.getMaterial()); + // Didnt see a reason to pass BlockData when all that's used was the material + private void checkBlock(Material mat, boolean belowSeaLevel) { + int count = limitCount(mat); if (belowSeaLevel) { result.underWaterBlockCount.addAndGet(count); - result.uwCount.add(bd.getMaterial()); + result.uwCount.add(mat); } else { result.rawBlockCount.addAndGet(count); - result.mdCount.add(bd.getMaterial()); + result.mdCount.add(mat); } } @@ -383,7 +408,7 @@ public class CalcIslandLevel { * Set level * @param level - level */ - public void setLevel(int level) { + public void setLevel(long level) { this.level.set(level); } /** diff --git a/src/main/java/world/bentobox/level/calculators/PlayerLevel.java b/src/main/java/world/bentobox/level/calculators/PlayerLevel.java index 90e3f3f..87820f8 100644 --- a/src/main/java/world/bentobox/level/calculators/PlayerLevel.java +++ b/src/main/java/world/bentobox/level/calculators/PlayerLevel.java @@ -66,7 +66,7 @@ public class PlayerLevel { keyValues.put("pointsToNextLevel", calc.getResult().getPointsToNextLevel()); keyValues.put("deathHandicap", calc.getResult().getDeathHandicap()); keyValues.put("initialLevel", calc.getResult().getInitialLevel()); - addon.getServer().getPluginManager().callEvent(new AddonEvent().builder().addon(addon).keyValues(keyValues).build()); + new AddonEvent().builder().addon(addon).keyValues(keyValues).build(); Results results = ilce.getResults(); // Save the results island.getMemberSet().forEach(m -> addon.setIslandLevel(world, m, results.getLevel())); diff --git a/src/main/java/world/bentobox/level/config/Settings.java b/src/main/java/world/bentobox/level/config/Settings.java index 4bd5a4c..9beeef9 100644 --- a/src/main/java/world/bentobox/level/config/Settings.java +++ b/src/main/java/world/bentobox/level/config/Settings.java @@ -23,6 +23,8 @@ public class Settings { private int deathpenalty; private long levelCost; private int levelWait; + private int chunks; + private long taskDelay; private List gameModes; @@ -33,9 +35,22 @@ public class Settings { // GameModes gameModes = level.getConfig().getStringList("game-modes"); + + // Performance + setTickDelay(level.getConfig().getLong("task-delay",1)); + if (taskDelay < 1L) { + level.logError("task-delay must be at least 1"); + setTickDelay(1L); + } + setChunks(level.getConfig().getInt("chunks", 10)); + if (chunks < 1) { + level.logError("chunks must be at least 1"); + setChunks(1); + } setLevelWait(level.getConfig().getInt("levelwait", 60)); if (getLevelWait() < 0) { + level.logError("levelwait must be at least 0"); setLevelWait(0); } setDeathpenalty(level.getConfig().getInt("deathpenalty", 0)); @@ -215,7 +230,7 @@ public class Settings { } /** - * @return true if levels should be shown in shorthand notation, e.g., 10,234 -> 10k + * @return true if levels should be shown in shorthand notation, e.g., 10,234 = 10k */ public boolean isShortHand() { return level.getConfig().getBoolean("shorthand"); @@ -228,4 +243,32 @@ public class Settings { return level.getConfig().getString("level-calc", "blocks / level_cost"); } + /** + * @return the chunks + */ + public int getChunks() { + return chunks; + } + + /** + * @param chunks the chunks to set + */ + public void setChunks(int chunks) { + this.chunks = chunks; + } + + /** + * @return the tickDelay + */ + public long getTickDelay() { + return taskDelay; + } + + /** + * @param tickDelay the tickDelay to set + */ + public void setTickDelay(long tickDelay) { + this.taskDelay = tickDelay; + } + } diff --git a/src/main/java/world/bentobox/level/event/IslandLevelCalculatedEvent.java b/src/main/java/world/bentobox/level/event/IslandLevelCalculatedEvent.java index 73a9ed8..8ae9e9e 100644 --- a/src/main/java/world/bentobox/level/event/IslandLevelCalculatedEvent.java +++ b/src/main/java/world/bentobox/level/event/IslandLevelCalculatedEvent.java @@ -1,5 +1,6 @@ package world.bentobox.level.event; +import java.util.List; import java.util.UUID; import world.bentobox.bentobox.api.events.IslandBaseEvent; @@ -29,11 +30,58 @@ public class IslandLevelCalculatedEvent extends IslandBaseEvent { } /** + * Do NOT get this result if you are not a BentoBox addon! * @return the results */ public Results getResults() { return results; } + + /** + * @return death handicap value + */ + public int getDeathHandicap() { + return results.getDeathHandicap(); + } + + /** + * Get the island's initial level. It may be zero if it was never calculated + * or if a player was registered to the island after it was made. + * @return initial level of island as calculated when the island was created. + */ + public long getInitialLevel() { + return results.getInitialLevel(); + } + + /** + * @return the level calculated + */ + public long getLevel() { + return results.getLevel(); + } + + + /** + * Overwrite the level. This level will be used instead of the calculated level. + * @param level - the level to set + */ + public void setLevel(long level) { + results.setLevel(level); + } + + /** + * @return number of points required to next level + */ + public long getPointsToNextLevel() { + return results.getPointsToNextLevel(); + } + + /** + * @return a human readable report explaining how the calculation was made + */ + public List getReport() { + return results.getReport(); + } /** * @return the targetPlayer @@ -42,6 +90,7 @@ public class IslandLevelCalculatedEvent extends IslandBaseEvent { return targetPlayer; } /** + * Do not use this if you are not a BentoBox addon * @param results the results to set */ public void setResults(Results results) { @@ -55,5 +104,4 @@ public class IslandLevelCalculatedEvent extends IslandBaseEvent { this.targetPlayer = targetPlayer; } - } diff --git a/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java b/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java index 928e20a..964a4c6 100644 --- a/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java +++ b/src/main/java/world/bentobox/level/placeholders/LevelPlaceholder.java @@ -31,9 +31,6 @@ 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 3e43d47..fe52a5e 100644 --- a/src/main/java/world/bentobox/level/placeholders/TopTenNamePlaceholder.java +++ b/src/main/java/world/bentobox/level/placeholders/TopTenNamePlaceholder.java @@ -30,9 +30,6 @@ 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 7fbd9b6..f96c9be 100644 --- a/src/main/java/world/bentobox/level/placeholders/TopTenPlaceholder.java +++ b/src/main/java/world/bentobox/level/placeholders/TopTenPlaceholder.java @@ -30,9 +30,6 @@ 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/TopTenRequestHandler.java b/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java index 6c92f4c..ea2f524 100644 --- a/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java +++ b/src/main/java/world/bentobox/level/requests/TopTenRequestHandler.java @@ -7,7 +7,6 @@ import org.bukkit.Bukkit; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.level.Level; -import world.bentobox.level.objects.TopTenData; /** @@ -54,8 +53,7 @@ public class TopTenRequestHandler extends AddonRequestHandler { return Collections.emptyMap(); } - // Null-point check. - TopTenData data = addon.getTopTen().getTopTenList(Bukkit.getWorld((String) map.get(WORLD_NAME))); - return data != null ? data.getTopTen() : Collections.emptyMap(); + // No null check required + return addon.getTopTen().getTopTenList(Bukkit.getWorld((String) map.get(WORLD_NAME))).getTopTen(); } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ae1f500..38764e6 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -9,6 +9,15 @@ game-modes: - CaveBlock #- SkyGrid +# Performance settings +# Level is very processor-intensive, so these settings may need to be tweaked to optimize for your server +# Delay between each task that loads chunks for calculating levels +# Increasing this will slow down level calculations but reduce average load +task-delay: 1 + +# Number of chunks that will be processed per task +chunks: 10 + # This file lists the values for various blocks that are used to calculate the # island level. Level = total of all blocks in island boundary / 100. # Players with the permission askyblock.island.multiplier.# will have their blocks diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml new file mode 100644 index 0000000..ea66a57 --- /dev/null +++ b/src/main/resources/locales/cs.yml @@ -0,0 +1,42 @@ +########################################################################################### +# 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 # +# # +# Translation by: CZghost # +########################################################################################### + +admin: + level: + parameters: "" + description: "vypočítat úroveň ostrova hráče" + top: + description: "ukázat seznam TOP 10" + unknown-world: "&cNeznámý svět!" + display: "&f[rank]. &a[name] &7- &b[level]" + remove: + description: "odstranit hráče z TOP 10" + parameters: "" + +island: + level: + parameters: "[player]" + description: "spočítat úroveň tvého ostrova nebo ostrova hráče [player]" + calculating: "&aPočítám úroveň..." + island-level-is: "&aÚroveň ostrova je &b[level]" + required-points-to-next-level: "&a[points] vyžadováno do další úrovně" + deaths: "&c([number] smrtí)" + cooldown: "&cMusíš čekat &b[time] &csekund, než můžeš příkaz znovu použít" + + top: + description: "ukázat TOP 10" + gui-title: "&aTOP 10" + gui-heading: "&6[name]: &B[rank]" + island-level: "&BÚroveň [level]" + warp-to: "&AWarp na ostrov [name]" + + value: + description: "ukáže hodnotu jakéhokoliv bloku" + success: "&7Hodnota tohoto bloku je: &e[value]" + success-underwater: "&7Hodnota tohoto bloku pod úrovní moře: &e[value]" + empty-hand: "&cNemáš v ruce žádný blok" + no-value: "&cTento předmět nemá žádnou hodnotu." \ No newline at end of file diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index 08b3621..9e0863d 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -1,37 +1,35 @@ -########################################################################################### -# 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: aprēķina spēlētāja salas līmeni parameters: "" - description: "aprēķina spēlētāja salas līmeni" top: - description: "rādīt labākās 10 salas" - unknown-world: "&cNezināma pasaule!" + description: rādīt labākās 10 salas display: "&f[rank]. &a[name] &7- &b[level]" - + unknown-world: "&cNezināma pasaule!" + remove: + description: noņemt spēlētāju no labāko desmit saraksta + parameters: "" island: - level: - parameters: "[player]" - description: "aprēķina tavas salas līmeni, vai parāda spēlētāja [player] līmeni" - calculating: "&aAprēķina līmeni..." - island-level-is: "&aSalas līmenis ir &b[level]" - required-points-to-next-level: "&aNepieciešami [points] punkti, lai sasniegtu nākošo līmeni" + level: + calculating: "&aAprēķina līmeni..." + cooldown: "&cTev ir jāuzgaida &b[time]&c sekundes, lai vēlreiz aprēķinātu salas + līmeni!" deaths: "&c([number] nāves)" - cooldown: "&cTev ir jāuzgaida &b[time]&c sekundes, lai vēlreiz aprēķinātu salas līmeni!" - + description: aprēķina tavas salas līmeni, vai parāda spēlētāja [player] līmeni + island-level-is: "&aSalas līmenis ir &b[level]" + parameters: "[player]" + required-points-to-next-level: "&aNepieciešami [points] punkti, lai sasniegtu + nākošo līmeni" top: - description: "rādīt labākos 10" - gui-title: "&aLabākie 10" + description: rādīt labākos 10 gui-heading: "&6[name]: &B[rank]" + gui-title: "&aLabākie 10" island-level: "&BLīmenis [level]" warp-to: "&APārvietoties uz [name] salu." - value: - description: "rādīt vērtību jebkuram blokam" - success: "&7Vērtība šim blokam ir: &e[value]" - success-underwater: "&7Vērtība šim blokam zem jūras līmeņa: &e[value]" + description: rādīt vērtību jebkuram blokam empty-hand: "&cTev nav bloks rokās." no-value: "&cŠim blokam/priekšmetam nav vērtības." + success: "&7Vērtība šim blokam ir: &e[value]" + success-underwater: "&7Vērtība šim blokam zem jūras līmeņa: &e[value]" diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 47001af..00f291d 100755 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -6,25 +6,35 @@ admin: level: parameters: "" - description: "计算玩家的岛屿等级" + description: "计算某玩家的岛屿等级" top: - description: "显示排名前十榜单" - unknown-world: "&c未知世界!" - + description: "显示前十名" + unknown-world: "&c未知世界!" + display: "&f[rank]. &a[name] &7- &b[level]" + remove: + description: "将玩家移出前十" + parameters: "" + island: level: parameters: "[player]" - description: "计算您的岛屿等级或显示 [player] 的岛屿等级" - calculating: "&a正在计算等级……" - island-level-is: "&a岛屿等级是 &b[level]" - required-points-to-next-level: "&a距离下一级还需要 [points] 点" - deaths: "&c([number] 次死亡)" - cooldown: "&c在您可以再次做这件事之前必须等待 &b[time] &c秒" + description: "计算你或玩家 [player] 的岛屿等级" + calculating: "&a计算等级中..." + island-level-is: "&a岛屿等级为 &b[level]" + required-points-to-next-level: "&a还需 [points] 才能升到下一级" + deaths: "&c([number] 次死亡)" + cooldown: "&c再等 &b[time] &c秒才能再次使用" top: - description: "显示排名前十榜单" - gui-title: "&a前十榜" + description: "显示前十名" + gui-title: "&a前十" gui-heading: "&6[name]: &B[rank]" island-level: "&B等级 [level]" - warp-to: "&A正在传送到 [name] 的岛屿" - \ No newline at end of file + warp-to: "&A正传送到 [name] 的岛屿" + + value: + description: "查看某方块的价值" + success: "&7本方块的价值: &e[value]" + success-underwater: "&7本方块的水下价值: &e[value]" + empty-hand: "&c你手里没有方块" + no-value: "&c这个东西一文不值." diff --git a/src/test/java/world/bentobox/level/LevelTest.java b/src/test/java/world/bentobox/level/LevelTest.java new file mode 100644 index 0000000..d2d9353 --- /dev/null +++ b/src/test/java/world/bentobox/level/LevelTest.java @@ -0,0 +1,371 @@ +package world.bentobox.level; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +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.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; +import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +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.Settings; +import world.bentobox.bentobox.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.FlagsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.level.listeners.IslandTeamListeners; +import world.bentobox.level.listeners.JoinLeaveListener; + +/** + * @author tastybento + * + */ +@SuppressWarnings("deprecation") +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class}) +public class LevelTest { + + private static File jFile; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private BentoBox plugin; + @Mock + private FlagsManager fm; + @Mock + private GameModeAddon gameMode; + @Mock + private AddonsManager am; + @Mock + private BukkitScheduler scheduler; + @Mock + private Settings settings; + + private Level addon; + + @Mock + private Logger logger; + @Mock + private PlaceholdersManager phm; + @Mock + private CompositeCommand cmd; + @Mock + private CompositeCommand adminCmd; + @Mock + private World world; + private UUID uuid; + + @BeforeClass + public static void beforeClass() throws IOException { + jFile = new File("addon.jar"); + // Copy over config file from src folder + Path fromPath = Paths.get("src/main/resources/config.yml"); + Path path = Paths.get("config.yml"); + Files.copy(fromPath, path); + try (JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(jFile))) { + //Added the new files to the jar. + try (FileInputStream fis = new FileInputStream(path.toFile())) { + byte[] buffer = new byte[1024]; + int bytesRead = 0; + JarEntry entry = new JarEntry(path.toString()); + tempJarOutputStream.putNextEntry(entry); + while((bytesRead = fis.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + } + } + } + + /** + * @throws java.lang.Exception + */ + @SuppressWarnings("deprecation") + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger()); + //when(plugin.isEnabled()).thenReturn(true); + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + + // Locales + // Return the reference (USE THIS IN THE FUTURE) + when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + + // Server + PowerMockito.mockStatic(Bukkit.class); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class)); + + // Addon + addon = new Level(); + File dataFolder = new File("addons/Level"); + addon.setDataFolder(dataFolder); + addon.setFile(jFile); + AddonDescription desc = new AddonDescription.Builder("bentobox", "Level", "1.3").description("test").authors("tastybento").build(); + addon.setDescription(desc); + // Addons manager + when(plugin.getAddonsManager()).thenReturn(am); + // One game mode + when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode)); + AddonDescription desc2 = new AddonDescription.Builder("bentobox", "BSkyBlock", "1.3").description("test").authors("tasty").build(); + when(gameMode.getDescription()).thenReturn(desc2); + + // Player command + @NonNull + Optional opCmd = Optional.of(cmd); + when(gameMode.getPlayerCommand()).thenReturn(opCmd); + // Admin command + Optional opAdminCmd = Optional.of(adminCmd); + when(gameMode.getAdminCommand()).thenReturn(opAdminCmd); + + // Flags manager + when(plugin.getFlagsManager()).thenReturn(fm); + when(fm.getFlags()).thenReturn(Collections.emptyList()); + + // The database type has to be created one line before the thenReturn() to work! + when(plugin.getSettings()).thenReturn(settings); + DatabaseType value = DatabaseType.JSON; + when(settings.getDatabaseType()).thenReturn(value); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(scheduler); + ItemMeta meta = mock(ItemMeta.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(itemFactory.getItemMeta(any())).thenReturn(meta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + UnsafeValues unsafe = mock(UnsafeValues.class); + when(unsafe.getDataVersion()).thenReturn(777); + when(Bukkit.getUnsafe()).thenReturn(unsafe); + + // placeholders + when(plugin.getPlaceholdersManager()).thenReturn(phm); + + // World + when(world.getName()).thenReturn("bskyblock-world"); + // Island + when(island.getWorld()).thenReturn(world); + when(island.getOwner()).thenReturn(uuid); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + deleteAll(new File("database")); + } + + @AfterClass + public static void cleanUp() throws Exception { + new File("addon.jar").delete(); + new File("config.yml").delete(); + deleteAll(new File("addons")); + } + + private static void deleteAll(File file) throws IOException { + if (file.exists()) { + Files.walk(file.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + } + + /** + * Test method for {@link world.bentobox.level.Level#onEnable()}. + */ + @Test + public void testOnEnable() { + addon.onEnable(); + verify(plugin).logWarning("[Level] Level Addon: No such world in config.yml : acidisland_world"); + verify(plugin).log("[Level] Level hooking into BSkyBlock"); + verify(cmd, times(3)).getAddon(); // Three commands + verify(adminCmd, times(2)).getAddon(); // Two commands + // Placeholders + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock-island-level"), any()); + for (int i = 1; i < 11; i++) { + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock-island-level-top-name-" + i), any()); + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock-island-level-top-value-" + i), any()); + } + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_island_level"), any()); + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_visited_island_level"), any()); + for (int i = 1; i < 11; i++) { + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_top_name_" + i), any()); + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_top_value_" + i), any()); + } + // Commands + verify(am).registerListener(eq(addon), any(IslandTeamListeners.class)); + verify(am).registerListener(eq(addon), any(JoinLeaveListener.class)); + } + + /** + * Test method for {@link world.bentobox.level.Level#getIslandLevel(org.bukkit.World, java.util.UUID)}. + */ + @Test + public void testGetIslandLevelUnknown() { + addon.onEnable(); + assertEquals(0L, addon.getIslandLevel(world, UUID.randomUUID())); + } + + /** + * Test method for {@link world.bentobox.level.Level#getIslandLevel(org.bukkit.World, java.util.UUID)}. + */ + @Test + public void testGetIslandLevelNullTarget() { + addon.onEnable(); + assertEquals(0L, addon.getIslandLevel(world, UUID.randomUUID())); + + } + + /** + * Test method for {@link world.bentobox.level.Level#getLevelsData(java.util.UUID)}. + */ + @Test + public void testGetLevelsDataUnknown() { + addon.onEnable(); + assertNull(addon.getLevelsData(UUID.randomUUID())); + } + + /** + * Test method for {@link world.bentobox.level.Level#getSettings()}. + */ + @Test + public void testGetSettings() { + addon.onEnable(); + world.bentobox.level.config.Settings s = addon.getSettings(); + assertEquals(100, s.getDeathPenalty()); + } + + /** + * Test method for {@link world.bentobox.level.Level#getTopTen()}. + */ + @Test + public void testGetTopTen() { + addon.onEnable(); + assertNotNull(addon.getTopTen()); + } + + /** + * Test method for {@link world.bentobox.level.Level#getLevelPresenter()}. + */ + @Test + public void testGetLevelPresenter() { + addon.onEnable(); + assertNotNull(addon.getLevelPresenter()); + } + + /** + * Test method for {@link world.bentobox.level.Level#setIslandLevel(org.bukkit.World, java.util.UUID, long)}. + */ + @Test + public void testSetIslandLevel() { + addon.onEnable(); + addon.setIslandLevel(world, uuid, 345L); + assertEquals(345L, addon.getIslandLevel(world, uuid)); + verify(plugin, never()).logError(anyString()); + } + + /** + * Test method for {@link world.bentobox.level.Level#getInitialIslandLevel(world.bentobox.bentobox.database.objects.Island)}. + */ + @Test + public void testGetInitialIslandLevel() { + addon.onEnable(); + addon.setInitialIslandLevel(island, 40); + verify(plugin, never()).logError(anyString()); + assertEquals(40, addon.getInitialIslandLevel(island)); + + } + + /** + * Test method for {@link world.bentobox.level.Level#getHandler()}. + */ + @Test + public void testGetHandler() { + addon.onEnable(); + assertNotNull(addon.getHandler()); + } + + +}