Adds an admin stats command. See #293

This commit is contained in:
tastybento 2023-11-18 14:41:18 -08:00
parent eb71b35c5c
commit 3a3c8a320c
4 changed files with 878 additions and 737 deletions

View File

@ -24,6 +24,7 @@ import world.bentobox.level.calculators.Pipeliner;
import world.bentobox.level.commands.AdminLevelCommand; import world.bentobox.level.commands.AdminLevelCommand;
import world.bentobox.level.commands.AdminLevelStatusCommand; import world.bentobox.level.commands.AdminLevelStatusCommand;
import world.bentobox.level.commands.AdminSetInitialLevelCommand; import world.bentobox.level.commands.AdminSetInitialLevelCommand;
import world.bentobox.level.commands.AdminStatsCommand;
import world.bentobox.level.commands.AdminTopCommand; import world.bentobox.level.commands.AdminTopCommand;
import world.bentobox.level.commands.IslandLevelCommand; import world.bentobox.level.commands.IslandLevelCommand;
import world.bentobox.level.commands.IslandTopCommand; import world.bentobox.level.commands.IslandTopCommand;
@ -39,7 +40,6 @@ import world.bentobox.level.requests.TopTenRequestHandler;
import world.bentobox.visit.VisitAddon; import world.bentobox.visit.VisitAddon;
import world.bentobox.warps.Warp; import world.bentobox.warps.Warp;
/** /**
* @author tastybento * @author tastybento
* *
@ -71,7 +71,6 @@ public class Level extends Addon {
*/ */
private VisitAddon visitHook; private VisitAddon visitHook;
@Override @Override
public void onLoad() { public void onLoad() {
// Save the default config from config.yml // Save the default config from config.yml
@ -112,8 +111,7 @@ public class Level extends Addon {
// Register commands for GameModes // Register commands for GameModes
registeredGameModes.clear(); registeredGameModes.clear();
getPlugin().getAddonsManager().getGameModeAddons().stream() getPlugin().getAddonsManager().getGameModeAddons().stream()
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())) .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
.forEach(gm -> {
log("Level hooking into " + gm.getDescription().getName()); log("Level hooking into " + gm.getDescription().getName());
registerCommands(gm); registerCommands(gm);
new PlaceholderManager(this).registerPlaceholders(gm); new PlaceholderManager(this).registerPlaceholders(gm);
@ -142,7 +140,8 @@ public class Level extends Addon {
if (compareVersions(advChest.getDescription().getVersion(), "23.0") > 0) { if (compareVersions(advChest.getDescription().getVersion(), "23.0") > 0) {
log("Hooked into AdvancedChests."); log("Hooked into AdvancedChests.");
} else { } else {
logError("Could not hook into AdvancedChests " + advChest.getDescription().getVersion() + " - requires version 23.0 or later"); logError("Could not hook into AdvancedChests " + advChest.getDescription().getVersion()
+ " - requires version 23.0 or later");
advChestEnabled = false; advChestEnabled = false;
} }
} }
@ -166,40 +165,34 @@ public class Level extends Addon {
} }
@Override @Override
public void allLoaded() public void allLoaded() {
{
super.allLoaded(); super.allLoaded();
if (this.isEnabled()) if (this.isEnabled()) {
{
this.hookExtensions(); this.hookExtensions();
} }
} }
/** /**
* This method tries to hook into addons and plugins * This method tries to hook into addons and plugins
*/ */
private void hookExtensions() private void hookExtensions() {
{
// Try to find Visit addon and if it does not exist, display a warning // Try to find Visit addon and if it does not exist, display a warning
this.getAddonByName("Visit").ifPresentOrElse(addon -> this.getAddonByName("Visit").ifPresentOrElse(addon -> {
{
this.visitHook = (VisitAddon) addon; this.visitHook = (VisitAddon) addon;
this.log("Level Addon hooked into Visit addon."); this.log("Level Addon hooked into Visit addon.");
}, () -> this.visitHook = null); }, () -> this.visitHook = null);
// Try to find Warps addon and if it does not exist, display a warning // Try to find Warps addon and if it does not exist, display a warning
this.getAddonByName("Warps").ifPresentOrElse(addon -> this.getAddonByName("Warps").ifPresentOrElse(addon -> {
{
this.warpHook = (Warp) addon; this.warpHook = (Warp) addon;
this.log("Level Addon hooked into Warps addon."); this.log("Level Addon hooked into Warps addon.");
}, () -> this.warpHook = null); }, () -> this.warpHook = null);
} }
/** /**
* Compares versions * Compares versions
*
* @param version1 version 1 * @param version1 version 1
* @param version2 version 2 * @param version2 version 2
* @return {@code <0 if version 1 is older than version 2, =0 if the same, >0 if version 1 is newer than version 2} * @return {@code <0 if version 1 is older than version 2, =0 if the same, >0 if version 1 is newer than version 2}
@ -231,6 +224,7 @@ public class Level extends Addon {
if (getSettings().isZeroNewIslandLevels()) { if (getSettings().isZeroNewIslandLevels()) {
new AdminSetInitialLevelCommand(this, adminCommand); new AdminSetInitialLevelCommand(this, adminCommand);
} }
new AdminStatsCommand(this, adminCommand);
}); });
gm.getPlayerCommand().ifPresent(playerCmd -> { gm.getPlayerCommand().ifPresent(playerCmd -> {
new IslandLevelCommand(this, playerCmd); new IslandLevelCommand(this, playerCmd);
@ -263,7 +257,6 @@ public class Level extends Addon {
} }
/** /**
* @return the blockConfig * @return the blockConfig
*/ */
@ -294,6 +287,7 @@ public class Level extends Addon {
/** /**
* Set the config settings - used for tests only * Set the config settings - used for tests only
*
* @param configSettings - config settings * @param configSettings - config settings
*/ */
void setSettings(ConfigSettings configSettings) { void setSettings(ConfigSettings configSettings) {
@ -317,6 +311,7 @@ public class Level extends Addon {
/** /**
* Get level from cache for a player. * Get level from cache for a player.
*
* @param targetPlayer - target player UUID * @param targetPlayer - target player UUID
* @return Level of player or zero if player is unknown or UUID is null * @return Level of player or zero if player is unknown or UUID is null
*/ */
@ -326,6 +321,7 @@ public class Level extends Addon {
/** /**
* Sets the player's level to a value * Sets the player's level to a value
*
* @param world - world * @param world - world
* @param targetPlayer - target player * @param targetPlayer - target player
* @param level - level * @param level - level
@ -336,6 +332,7 @@ public class Level extends Addon {
/** /**
* Zeros the initial island level * Zeros the initial island level
*
* @param island - island * @param island - island
* @param level - initial calculated island level * @param level - initial calculated island level
*/ */
@ -345,6 +342,7 @@ public class Level extends Addon {
/** /**
* Get the initial island level * Get the initial island level
*
* @param island - island * @param island - island
* @return level or 0 by default * @return level or 0 by default
*/ */
@ -354,19 +352,23 @@ public class Level extends Addon {
/** /**
* Calculates a user's island * Calculates a user's island
*
* @param world - the world where this island is * @param world - the world where this island is
* @param user - not used! See depecration message * @param user - not used! See depecration message
* @param playerUUID - the target island member's UUID * @param playerUUID - the target island member's UUID
* @deprecated Do not use this anymore. Use getManager().calculateLevel(playerUUID, island) * @deprecated Do not use this anymore. Use
* getManager().calculateLevel(playerUUID, island)
*/ */
@Deprecated(since = "2.3.0", forRemoval = true) @Deprecated(since = "2.3.0", forRemoval = true)
public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) { public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) {
Island island = getIslands().getIsland(world, playerUUID); Island island = getIslands().getIsland(world, playerUUID);
if (island != null) getManager().calculateLevel(playerUUID, island); if (island != null)
getManager().calculateLevel(playerUUID, island);
} }
/** /**
* Provide the levels data for the target player * Provide the levels data for the target player
*
* @param targetPlayer - UUID of target player * @param targetPlayer - UUID of target player
* @return LevelsData object or null if not found. Only island levels are set! * @return LevelsData object or null if not found. Only island levels are set!
* @deprecated Do not use this anymore. Use {@link #getIslandLevel(World, UUID)} * @deprecated Do not use this anymore. Use {@link #getIslandLevel(World, UUID)}
@ -375,8 +377,7 @@ public class Level extends Addon {
public LevelsData getLevelsData(UUID targetPlayer) { public LevelsData getLevelsData(UUID targetPlayer) {
LevelsData ld = new LevelsData(targetPlayer); LevelsData ld = new LevelsData(targetPlayer);
getPlugin().getAddonsManager().getGameModeAddons().stream() getPlugin().getAddonsManager().getGameModeAddons().stream()
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())) .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
.forEach(gm -> {
if (getSettings().isZeroNewIslandLevels()) { if (getSettings().isZeroNewIslandLevels()) {
Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer); Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
if (island != null) { if (island != null) {
@ -397,6 +398,7 @@ public class Level extends Addon {
/** /**
* Check if Level addon is active in game mode * Check if Level addon is active in game mode
*
* @param gm Game Mode Addon * @param gm Game Mode Addon
* @return true if active, false if not * @return true if active, false if not
*/ */
@ -406,6 +408,7 @@ public class Level extends Addon {
/** /**
* Checks if Level addon is active in world * Checks if Level addon is active in world
*
* @param world world * @param world world
* @return true if active, false if not * @return true if active, false if not
*/ */
@ -432,8 +435,7 @@ public class Level extends Addon {
* *
* @return {@code Visit} of this object, {@code null} otherwise. * @return {@code Visit} of this object, {@code null} otherwise.
*/ */
public VisitAddon getVisitHook() public VisitAddon getVisitHook() {
{
return this.visitHook; return this.visitHook;
} }
@ -442,8 +444,7 @@ public class Level extends Addon {
* *
* @return {@code Warp} of this object, {@code null} otherwise. * @return {@code Warp} of this object, {@code null} otherwise.
*/ */
public Warp getWarpHook() public Warp getWarpHook() {
{
return this.warpHook; return this.warpHook;
} }
} }

View File

@ -31,7 +31,6 @@ import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.objects.TopTenData; import world.bentobox.level.objects.TopTenData;
public class LevelsManager { public class LevelsManager {
private static final String INTOPTEN = "intopten"; private static final String INTOPTEN = "intopten";
private static final TreeMap<BigInteger, String> LEVELS; private static final TreeMap<BigInteger, String> LEVELS;
@ -53,7 +52,6 @@ public class LevelsManager {
// Top ten lists // Top ten lists
private final Map<World, TopTenData> topTenLists; private final Map<World, TopTenData> topTenLists;
public LevelsManager(Level addon) { public LevelsManager(Level addon) {
this.addon = addon; this.addon = addon;
// Get the BentoBox database // Get the BentoBox database
@ -76,8 +74,7 @@ public class LevelsManager {
// World // World
.map(Bukkit::getWorld).filter(Objects::nonNull) .map(Bukkit::getWorld).filter(Objects::nonNull)
// Island // Island
.map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull) .map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull).forEach(i -> {
.forEach(i -> {
// Make new database entry // Make new database entry
World w = i.getWorld(); World w = i.getWorld();
IslandLevels il = new IslandLevels(i.getUniqueId()); IslandLevels il = new IslandLevels(i.getUniqueId());
@ -101,6 +98,7 @@ public class LevelsManager {
/** /**
* Add a score to the top players list * Add a score to the top players list
*
* @param world - world * @param world - world
* @param targetPlayer - target player * @param targetPlayer - target player
* @param lv - island level * @param lv - island level
@ -108,7 +106,8 @@ public class LevelsManager {
private void addToTopTen(@NonNull World world, @NonNull UUID targetPlayer, long lv) { private void addToTopTen(@NonNull World world, @NonNull UUID targetPlayer, long lv) {
// Get top ten // Get top ten
Map<UUID, Long> topTen = topTenLists.computeIfAbsent(world, k -> new TopTenData(world)).getTopTen(); Map<UUID, Long> topTen = topTenLists.computeIfAbsent(world, k -> new TopTenData(world)).getTopTen();
// Remove this player from the top list no matter what (we'll put them back later if required) // Remove this player from the top list no matter what (we'll put them back
// later if required)
topTen.remove(targetPlayer); topTen.remove(targetPlayer);
// Get the island // Get the island
@ -121,21 +120,24 @@ public class LevelsManager {
/** /**
* Add an island to a top ten * Add an island to a top ten
*
* @param island - island to add * @param island - island to add
* @param lv - level * @param lv - level
* @return true if successful, false if not added * @return true if successful, false if not added
*/ */
private boolean addToTopTen(Island island, long lv) { private boolean addToTopTen(Island island, long lv) {
if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) { if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) {
topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())) topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen()
.getTopTen().put(island.getOwner(), lv); .put(island.getOwner(), lv);
return true; return true;
} }
return false; return false;
} }
/** /**
* Calculate the island level, set all island member's levels to the result and try to add the owner to the top ten * Calculate the island level, set all island member's levels to the result and
* try to add the owner to the top ten
*
* @param targetPlayer - uuid of targeted player - owner or team member * @param targetPlayer - uuid of targeted player - owner or team member
* @param island - island to calculate * @param island - island to calculate
* @return completable future with the results of the calculation * @return completable future with the results of the calculation
@ -150,7 +152,8 @@ public class LevelsManager {
} }
// Add island to the pipeline // Add island to the pipeline
addon.getPipeliner().addIsland(island).thenAccept(r -> { addon.getPipeliner().addIsland(island).thenAccept(r -> {
// Results are irrelevant because the island is unowned or deleted, or IslandLevelCalcEvent is cancelled // Results are irrelevant because the island is unowned or deleted, or
// IslandLevelCalcEvent is cancelled
if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) { if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) {
result.complete(null); result.complete(null);
} }
@ -164,6 +167,7 @@ public class LevelsManager {
/** /**
* Fires the IslandLevelCalculatedEvent and returns true if it is canceled * Fires the IslandLevelCalculatedEvent and returns true if it is canceled
*
* @param targetPlayer - target player * @param targetPlayer - target player
* @param island - island * @param island - island
* @param results - results set * @param results - results set
@ -173,23 +177,28 @@ public class LevelsManager {
// Fire post calculation event // Fire post calculation event
IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results); IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results);
Bukkit.getPluginManager().callEvent(ilce); Bukkit.getPluginManager().callEvent(ilce);
if (ilce.isCancelled()) return true; if (ilce.isCancelled())
return true;
// Set the values if they were altered // Set the values if they were altered
results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel())); results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel()));
results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel())); results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel()));
results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap())); results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap()));
results.setPointsToNextLevel((Long)ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel())); results.setPointsToNextLevel(
(Long) ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel()));
results.setTotalPoints((Long) ilce.getKeyValues().getOrDefault("totalPoints", results.getTotalPoints())); results.setTotalPoints((Long) ilce.getKeyValues().getOrDefault("totalPoints", results.getTotalPoints()));
return ((Boolean) ilce.getKeyValues().getOrDefault("isCancelled", false)); return ((Boolean) ilce.getKeyValues().getOrDefault("isCancelled", false));
} }
/** /**
* 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 * @param lvl - long value to represent
* @return string of the level. * @return string of the level.
*/ */
public String formatLevel(@Nullable Long lvl) { public String formatLevel(@Nullable Long lvl) {
if (lvl == null) return ""; if (lvl == null)
return "";
String level = String.valueOf(lvl); String level = String.valueOf(lvl);
// Asking for the level of another player // Asking for the level of another player
if (addon.getSettings().isShorthand()) { if (addon.getSettings().isShorthand()) {
@ -202,7 +211,8 @@ public class LevelsManager {
// 1 527 314 -> 1.5M // 1 527 314 -> 1.5M
// 3 874 130 021 -> 3.8G // 3 874 130 021 -> 3.8G
// 4 002 317 889 -> 4.0T // 4 002 317 889 -> 4.0T
level = new DecimalFormat("#.#").format(levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue()/1000.0) + stage.getValue(); level = new DecimalFormat("#.#").format(
levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue() / 1000.0) + stage.getValue();
} }
} }
return level; return level;
@ -210,6 +220,7 @@ public class LevelsManager {
/** /**
* Get the initial level of the island. Used to zero island levels * Get the initial level of the island. Used to zero island levels
*
* @param island - island * @param island - island
* @return initial level of island * @return initial level of island
*/ */
@ -219,12 +230,15 @@ public class LevelsManager {
/** /**
* Get level of island from cache for a player. * Get level of island from cache for a player.
*
* @param world - world where the island is * @param world - world where the island is
* @param targetPlayer - target player UUID * @param targetPlayer - target player UUID
* @return Level of the player's island or zero if player is unknown or UUID is null * @return Level of the player's island or zero if player is unknown or UUID is
* null
*/ */
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) return 0L; if (targetPlayer == null)
return 0L;
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? 0L : getLevelsData(island).getLevel(); return island == null ? 0L : getLevelsData(island).getLevel();
@ -232,12 +246,15 @@ public class LevelsManager {
/** /**
* Get the maximum level ever given to this island * Get the maximum level ever given to this island
*
* @param world - world where the island is * @param world - world where the island is
* @param targetPlayer - target player UUID * @param targetPlayer - target player UUID
* @return Max level of the player's island or zero if player is unknown or UUID is null * @return Max level of the player's island or zero if player is unknown or UUID
* is null
*/ */
public long getIslandMaxLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandMaxLevel(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) return 0L; if (targetPlayer == null)
return 0L;
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? 0L : getLevelsData(island).getMaxLevel(); return island == null ? 0L : getLevelsData(island).getMaxLevel();
@ -245,9 +262,11 @@ public class LevelsManager {
/** /**
* Returns a formatted string of the target player's island level * Returns a formatted string of the target player's island level
*
* @param world - world where the island is * @param world - world where the island is
* @param targetPlayer - target player's UUID * @param targetPlayer - target player's UUID
* @return Formatted level of player or zero if player is unknown or UUID is null * @return Formatted level of player or zero if player is unknown or UUID is
* null
*/ */
public String getIslandLevelString(@NonNull World world, @Nullable UUID targetPlayer) { public String getIslandLevelString(@NonNull World world, @Nullable UUID targetPlayer) {
return formatLevel(getIslandLevel(world, targetPlayer)); return formatLevel(getIslandLevel(world, targetPlayer));
@ -255,6 +274,7 @@ public class LevelsManager {
/** /**
* Load a level data for the island from the cache or database. * Load a level data for the island from the cache or database.
*
* @param island - UUID of island * @param island - UUID of island
* @return IslandLevels object * @return IslandLevels object
*/ */
@ -281,19 +301,24 @@ public class LevelsManager {
} }
/** /**
* Get the number of points required until the next level since the last level calc * Get the number of points required until the next level since the last level
* calc
*
* @param world - world where the island is * @param world - world where the island is
* @param targetPlayer - target player UUID * @param targetPlayer - target player UUID
* @return string with the number required or blank if the player is unknown * @return string with the number required or blank if the player is unknown
*/ */
public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) { public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) return ""; if (targetPlayer == null)
return "";
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? "" : String.valueOf(getLevelsData(island).getPointsToNextLevel()); return island == null ? "" : String.valueOf(getLevelsData(island).getPointsToNextLevel());
} }
/** /**
* Get the top ten for this world. Returns offline players or players with the intopten permission. * Get the top ten for this world. Returns offline players or players with the
* intopten permission.
*
* @param world - world requested * @param world - world requested
* @param size - size of the top ten * @param size - size of the top ten
* @return sorted top ten map * @return sorted top ten map
@ -303,11 +328,9 @@ public class LevelsManager {
createAndCleanRankings(world); createAndCleanRankings(world);
// Return the sorted map // Return the sorted map
return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream() return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
.filter(e -> addon.getIslands().isOwner(world, e.getKey())) .filter(e -> addon.getIslands().hasIsland(world, e.getKey())).filter(l -> l.getValue() > 0)
.filter(l -> l.getValue() > 0)
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(size) .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(size)
.collect(Collectors.toMap( .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)));
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)));
} }
void createAndCleanRankings(@NonNull World world) { void createAndCleanRankings(@NonNull World world) {
@ -319,12 +342,13 @@ public class LevelsManager {
/** /**
* @return the topTenLists * @return the topTenLists
*/ */
protected Map<World, TopTenData> getTopTenLists() { public Map<World, TopTenData> getTopTenLists() {
return topTenLists; return topTenLists;
} }
/** /**
* Get the rank of the player in the rankings * Get the rank of the player in the rankings
*
* @param world - world * @param world - world
* @param uuid - player UUID * @param uuid - player UUID
* @return rank placing - note - placing of 1 means top ranked * @return rank placing - note - placing of 1 means top ranked
@ -332,21 +356,22 @@ public class LevelsManager {
public int getRank(@NonNull World world, UUID uuid) { public int getRank(@NonNull World world, UUID uuid) {
createAndCleanRankings(world); createAndCleanRankings(world);
Stream<Entry<UUID, Long>> stream = topTenLists.get(world).getTopTen().entrySet().stream() Stream<Entry<UUID, Long>> stream = topTenLists.get(world).getTopTen().entrySet().stream()
.filter(e -> addon.getIslands().isOwner(world, e.getKey())) .filter(e -> addon.getIslands().isOwner(world, e.getKey())).filter(l -> l.getValue() > 0)
.filter(l -> l.getValue() > 0)
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue())); .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
return (int) (stream.takeWhile(x -> !x.getKey().equals(uuid)).map(Map.Entry::getKey).count() + 1); return (int) (stream.takeWhile(x -> !x.getKey().equals(uuid)).map(Map.Entry::getKey).count() + 1);
} }
/** /**
* Checks if player has the correct top ten perm to have their level saved * Checks if player has the correct top ten perm to have their level saved
*
* @param world * @param world
* @param targetPlayer * @param targetPlayer
* @return true if player has the perm or the player is offline * @return true if player has the perm or the player is offline
*/ */
boolean hasTopTenPerm(@NonNull World world, @NonNull UUID targetPlayer) { boolean hasTopTenPerm(@NonNull World world, @NonNull UUID targetPlayer) {
String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world); String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world);
return Bukkit.getPlayer(targetPlayer) == null || Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN); return Bukkit.getPlayer(targetPlayer) == null
|| Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN);
} }
/** /**
@ -358,7 +383,8 @@ public class LevelsManager {
addon.log("Generating rankings"); addon.log("Generating rankings");
handler.loadObjects().forEach(il -> { handler.loadObjects().forEach(il -> {
if (il.getLevel() > 0) { if (il.getLevel() > 0) {
addon.getIslands().getIslandById(il.getUniqueId()).ifPresent(i -> this.addToTopTen(i, il.getLevel())); addon.getIslands().getIslandById(il.getUniqueId())
.ifPresent(i -> this.addToTopTen(i, il.getLevel()));
} }
}); });
topTenLists.keySet().forEach(w -> addon.log("Generated rankings for " + w.getName())); topTenLists.keySet().forEach(w -> addon.log("Generated rankings for " + w.getName()));
@ -366,7 +392,9 @@ public class LevelsManager {
} }
/** /**
* Removes a player from a world's top ten and removes world from player's level data * Removes a player from a world's top ten and removes world from player's level
* data
*
* @param world - world * @param world - world
* @param uuid - the player's uuid * @param uuid - the player's uuid
*/ */
@ -379,17 +407,21 @@ public class LevelsManager {
/** /**
* Set an initial island level * Set an initial island level
*
* @param island - the island to set. Must have a non-null world * @param island - the island to set. Must have a non-null world
* @param lv - initial island level * @param lv - initial island level
*/ */
public void setInitialIslandLevel(@NonNull Island island, long lv) { public void setInitialIslandLevel(@NonNull Island island, long lv) {
if (island.getWorld() == null) return; if (island.getWorld() == null)
return;
levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv); levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
handler.saveObjectAsync(levelsCache.get(island.getUniqueId())); handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
} }
/** /**
* Set the island level for the owner of the island that targetPlayer is a member * Set the island level for the owner of the island that targetPlayer is a
* member
*
* @param world - world * @param world - world
* @param targetPlayer - player, may be a team member * @param targetPlayer - player, may be a team member
* @param lv - level * @param lv - level
@ -414,7 +446,9 @@ public class LevelsManager {
} }
/** /**
* Set the island level for the owner of the island that targetPlayer is a member * Set the island level for the owner of the island that targetPlayer is a
* member
*
* @param world - world * @param world - world
* @param owner - owner of the island * @param owner - owner of the island
* @param r - results of the calculation * @param r - results of the calculation
@ -422,7 +456,8 @@ public class LevelsManager {
private void setIslandResults(World world, @NonNull UUID owner, Results r) { private void setIslandResults(World world, @NonNull UUID owner, Results r) {
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, owner); Island island = addon.getIslands().getIsland(world, owner);
if (island == null) return; if (island == null)
return;
IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new); IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new);
ld.setLevel(r.getLevel()); ld.setLevel(r.getLevel());
ld.setUwCount(Maps.asMap(r.getUwCount().elementSet(), elem -> r.getUwCount().count(elem))); ld.setUwCount(Maps.asMap(r.getUwCount().elementSet(), elem -> r.getUwCount().count(elem)));
@ -437,6 +472,7 @@ public class LevelsManager {
/** /**
* Removes island from cache when it is deleted * Removes island from cache when it is deleted
*
* @param uniqueId - id of island * @param uniqueId - id of island
*/ */
public void deleteIsland(String uniqueId) { public void deleteIsland(String uniqueId) {

View File

@ -0,0 +1,94 @@
package world.bentobox.level.commands;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.World;
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;
import world.bentobox.level.objects.TopTenData;
public class AdminStatsCommand extends CompositeCommand {
private final Level level;
public AdminStatsCommand(Level addon, CompositeCommand parent) {
super(parent, "stats");
this.level = addon;
new AdminTopRemoveCommand(addon, this);
}
@Override
public void setup() {
this.setPermission("admin.stats");
this.setOnlyPlayer(false);
this.setDescription("admin.stats.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
user.sendMessage("admin.stats.title");
for (Entry<World, TopTenData> en : level.getManager().getTopTenLists().entrySet()) {
user.sendMessage("admin.stats.world", TextVariables.NAME,
level.getPlugin().getIWM().getWorldName(en.getKey()));
Map<UUID, Long> topTen = en.getValue().getTopTen();
if (topTen.isEmpty()) {
user.sendMessage("admin.stats.no-data");
return false;
}
// Calculating basic statistics
long sum = 0, max = Long.MIN_VALUE, min = Long.MAX_VALUE;
Map<Long, Integer> levelFrequency = new HashMap<>();
for (Long level : topTen.values()) {
sum += level;
max = Math.max(max, level);
min = Math.min(min, level);
levelFrequency.merge(level, 1, Integer::sum);
}
double average = sum / (double) topTen.size();
List<Long> sortedLevels = topTen.values().stream().sorted().collect(Collectors.toList());
long median = sortedLevels.get(sortedLevels.size() / 2);
Long mode = Collections.max(levelFrequency.entrySet(), Map.Entry.comparingByValue()).getKey();
// Logging basic statistics
user.sendMessage("admin.stats.average-level", TextVariables.NUMBER, String.valueOf(average));
user.sendMessage("admin.stats.median-level", TextVariables.NUMBER, String.valueOf(median));
user.sendMessage("admin.stats.mode-level", TextVariables.NUMBER, String.valueOf(mode));
user.sendMessage("admin.stats.highest-level", TextVariables.NUMBER, String.valueOf(max));
user.sendMessage("admin.stats.lowest-level", TextVariables.NUMBER, String.valueOf(min));
// Grouping data for distribution analysis
Map<String, Integer> rangeMap = new TreeMap<>();
for (Long level : topTen.values()) {
String range = getRange(level);
rangeMap.merge(range, 1, Integer::sum);
}
// Logging distribution
user.sendMessage("admin.stats.distribution");
for (Map.Entry<String, Integer> entry : rangeMap.entrySet()) {
user.sendMessage(
entry.getKey() + ": " + entry.getValue() + " " + user.getTranslation("admin.stats.islands"));
}
}
return true;
}
private static String getRange(long level) {
long rangeStart = level / 100 * 100;
long rangeEnd = rangeStart + 99;
return rangeStart + "-" + rangeEnd;
}
}

View File

@ -22,8 +22,18 @@ admin:
remove: remove:
description: "remove player from Top Ten" description: "remove player from Top Ten"
parameters: "<player>" parameters: "<player>"
stats:
description: "show stats on islands on this server"
title: "Server Island Stats"
world: "&a [name]"
no-data: "&c No data to process."
average-level: "Average Island Level: [number]"
median-level: "Median Island Level: [number]"
mode-level: "Mode Island Level: [number]"
highest-level: "Highest Island Level: [number]"
lowest-level: "Lowest Island Level: [number]"
distribution: "Island Level Distribution:"
islands: "islands"
island: island:
level: level:
parameters: "[player]" parameters: "[player]"