diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c771fd5..825b18d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,21 +11,22 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
+ distribution: 'adopt'
java-version: 17
- name: Cache SonarCloud packages
- uses: actions/cache@v1
+ uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
- uses: actions/cache@v1
+ uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
diff --git a/pom.xml b/pom.xml
index d204ec7..881d55e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,12 +58,12 @@
2.0.9
- 1.19.4-R0.1-SNAPSHOT
- 1.23.0
+ 1.20.4-R0.1-SNAPSHOT
+ 2.0.0-SNAPSHOT
1.12.0
- 1.4.0
+ 1.6.0
1.1.0
@@ -71,7 +71,7 @@
-LOCAL
- 2.11.0
+ 2.12.0
BentoBoxWorld_Level
bentobox-world
https://sonarcloud.io
@@ -237,7 +237,7 @@
com.songoda
UltimateStacker
- 2.3.3
+ 2.4.0
provided
@@ -403,13 +403,15 @@
org.jacoco
jacoco-maven-plugin
- 0.8.7
+ 0.8.10
true
**/*Names*
+
+ org/bukkit/Material*
diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java
index 0d02802..e13a70d 100644
--- a/src/main/java/world/bentobox/level/Level.java
+++ b/src/main/java/world/bentobox/level/Level.java
@@ -24,6 +24,7 @@
import world.bentobox.level.commands.AdminLevelCommand;
import world.bentobox.level.commands.AdminLevelStatusCommand;
import world.bentobox.level.commands.AdminSetInitialLevelCommand;
+import world.bentobox.level.commands.AdminStatsCommand;
import world.bentobox.level.commands.AdminTopCommand;
import world.bentobox.level.commands.IslandLevelCommand;
import world.bentobox.level.commands.IslandTopCommand;
@@ -39,401 +40,412 @@
import world.bentobox.visit.VisitAddon;
import world.bentobox.warps.Warp;
-
/**
* @author tastybento
*
*/
public class Level extends Addon {
- // The 10 in top ten
- public static final int TEN = 10;
-
- // Settings
- private ConfigSettings settings;
- private Config configObject = new Config<>(this, ConfigSettings.class);
- private BlockConfig blockConfig;
- private Pipeliner pipeliner;
- private LevelsManager manager;
- private boolean stackersEnabled;
- private boolean advChestEnabled;
- private boolean roseStackersEnabled;
- private boolean ultimateStackerEnabled;
- private final List registeredGameModes = new ArrayList<>();
-
- /**
- * Local variable that stores if warpHook is present.
- */
- private Warp warpHook;
-
- /**
- * Local variable that stores if visitHook is present.
- */
- private VisitAddon visitHook;
-
-
- @Override
- public void onLoad() {
- // Save the default config from config.yml
- saveDefaultConfig();
- if (loadSettings()) {
- // Disable
- logError("Level settings could not load! Addon disabled.");
- setState(State.DISABLED);
- } else {
- configObject.saveConfigObject(settings);
- }
-
- // Save existing panels.
- this.saveResource("panels/top_panel.yml", false);
- this.saveResource("panels/detail_panel.yml", false);
- this.saveResource("panels/value_panel.yml", false);
- }
-
- private boolean loadSettings() {
- // Load settings again to get worlds
- settings = configObject.loadConfigObject();
-
- return settings == null;
- }
-
- @Override
- public void onEnable() {
- loadBlockSettings();
- // Start pipeline
- pipeliner = new Pipeliner(this);
- // Start Manager
- manager = new LevelsManager(this);
- // Register listeners
- this.registerListener(new IslandActivitiesListeners(this));
- this.registerListener(new JoinLeaveListener(this));
- this.registerListener(new MigrationListener(this));
-
- // Register commands for GameModes
- registeredGameModes.clear();
- getPlugin().getAddonsManager().getGameModeAddons().stream()
- .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName()))
- .forEach(gm -> {
- log("Level hooking into " + gm.getDescription().getName());
- registerCommands(gm);
- new PlaceholderManager(this).registerPlaceholders(gm);
- registeredGameModes.add(gm);
- });
- // Register request handlers
- registerRequestHandler(new LevelRequestHandler(this));
- registerRequestHandler(new TopTenRequestHandler(this));
-
- // Check if WildStackers is enabled on the server
- // I only added support for counting blocks into the island level
- // Someone else can PR if they want spawners added to the Leveling system :)
- stackersEnabled = Bukkit.getPluginManager().isPluginEnabled("WildStacker");
- if (stackersEnabled) {
- log("Hooked into WildStackers.");
- }
- // Check if AdvancedChests is enabled on the server
- Plugin advChest = Bukkit.getPluginManager().getPlugin("AdvancedChests");
- advChestEnabled = advChest != null;
- if (advChestEnabled) {
- // Check version
- if (compareVersions(advChest.getDescription().getVersion(), "23.0") > 0) {
- log("Hooked into AdvancedChests.");
- } else {
- logError("Could not hook into AdvancedChests " + advChest.getDescription().getVersion() + " - requires version 23.0 or later");
- advChestEnabled = false;
- }
- }
- // Check if RoseStackers is enabled
- roseStackersEnabled = Bukkit.getPluginManager().isPluginEnabled("RoseStacker");
- if (roseStackersEnabled) {
- log("Hooked into RoseStackers.");
- }
-
- // Check if UltimateStacker is enabled
- ultimateStackerEnabled = Bukkit.getPluginManager().isPluginEnabled("UltimateStacker");
- if (ultimateStackerEnabled) {
- log("Hooked into UltimateStacker.");
- }
- }
-
- @Override
- public void allLoaded()
- {
- super.allLoaded();
-
- if (this.isEnabled())
- {
- this.hookExtensions();
- }
- }
-
-
- /**
- * This method tries to hook into addons and plugins
- */
- private void hookExtensions()
- {
- // Try to find Visit addon and if it does not exist, display a warning
- this.getAddonByName("Visit").ifPresentOrElse(addon ->
- {
- this.visitHook = (VisitAddon) addon;
- this.log("Level Addon hooked into Visit addon.");
- }, () -> this.visitHook = null);
-
- // Try to find Warps addon and if it does not exist, display a warning
- this.getAddonByName("Warps").ifPresentOrElse(addon ->
- {
- this.warpHook = (Warp) addon;
- this.log("Level Addon hooked into Warps addon.");
- }, () -> this.warpHook = null);
- }
-
-
- /**
- * Compares versions
- * @param version1 version 1
- * @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}
- */
- public static int compareVersions(String version1, String version2) {
- int comparisonResult = 0;
-
- String[] version1Splits = version1.split("\\.");
- String[] version2Splits = version2.split("\\.");
- int maxLengthOfVersionSplits = Math.max(version1Splits.length, version2Splits.length);
-
- for (int i = 0; i < maxLengthOfVersionSplits; i++){
- Integer v1 = i < version1Splits.length ? Integer.parseInt(version1Splits[i]) : 0;
- Integer v2 = i < version2Splits.length ? Integer.parseInt(version2Splits[i]) : 0;
- int compare = v1.compareTo(v2);
- if (compare != 0) {
- comparisonResult = compare;
- break;
- }
- }
- return comparisonResult;
- }
-
- private void registerCommands(GameModeAddon gm) {
- gm.getAdminCommand().ifPresent(adminCommand -> {
- new AdminLevelCommand(this, adminCommand);
- new AdminTopCommand(this, adminCommand);
- new AdminLevelStatusCommand(this, adminCommand);
- if (getSettings().isZeroNewIslandLevels()) {
- new AdminSetInitialLevelCommand(this, adminCommand);
- }
- });
- gm.getPlayerCommand().ifPresent(playerCmd -> {
- new IslandLevelCommand(this, playerCmd);
- new IslandTopCommand(this, playerCmd);
- new IslandValueCommand(this, playerCmd);
- });
- }
-
- @Override
- public void onDisable() {
- // Stop the pipeline
- this.getPipeliner().stop();
- }
-
- private void loadBlockSettings() {
- // Save the default blockconfig.yml
- this.saveResource("blockconfig.yml", false);
-
- YamlConfiguration blockValues = new YamlConfiguration();
- try {
- File file = new File(this.getDataFolder(), "blockconfig.yml");
- blockValues.load(file);
- // Load the block config class
- blockConfig = new BlockConfig(this, blockValues, file);
- } catch (IOException | InvalidConfigurationException e) {
- // Disable
- logError("Level blockconfig.yml settings could not load! Addon disabled.");
- setState(State.DISABLED);
- }
-
- }
-
-
- /**
- * @return the blockConfig
- */
- public BlockConfig getBlockConfig() {
- return blockConfig;
- }
-
- /**
- * @return the settings
- */
- public ConfigSettings getSettings() {
- return settings;
- }
-
- /**
- * @return the pipeliner
- */
- public Pipeliner getPipeliner() {
- return pipeliner;
- }
-
- /**
- * @return the manager
- */
- public LevelsManager getManager() {
- return manager;
- }
-
- /**
- * Set the config settings - used for tests only
- * @param configSettings - config settings
- */
- void setSettings(ConfigSettings configSettings) {
- this.settings = configSettings;
-
- }
-
- /**
- * @return the stackersEnabled
- */
- public boolean isStackersEnabled() {
- return stackersEnabled;
- }
-
- /**
- * @return the advChestEnabled
- */
- public boolean isAdvChestEnabled() {
- return advChestEnabled;
- }
-
- /**
- * 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, @Nullable UUID targetPlayer) {
- return getManager().getIslandLevel(world, targetPlayer);
- }
-
- /**
- * Sets the player's level to a value
- * @param world - world
- * @param targetPlayer - target player
- * @param level - level
- */
- public void setIslandLevel(World world, UUID targetPlayer, long level) {
- getManager().setIslandLevel(world, targetPlayer, level);
- }
-
- /**
- * Zeros the initial island level
- * @param island - island
- * @param level - initial calculated island level
- */
- public void setInitialIslandLevel(@NonNull Island island, long level) {
- getManager().setInitialIslandLevel(island, level);
- }
-
- /**
- * Get the initial island level
- * @param island - island
- * @return level or 0 by default
- */
- public long getInitialIslandLevel(@NonNull Island island) {
- return getManager().getInitialLevel(island);
- }
-
- /**
- * Calculates a user's island
- * @param world - the world where this island is
- * @param user - not used! See depecration message
- * @param playerUUID - the target island member's UUID
- * @deprecated Do not use this anymore. Use getManager().calculateLevel(playerUUID, island)
- */
- @Deprecated(since="2.3.0", forRemoval=true)
- public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) {
- Island island = getIslands().getIsland(world, playerUUID);
- if (island != null) getManager().calculateLevel(playerUUID, island);
- }
-
- /**
- * Provide the levels data for the target player
- * @param targetPlayer - UUID of target player
- * @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(since="2.3.0", forRemoval=true)
- public LevelsData getLevelsData(UUID targetPlayer) {
- LevelsData ld = new LevelsData(targetPlayer);
- getPlugin().getAddonsManager().getGameModeAddons().stream()
- .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName()))
- .forEach(gm -> {
- if (getSettings().isZeroNewIslandLevels()) {
- Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
- if (island != null) {
- ld.setInitialLevel(gm.getOverWorld(), this.getInitialIslandLevel(island));
- }
- }
- ld.setLevel(gm.getOverWorld(), this.getIslandLevel(gm.getOverWorld(), targetPlayer));
- });
- return ld;
- }
-
- /**
- * @return the registeredGameModes
- */
- public List getRegisteredGameModes() {
- return registeredGameModes;
- }
-
- /**
- * Check if Level addon is active in game mode
- * @param gm Game Mode Addon
- * @return true if active, false if not
- */
- public boolean isRegisteredGameMode(GameModeAddon gm) {
- return registeredGameModes.contains(gm);
- }
-
- /**
- * Checks if Level addon is active in world
- * @param world world
- * @return true if active, false if not
- */
- public boolean isRegisteredGameModeWorld(World world) {
- return registeredGameModes.stream().map(GameModeAddon::getOverWorld).anyMatch(w -> Util.sameWorld(world, w));
- }
-
- /**
- * @return the roseStackersEnabled
- */
- public boolean isRoseStackersEnabled() {
- return roseStackersEnabled;
- }
-
- /**
- * @return the ultimateStackerEnabled
- */
- public boolean isUltimateStackerEnabled() {
- return ultimateStackerEnabled;
- }
-
- /**
- * Method Level#getVisitHook returns the visitHook of this object.
- *
- * @return {@code Visit} of this object, {@code null} otherwise.
- */
- public VisitAddon getVisitHook()
- {
- return this.visitHook;
- }
-
- /**
- * Method Level#getWarpHook returns the warpHook of this object.
- *
- * @return {@code Warp} of this object, {@code null} otherwise.
- */
- public Warp getWarpHook()
- {
- return this.warpHook;
- }
+ // The 10 in top ten
+ public static final int TEN = 10;
+
+ // Settings
+ private ConfigSettings settings;
+ private Config configObject = new Config<>(this, ConfigSettings.class);
+ private BlockConfig blockConfig;
+ private Pipeliner pipeliner;
+ private LevelsManager manager;
+ private boolean stackersEnabled;
+ private boolean advChestEnabled;
+ private boolean roseStackersEnabled;
+ private boolean ultimateStackerEnabled;
+ private final List registeredGameModes = new ArrayList<>();
+
+ /**
+ * Local variable that stores if warpHook is present.
+ */
+ private Warp warpHook;
+
+ /**
+ * Local variable that stores if visitHook is present.
+ */
+ private VisitAddon visitHook;
+
+ @Override
+ public void onLoad() {
+ // Save the default config from config.yml
+ saveDefaultConfig();
+ if (loadSettings()) {
+ // Disable
+ logError("Level settings could not load! Addon disabled.");
+ setState(State.DISABLED);
+ } else {
+ configObject.saveConfigObject(settings);
+ }
+
+ // Save existing panels.
+ this.saveResource("panels/top_panel.yml", false);
+ this.saveResource("panels/detail_panel.yml", false);
+ this.saveResource("panels/value_panel.yml", false);
+ }
+
+ private boolean loadSettings() {
+ // Load settings again to get worlds
+ settings = configObject.loadConfigObject();
+
+ return settings == null;
+ }
+
+ @Override
+ public void onEnable() {
+ loadBlockSettings();
+ // Start pipeline
+ pipeliner = new Pipeliner(this);
+ // Start Manager
+ manager = new LevelsManager(this);
+ // Register listeners
+ this.registerListener(new IslandActivitiesListeners(this));
+ this.registerListener(new JoinLeaveListener(this));
+ this.registerListener(new MigrationListener(this));
+
+ // Register commands for GameModes
+ registeredGameModes.clear();
+ getPlugin().getAddonsManager().getGameModeAddons().stream()
+ .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
+ log("Level hooking into " + gm.getDescription().getName());
+ registerCommands(gm);
+ new PlaceholderManager(this).registerPlaceholders(gm);
+ registeredGameModes.add(gm);
+ });
+ // Register request handlers
+ registerRequestHandler(new LevelRequestHandler(this));
+ registerRequestHandler(new TopTenRequestHandler(this));
+
+ // Check if WildStackers is enabled on the server
+ // I only added support for counting blocks into the island level
+ // Someone else can PR if they want spawners added to the Leveling system :)
+ if (!settings.getDisabledPluginHooks().contains("WildStacker")) {
+ stackersEnabled = Bukkit.getPluginManager().isPluginEnabled("WildStacker");
+ if (stackersEnabled) {
+ log("Hooked into WildStackers.");
+ }
+ }
+
+ // Check if AdvancedChests is enabled on the server
+ if (!settings.getDisabledPluginHooks().contains("AdvancedChests")) {
+ Plugin advChest = Bukkit.getPluginManager().getPlugin("AdvancedChests");
+ advChestEnabled = advChest != null;
+ if (advChestEnabled) {
+ // Check version
+ if (compareVersions(advChest.getDescription().getVersion(), "23.0") > 0) {
+ log("Hooked into AdvancedChests.");
+ } else {
+ logError("Could not hook into AdvancedChests " + advChest.getDescription().getVersion()
+ + " - requires version 23.0 or later");
+ advChestEnabled = false;
+ }
+ }
+ }
+
+ // Check if RoseStackers is enabled
+ if (!settings.getDisabledPluginHooks().contains("RoseStacker")) {
+ roseStackersEnabled = Bukkit.getPluginManager().isPluginEnabled("RoseStacker");
+ if (roseStackersEnabled) {
+ log("Hooked into RoseStackers.");
+ }
+ }
+
+ // Check if UltimateStacker is enabled
+ if (!settings.getDisabledPluginHooks().contains("UltimateStacker")) {
+ ultimateStackerEnabled = Bukkit.getPluginManager().isPluginEnabled("UltimateStacker");
+ if (ultimateStackerEnabled) {
+ log("Hooked into UltimateStacker.");
+ }
+ }
+ }
+
+ @Override
+ public void allLoaded() {
+ super.allLoaded();
+
+ if (this.isEnabled()) {
+ this.hookExtensions();
+ }
+ }
+
+ /**
+ * This method tries to hook into addons and plugins
+ */
+ private void hookExtensions() {
+ // Try to find Visit addon and if it does not exist, display a warning
+ this.getAddonByName("Visit").ifPresentOrElse(addon -> {
+ this.visitHook = (VisitAddon) addon;
+ this.log("Level Addon hooked into Visit addon.");
+ }, () -> this.visitHook = null);
+
+ // Try to find Warps addon and if it does not exist, display a warning
+ this.getAddonByName("Warps").ifPresentOrElse(addon -> {
+ this.warpHook = (Warp) addon;
+ this.log("Level Addon hooked into Warps addon.");
+ }, () -> this.warpHook = null);
+ }
+
+ /**
+ * Compares versions
+ *
+ * @param version1 version 1
+ * @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}
+ */
+ public static int compareVersions(String version1, String version2) {
+ int comparisonResult = 0;
+
+ String[] version1Splits = version1.split("\\.");
+ String[] version2Splits = version2.split("\\.");
+ int maxLengthOfVersionSplits = Math.max(version1Splits.length, version2Splits.length);
+
+ for (int i = 0; i < maxLengthOfVersionSplits; i++) {
+ Integer v1 = i < version1Splits.length ? Integer.parseInt(version1Splits[i]) : 0;
+ Integer v2 = i < version2Splits.length ? Integer.parseInt(version2Splits[i]) : 0;
+ int compare = v1.compareTo(v2);
+ if (compare != 0) {
+ comparisonResult = compare;
+ break;
+ }
+ }
+ return comparisonResult;
+ }
+
+ private void registerCommands(GameModeAddon gm) {
+ gm.getAdminCommand().ifPresent(adminCommand -> {
+ new AdminLevelCommand(this, adminCommand);
+ new AdminTopCommand(this, adminCommand);
+ new AdminLevelStatusCommand(this, adminCommand);
+ if (getSettings().isZeroNewIslandLevels()) {
+ new AdminSetInitialLevelCommand(this, adminCommand);
+ }
+ new AdminStatsCommand(this, adminCommand);
+ });
+ gm.getPlayerCommand().ifPresent(playerCmd -> {
+ new IslandLevelCommand(this, playerCmd);
+ new IslandTopCommand(this, playerCmd);
+ new IslandValueCommand(this, playerCmd);
+ });
+ }
+
+ @Override
+ public void onDisable() {
+ // Stop the pipeline
+ this.getPipeliner().stop();
+ }
+
+ private void loadBlockSettings() {
+ // Save the default blockconfig.yml
+ this.saveResource("blockconfig.yml", false);
+
+ YamlConfiguration blockValues = new YamlConfiguration();
+ try {
+ File file = new File(this.getDataFolder(), "blockconfig.yml");
+ blockValues.load(file);
+ // Load the block config class
+ blockConfig = new BlockConfig(this, blockValues, file);
+ } catch (IOException | InvalidConfigurationException e) {
+ // Disable
+ logError("Level blockconfig.yml settings could not load! Addon disabled.");
+ setState(State.DISABLED);
+ }
+
+ }
+
+ /**
+ * @return the blockConfig
+ */
+ public BlockConfig getBlockConfig() {
+ return blockConfig;
+ }
+
+ /**
+ * @return the settings
+ */
+ public ConfigSettings getSettings() {
+ return settings;
+ }
+
+ /**
+ * @return the pipeliner
+ */
+ public Pipeliner getPipeliner() {
+ return pipeliner;
+ }
+
+ /**
+ * @return the manager
+ */
+ public LevelsManager getManager() {
+ return manager;
+ }
+
+ /**
+ * Set the config settings - used for tests only
+ *
+ * @param configSettings - config settings
+ */
+ void setSettings(ConfigSettings configSettings) {
+ this.settings = configSettings;
+
+ }
+
+ /**
+ * @return the stackersEnabled
+ */
+ public boolean isStackersEnabled() {
+ return stackersEnabled;
+ }
+
+ /**
+ * @return the advChestEnabled
+ */
+ public boolean isAdvChestEnabled() {
+ return advChestEnabled;
+ }
+
+ /**
+ * 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, @Nullable UUID targetPlayer) {
+ return getManager().getIslandLevel(world, targetPlayer);
+ }
+
+ /**
+ * Sets the player's level to a value
+ *
+ * @param world - world
+ * @param targetPlayer - target player
+ * @param level - level
+ */
+ public void setIslandLevel(World world, UUID targetPlayer, long level) {
+ getManager().setIslandLevel(world, targetPlayer, level);
+ }
+
+ /**
+ * Zeros the initial island level
+ *
+ * @param island - island
+ * @param level - initial calculated island level
+ */
+ public void setInitialIslandLevel(@NonNull Island island, long level) {
+ getManager().setInitialIslandLevel(island, level);
+ }
+
+ /**
+ * Get the initial island level
+ *
+ * @param island - island
+ * @return level or 0 by default
+ */
+ public long getInitialIslandLevel(@NonNull Island island) {
+ return getManager().getInitialLevel(island);
+ }
+
+ /**
+ * Calculates a user's island
+ *
+ * @param world - the world where this island is
+ * @param user - not used! See depecration message
+ * @param playerUUID - the target island member's UUID
+ * @deprecated Do not use this anymore. Use
+ * getManager().calculateLevel(playerUUID, island)
+ */
+ @Deprecated(since = "2.3.0", forRemoval = true)
+ public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) {
+ Island island = getIslands().getIsland(world, playerUUID);
+ if (island != null)
+ getManager().calculateLevel(playerUUID, island);
+ }
+
+ /**
+ * Provide the levels data for the target player
+ *
+ * @param targetPlayer - UUID of target player
+ * @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(since = "2.3.0", forRemoval = true)
+ public LevelsData getLevelsData(UUID targetPlayer) {
+ LevelsData ld = new LevelsData(targetPlayer);
+ getPlugin().getAddonsManager().getGameModeAddons().stream()
+ .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
+ if (getSettings().isZeroNewIslandLevels()) {
+ Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
+ if (island != null) {
+ ld.setInitialLevel(gm.getOverWorld(), this.getInitialIslandLevel(island));
+ }
+ }
+ ld.setLevel(gm.getOverWorld(), this.getIslandLevel(gm.getOverWorld(), targetPlayer));
+ });
+ return ld;
+ }
+
+ /**
+ * @return the registeredGameModes
+ */
+ public List getRegisteredGameModes() {
+ return registeredGameModes;
+ }
+
+ /**
+ * Check if Level addon is active in game mode
+ *
+ * @param gm Game Mode Addon
+ * @return true if active, false if not
+ */
+ public boolean isRegisteredGameMode(GameModeAddon gm) {
+ return registeredGameModes.contains(gm);
+ }
+
+ /**
+ * Checks if Level addon is active in world
+ *
+ * @param world world
+ * @return true if active, false if not
+ */
+ public boolean isRegisteredGameModeWorld(World world) {
+ return registeredGameModes.stream().map(GameModeAddon::getOverWorld).anyMatch(w -> Util.sameWorld(world, w));
+ }
+
+ /**
+ * @return the roseStackersEnabled
+ */
+ public boolean isRoseStackersEnabled() {
+ return roseStackersEnabled;
+ }
+
+ /**
+ * @return the ultimateStackerEnabled
+ */
+ public boolean isUltimateStackerEnabled() {
+ return ultimateStackerEnabled;
+ }
+
+ /**
+ * Method Level#getVisitHook returns the visitHook of this object.
+ *
+ * @return {@code Visit} of this object, {@code null} otherwise.
+ */
+ public VisitAddon getVisitHook() {
+ return this.visitHook;
+ }
+
+ /**
+ * Method Level#getWarpHook returns the warpHook of this object.
+ *
+ * @return {@code Warp} of this object, {@code null} otherwise.
+ */
+ public Warp getWarpHook() {
+ return this.warpHook;
+ }
+
}
diff --git a/src/main/java/world/bentobox/level/LevelsManager.java b/src/main/java/world/bentobox/level/LevelsManager.java
index e40ba49..f3532aa 100644
--- a/src/main/java/world/bentobox/level/LevelsManager.java
+++ b/src/main/java/world/bentobox/level/LevelsManager.java
@@ -2,6 +2,7 @@
import java.math.BigInteger;
import java.text.DecimalFormat;
+import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -31,18 +32,17 @@
import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.objects.TopTenData;
-
public class LevelsManager {
private static final String INTOPTEN = "intopten";
private static final TreeMap LEVELS;
private static final BigInteger THOUSAND = BigInteger.valueOf(1000);
static {
- LEVELS = new TreeMap<>();
+ LEVELS = new TreeMap<>();
- LEVELS.put(THOUSAND, "k");
- LEVELS.put(THOUSAND.pow(2), "M");
- LEVELS.put(THOUSAND.pow(3), "G");
- LEVELS.put(THOUSAND.pow(4), "T");
+ LEVELS.put(THOUSAND, "k");
+ LEVELS.put(THOUSAND.pow(2), "M");
+ LEVELS.put(THOUSAND.pow(3), "G");
+ LEVELS.put(THOUSAND.pow(4), "T");
}
private final Level addon;
@@ -51,397 +51,445 @@ public class LevelsManager {
// A cache of island levels.
private final Map levelsCache;
// Top ten lists
- private final Map topTenLists;
-
+ private final Map topTenLists;
public LevelsManager(Level addon) {
- this.addon = addon;
- // Get the BentoBox database
- // Set up the database handler to store and retrieve data
- // Note that these are saved by the BentoBox database
- handler = new Database<>(addon, IslandLevels.class);
- // Initialize the cache
- levelsCache = new HashMap<>();
- // Initialize top ten lists
- topTenLists = new ConcurrentHashMap<>();
+ this.addon = addon;
+ // Get the BentoBox database
+ // Set up the database handler to store and retrieve data
+ // Note that these are saved by the BentoBox database
+ handler = new Database<>(addon, IslandLevels.class);
+ // Initialize the cache
+ levelsCache = new HashMap<>();
+ // Initialize top ten lists
+ topTenLists = new ConcurrentHashMap<>();
}
public void migrate() {
- Database oldDb = new Database<>(addon, LevelsData.class);
- oldDb.loadObjects().forEach(ld -> {
- try {
- UUID owner = UUID.fromString(ld.getUniqueId());
- // Step through each world
- ld.getLevels().keySet().stream()
- // World
- .map(Bukkit::getWorld).filter(Objects::nonNull)
- // Island
- .map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull)
- .forEach(i -> {
- // Make new database entry
- World w = i.getWorld();
- IslandLevels il = new IslandLevels(i.getUniqueId());
- il.setInitialLevel(ld.getInitialLevel(w));
- il.setLevel(ld.getLevel(w));
- il.setMdCount(ld.getMdCount(w));
- il.setPointsToNextLevel(ld.getPointsToNextLevel(w));
- il.setUwCount(ld.getUwCount(w));
- // Save it
- handler.saveObjectAsync(il);
- });
- // Now delete the old database entry
- oldDb.deleteID(ld.getUniqueId());
- } catch (Exception e) {
- addon.logError("Could not migrate level data database! " + e.getMessage());
- e.printStackTrace();
- return;
- }
- });
- }
-
- /**
- * Add a score to the top players list
- * @param world - world
- * @param targetPlayer - target player
- * @param lv - island level
- */
- private void addToTopTen(@NonNull World world, @NonNull UUID targetPlayer, long lv) {
- // Get top ten
- Map 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)
- topTen.remove(targetPlayer);
-
- // Get the island
- Island island = addon.getIslands().getIsland(world, targetPlayer);
- if (island != null && island.getOwner() != null && hasTopTenPerm(world, island.getOwner())) {
- // Insert the owner into the top ten
- topTen.put(island.getOwner(), lv);
- }
+ Database oldDb = new Database<>(addon, LevelsData.class);
+ oldDb.loadObjects().forEach(ld -> {
+ try {
+ UUID owner = UUID.fromString(ld.getUniqueId());
+ // Step through each world
+ ld.getLevels().keySet().stream()
+ // World
+ .map(Bukkit::getWorld).filter(Objects::nonNull)
+ // Island
+ .map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull).forEach(i -> {
+ // Make new database entry
+ World w = i.getWorld();
+ IslandLevels il = new IslandLevels(i.getUniqueId());
+ il.setInitialLevel(ld.getInitialLevel(w));
+ il.setLevel(ld.getLevel(w));
+ il.setMdCount(ld.getMdCount(w));
+ il.setPointsToNextLevel(ld.getPointsToNextLevel(w));
+ il.setUwCount(ld.getUwCount(w));
+ // Save it
+ handler.saveObjectAsync(il);
+ });
+ // Now delete the old database entry
+ oldDb.deleteID(ld.getUniqueId());
+ } catch (Exception e) {
+ addon.logError("Could not migrate level data database! " + e.getMessage());
+ e.printStackTrace();
+ return;
+ }
+ });
}
/**
* Add an island to a top ten
+ *
* @param island - island to add
- * @param lv - level
+ * @param lv - level
* @return true if successful, false if not added
*/
private boolean addToTopTen(Island island, long lv) {
- if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) {
- topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld()))
- .getTopTen().put(island.getOwner(), lv);
- return true;
- }
- return false;
+ if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) {
+ topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen()
+ .put(island.getUniqueId(), lv);
+ return true;
+ }
+ 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 island - island to calculate
+ * @param island - island to calculate
* @return completable future with the results of the calculation
*/
public CompletableFuture calculateLevel(UUID targetPlayer, Island island) {
- CompletableFuture result = new CompletableFuture<>();
- // Fire pre-level calc event
- IslandPreLevelEvent e = new IslandPreLevelEvent(targetPlayer, island);
- Bukkit.getPluginManager().callEvent(e);
- if (e.isCancelled()) {
- return CompletableFuture.completedFuture(null);
- }
- // Add island to the pipeline
- addon.getPipeliner().addIsland(island).thenAccept(r -> {
- // Results are irrelevant because the island is unowned or deleted, or IslandLevelCalcEvent is cancelled
- if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) {
- result.complete(null);
- }
- // Save result
- setIslandResults(island.getWorld(), island.getOwner(), r);
- // Save the island scan details
- result.complete(r);
- });
- return result;
+ CompletableFuture result = new CompletableFuture<>();
+ // Fire pre-level calc event
+ IslandPreLevelEvent e = new IslandPreLevelEvent(targetPlayer, island);
+ Bukkit.getPluginManager().callEvent(e);
+ if (e.isCancelled()) {
+ return CompletableFuture.completedFuture(null);
+ }
+ // Add island to the pipeline
+ addon.getPipeliner().addIsland(island).thenAccept(r -> {
+ // Results are irrelevant because the island is unowned or deleted, or
+ // IslandLevelCalcEvent is cancelled
+ if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) {
+ result.complete(null);
+ }
+ // Save result
+ setIslandResults(island, r);
+ // Save the island scan details
+ result.complete(r);
+ });
+ return result;
}
/**
* Fires the IslandLevelCalculatedEvent and returns true if it is canceled
+ *
* @param targetPlayer - target player
- * @param island - island
- * @param results - results set
+ * @param island - island
+ * @param results - results set
* @return true if canceled
*/
private boolean fireIslandLevelCalcEvent(UUID targetPlayer, Island island, Results results) {
- // Fire post calculation event
- IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results);
- Bukkit.getPluginManager().callEvent(ilce);
- if (ilce.isCancelled()) return true;
- // Set the values if they were altered
- results.setLevel((Long)ilce.getKeyValues().getOrDefault("level", results.getLevel()));
- results.setInitialLevel((Long)ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel()));
- results.setDeathHandicap((int)ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap()));
- results.setPointsToNextLevel((Long)ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel()));
- results.setTotalPoints((Long)ilce.getKeyValues().getOrDefault("totalPoints", results.getTotalPoints()));
- return ((Boolean)ilce.getKeyValues().getOrDefault("isCancelled", false));
+ // Fire post calculation event
+ IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results);
+ Bukkit.getPluginManager().callEvent(ilce);
+ if (ilce.isCancelled())
+ return true;
+ // Set the values if they were altered
+ results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel()));
+ results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel()));
+ results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap()));
+ results.setPointsToNextLevel(
+ (Long) ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel()));
+ results.setTotalPoints((Long) ilce.getKeyValues().getOrDefault("totalPoints", results.getTotalPoints()));
+ 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
* @return string of the level.
*/
public String formatLevel(@Nullable Long lvl) {
- if (lvl == null) return "";
- String level = String.valueOf(lvl);
- // Asking for the level of another player
- if(addon.getSettings().isShorthand()) {
- BigInteger levelValue = BigInteger.valueOf(lvl);
-
- Map.Entry stage = LEVELS.floorEntry(levelValue);
-
- if (stage != null) { // level > 1000
- // 1 052 -> 1.0k
- // 1 527 314 -> 1.5M
- // 3 874 130 021 -> 3.8G
- // 4 002 317 889 -> 4.0T
- level = new DecimalFormat("#.#").format(levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue()/1000.0) + stage.getValue();
- }
- }
- return level;
+ if (lvl == null)
+ return "";
+ String level = String.valueOf(lvl);
+ // Asking for the level of another player
+ if (addon.getSettings().isShorthand()) {
+ BigInteger levelValue = BigInteger.valueOf(lvl);
+
+ Map.Entry stage = LEVELS.floorEntry(levelValue);
+
+ if (stage != null) { // level > 1000
+ // 1 052 -> 1.0k
+ // 1 527 314 -> 1.5M
+ // 3 874 130 021 -> 3.8G
+ // 4 002 317 889 -> 4.0T
+ level = new DecimalFormat("#.#").format(
+ levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue() / 1000.0) + stage.getValue();
+ }
+ }
+ return level;
}
/**
* Get the initial level of the island. Used to zero island levels
+ *
* @param island - island
* @return initial level of island
*/
public long getInitialLevel(Island island) {
- return getLevelsData(island).getInitialLevel();
+ return getLevelsData(island).getInitialLevel();
}
/**
* 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
- * @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) {
- if (targetPlayer == null) return 0L;
- // Get the island
- Island island = addon.getIslands().getIsland(world, targetPlayer);
- return island == null ? 0L : getLevelsData(island).getLevel();
+ if (targetPlayer == null)
+ return 0L;
+ // Get the island
+ Island island = addon.getIslands().getIsland(world, targetPlayer);
+ return island == null ? 0L : getLevelsData(island).getLevel();
}
/**
* 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
- * @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) {
- if (targetPlayer == null) return 0L;
- // Get the island
- Island island = addon.getIslands().getIsland(world, targetPlayer);
- return island == null ? 0L : getLevelsData(island).getMaxLevel();
+ if (targetPlayer == null)
+ return 0L;
+ // Get the island
+ Island island = addon.getIslands().getIsland(world, targetPlayer);
+ return island == null ? 0L : getLevelsData(island).getMaxLevel();
}
/**
* 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
- * @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) {
- return formatLevel(getIslandLevel(world, targetPlayer));
+ return formatLevel(getIslandLevel(world, targetPlayer));
}
/**
* Load a level data for the island from the cache or database.
+ *
* @param island - UUID of island
* @return IslandLevels object
*/
@NonNull
public IslandLevels getLevelsData(@NonNull Island island) {
- String id = island.getUniqueId();
- if (levelsCache.containsKey(id)) {
- return levelsCache.get(id);
- }
- // Get from database if not in cache
- if (handler.objectExists(id)) {
- IslandLevels ld = handler.loadObject(id);
- if (ld != null) {
- levelsCache.put(id, ld);
- } else {
- handler.deleteID(id);
- levelsCache.put(id, new IslandLevels(id));
- }
- } else {
- levelsCache.put(id, new IslandLevels(id));
- }
- // Return cached value
- return levelsCache.get(id);
+ String id = island.getUniqueId();
+ if (levelsCache.containsKey(id)) {
+ return levelsCache.get(id);
+ }
+ // Get from database if not in cache
+ if (handler.objectExists(id)) {
+ IslandLevels ld = handler.loadObject(id);
+ if (ld != null) {
+ levelsCache.put(id, ld);
+ } else {
+ handler.deleteID(id);
+ levelsCache.put(id, new IslandLevels(id));
+ }
+ } else {
+ levelsCache.put(id, new IslandLevels(id));
+ }
+ // Return cached value
+ return levelsCache.get(id);
}
/**
- * Get the number of points required until the next level since the last level calc
- * @param world - world where the island is
+ * Get the number of points required until the next level since the last level
+ * calc
+ *
+ * @param world - world where the island is
* @param targetPlayer - target player UUID
* @return string with the number required or blank if the player is unknown
*/
public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) {
- if (targetPlayer == null) return "";
- Island island = addon.getIslands().getIsland(world, targetPlayer);
- return island == null ? "" : String.valueOf(getLevelsData(island).getPointsToNextLevel());
+ if (targetPlayer == null)
+ return "";
+ Island island = addon.getIslands().getIsland(world, targetPlayer);
+ 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 weighted top ten for this world. Weighting is based on number of
+ * players per team.
+ *
* @param world - world requested
- * @param size - size of the top ten
- * @return sorted top ten map
+ * @param size - size of the top ten
+ * @return sorted top ten map. The key is the island unique ID
*/
@NonNull
- public Map getTopTen(@NonNull World world, int size) {
- createAndCleanRankings(world);
- // Return the sorted map
- return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
- .filter(e -> addon.getIslands().isOwner(world, e.getKey()))
- .filter(l -> l.getValue() > 0)
- .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(size)
- .collect(Collectors.toMap(
- Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)));
+ public Map getWeightedTopTen(@NonNull World world, int size) {
+ createAndCleanRankings(world);
+ Map weightedTopTen = topTenLists.get(world).getTopTen().entrySet().stream()
+ .map(en -> addon.getIslands().getIslandById(en.getKey()).map(island -> {
+
+ long value = (long) (en.getValue() / (double) Math.max(1, island.getMemberSet().size())); // Calculate
+ // weighted
+ // value
+ return new AbstractMap.SimpleEntry<>(island, value);
+ }).orElse(null)) // Handle islands that do not exist according to this ID - old deleted ones
+ .filter(Objects::nonNull) // Filter out null entries
+ .filter(en -> en.getValue() > 0) // Filter out entries with non-positive values
+ .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) // Sort in descending order of values
+ .limit(size) // Limit to the top 'size' entries
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, // In case of key
+ // collision, choose
+ // the first one
+ LinkedHashMap::new // Preserves the order of entries
+ ));
+
+ // Return the unmodifiable map
+ return Collections.unmodifiableMap(weightedTopTen);
+
+ }
+
+ /**
+ * Get the top ten for this world. Returns offline players or players with the
+ * intopten permission.
+ *
+ * @param world - world requested
+ * @param size - size of the top ten
+ * @return sorted top ten map. The key is the island unique ID
+ */
+ @NonNull
+ public Map getTopTen(@NonNull World world, int size) {
+ createAndCleanRankings(world);
+ // Return the sorted map
+ return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
+ .filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
+ .limit(size)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)));
}
void createAndCleanRankings(@NonNull World world) {
- topTenLists.computeIfAbsent(world, TopTenData::new);
- // Remove player from top ten if they are online and do not have the perm
- topTenLists.get(world).getTopTen().keySet().removeIf(u -> !hasTopTenPerm(world, u));
+ topTenLists.computeIfAbsent(world, TopTenData::new);
+ // Remove player from top ten if they are online and do not have the perm
+ topTenLists.get(world).getTopTen().keySet().removeIf(u -> addon.getIslands().getIslandById(u)
+ .filter(i -> i.getOwner() == null || !hasTopTenPerm(world, i.getOwner())).isPresent());
}
/**
* @return the topTenLists
*/
- protected Map getTopTenLists() {
- return topTenLists;
+ public Map getTopTenLists() {
+ return topTenLists;
}
/**
* Get the rank of the player in the rankings
+ *
* @param world - world
- * @param uuid - player UUID
+ * @param uuid - player UUID
* @return rank placing - note - placing of 1 means top ranked
*/
public int getRank(@NonNull World world, UUID uuid) {
- createAndCleanRankings(world);
- Stream> stream = topTenLists.get(world).getTopTen().entrySet().stream()
- .filter(e -> addon.getIslands().isOwner(world, e.getKey()))
- .filter(l -> l.getValue() > 0)
- .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
- return (int) (stream.takeWhile(x -> !x.getKey().equals(uuid)).map(Map.Entry::getKey).count() + 1);
+ createAndCleanRankings(world);
+ Stream> stream = topTenLists.get(world).getTopTen().entrySet().stream()
+ .filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
+ // Get player's current island
+ Island island = addon.getIslands().getIsland(world, uuid);
+ String id = island == null ? null : island.getUniqueId();
+ return (int) (stream.takeWhile(x -> !x.getKey().equals(id)).map(Map.Entry::getKey).count() + 1);
}
/**
* Checks if player has the correct top ten perm to have their level saved
+ *
* @param world
* @param targetPlayer
* @return true if player has the perm or the player is offline
*/
boolean hasTopTenPerm(@NonNull World world, @NonNull UUID targetPlayer) {
- String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world);
- return Bukkit.getPlayer(targetPlayer) == null || Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN);
+ String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world);
+ return Bukkit.getPlayer(targetPlayer) == null
+ || Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN);
}
/**
* Loads all the top tens from the database
*/
public void loadTopTens() {
- topTenLists.clear();
- Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
- addon.log("Generating rankings");
- handler.loadObjects().forEach(il -> {
- if (il.getLevel() > 0) {
- addon.getIslands().getIslandById(il.getUniqueId()).ifPresent(i -> this.addToTopTen(i, il.getLevel()));
- }
- });
- topTenLists.keySet().forEach(w -> addon.log("Generated rankings for " + w.getName()));
- });
+ topTenLists.clear();
+ Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
+ addon.log("Generating rankings");
+ handler.loadObjects().forEach(il -> {
+ if (il.getLevel() > 0) {
+ addon.getIslands().getIslandById(il.getUniqueId())
+ .ifPresent(i -> this.addToTopTen(i, il.getLevel()));
+ }
+ });
+ topTenLists.keySet().forEach(w -> addon.log("Generated rankings for " + w.getName()));
+ });
}
/**
- * Removes a player from a world's top ten and removes world from player's level data
+ * Removes an island from a world's top ten
+ *
* @param world - world
- * @param uuid - the player's uuid
+ * @param uuid - the island's uuid
*/
- public void removeEntry(World world, UUID uuid) {
- if (topTenLists.containsKey(world)) {
- topTenLists.get(world).getTopTen().remove(uuid);
- }
+ public void removeEntry(World world, String uuid) {
+ if (topTenLists.containsKey(world)) {
+ topTenLists.get(world).getTopTen().remove(uuid);
+ }
}
/**
* Set an initial island level
+ *
* @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) {
- if (island.getWorld() == null) return;
- levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
- handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
+ if (island.getWorld() == null)
+ return;
+ levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
+ handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
}
/**
- * Set the island level for the owner of the island that targetPlayer is a member
- * @param world - world
- * @param targetPlayer - player, may be a team member
- * @param lv - level
+ * Set the island level for the owner of the island that targetPlayer is a
+ * member
+ *
+ * @param world - world
+ * @param island - island
+ * @param lv - level
*/
public void setIslandLevel(@NonNull World world, @NonNull UUID targetPlayer, long lv) {
- // Get the island
- Island island = addon.getIslands().getIsland(world, targetPlayer);
- if (island != null) {
- String id = island.getUniqueId();
- IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new);
- // Remove the initial level
- if (addon.getSettings().isZeroNewIslandLevels()) {
- il.setLevel(lv - il.getInitialLevel());
- } else {
- il.setLevel(lv);
- }
- handler.saveObjectAsync(levelsCache.get(id));
- // Update TopTen
- addToTopTen(world, targetPlayer, levelsCache.get(id).getLevel());
- }
-
+ // Get the island
+ Island island = addon.getIslands().getIsland(world, targetPlayer);
+ if (island != null) {
+ String id = island.getUniqueId();
+ IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new);
+ // Remove the initial level
+ if (addon.getSettings().isZeroNewIslandLevels()) {
+ il.setLevel(lv - il.getInitialLevel());
+ } else {
+ il.setLevel(lv);
+ }
+ handler.saveObjectAsync(levelsCache.get(id));
+ // Update TopTen
+ addToTopTen(island, levelsCache.get(id).getLevel());
+ }
}
/**
- * 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 owner - owner of the island
- * @param r - results of the calculation
+ * @param r - results of the calculation
*/
- private void setIslandResults(World world, @NonNull UUID owner, Results r) {
- // Get the island
- Island island = addon.getIslands().getIsland(world, owner);
- if (island == null) return;
- IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new);
- ld.setLevel(r.getLevel());
- ld.setUwCount(Maps.asMap(r.getUwCount().elementSet(), elem -> r.getUwCount().count(elem)));
- ld.setMdCount(Maps.asMap(r.getMdCount().elementSet(), elem -> r.getMdCount().count(elem)));
- ld.setPointsToNextLevel(r.getPointsToNextLevel());
- ld.setTotalPoints(r.getTotalPoints());
- levelsCache.put(island.getUniqueId(), ld);
- handler.saveObjectAsync(ld);
- // Update TopTen
- addToTopTen(world, owner, ld.getLevel());
+ private void setIslandResults(Island island, Results r) {
+ if (island == null)
+ return;
+ IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new);
+ ld.setLevel(r.getLevel());
+ ld.setUwCount(Maps.asMap(r.getUwCount().elementSet(), elem -> r.getUwCount().count(elem)));
+ ld.setMdCount(Maps.asMap(r.getMdCount().elementSet(), elem -> r.getMdCount().count(elem)));
+ ld.setPointsToNextLevel(r.getPointsToNextLevel());
+ ld.setTotalPoints(r.getTotalPoints());
+ levelsCache.put(island.getUniqueId(), ld);
+ handler.saveObjectAsync(ld);
+ // Update TopTen
+ addToTopTen(island, ld.getLevel());
}
/**
* Removes island from cache when it is deleted
+ *
* @param uniqueId - id of island
*/
public void deleteIsland(String uniqueId) {
- levelsCache.remove(uniqueId);
- handler.deleteID(uniqueId);
+ levelsCache.remove(uniqueId);
+ handler.deleteID(uniqueId);
}
}
diff --git a/src/main/java/world/bentobox/level/PlaceholderManager.java b/src/main/java/world/bentobox/level/PlaceholderManager.java
index 5e02a2c..5f0bd17 100644
--- a/src/main/java/world/bentobox/level/PlaceholderManager.java
+++ b/src/main/java/world/bentobox/level/PlaceholderManager.java
@@ -2,10 +2,12 @@
import java.util.Collections;
import java.util.Map;
+import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.World;
+import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
@@ -18,6 +20,7 @@
/**
* Handles Level placeholders
+ *
* @author tastybento
*
*/
@@ -27,150 +30,186 @@ public class PlaceholderManager {
private final BentoBox plugin;
public PlaceholderManager(Level addon) {
- this.addon = addon;
- this.plugin = addon.getPlugin();
+ this.addon = addon;
+ this.plugin = addon.getPlugin();
}
-
+
protected void registerPlaceholders(GameModeAddon gm) {
- if (plugin.getPlaceholdersManager() == null) return;
- PlaceholdersManager bpm = plugin.getPlaceholdersManager();
- // Island Level
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_island_level",
- user -> addon.getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId()));
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_island_level_raw",
- user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId())));
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_island_total_points",
- user -> {
- IslandLevels data = addon.getManager().getLevelsData(addon.getIslands().getIsland(gm.getOverWorld(), user));
- return data.getTotalPoints()+"";
- });
-
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_points_to_next_level",
- user -> addon.getManager().getPointsToNextString(gm.getOverWorld(), user.getUniqueId()));
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_island_level_max",
- user -> String.valueOf(addon.getManager().getIslandMaxLevel(gm.getOverWorld(), user.getUniqueId())));
-
- // Visited Island Level
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_visited_island_level", user -> getVisitedIslandLevel(gm, user));
-
- // Register Top Ten Placeholders
- for (int i = 1; i < 11; i++) {
- final int rank = i;
- // Name
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_top_name_" + i, u -> getRankName(gm.getOverWorld(), rank));
- // Island Name
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_top_island_name_" + i, u -> getRankIslandName(gm.getOverWorld(), rank));
- // Members
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_top_members_" + i, u -> getRankMembers(gm.getOverWorld(), rank));
- // Level
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_top_value_" + i, u -> getRankLevel(gm.getOverWorld(), rank));
- }
-
- // Personal rank
- bpm.registerPlaceholder(addon,
- gm.getDescription().getName().toLowerCase() + "_rank_value", u -> getRankValue(gm.getOverWorld(), u));
+ if (plugin.getPlaceholdersManager() == null)
+ return;
+ PlaceholdersManager bpm = plugin.getPlaceholdersManager();
+ // Island Level
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level",
+ user -> addon.getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId()));
+ // Unformatted island level
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_raw",
+ user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId())));
+ // Total number of points counted before applying level formula
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_total_points", user -> {
+ IslandLevels data = addon.getManager().getLevelsData(addon.getIslands().getIsland(gm.getOverWorld(), user));
+ return data.getTotalPoints() + "";
+ });
+ // Points to the next level for player
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_points_to_next_level",
+ user -> addon.getManager().getPointsToNextString(gm.getOverWorld(), user.getUniqueId()));
+ // Maximum level this island has ever been. Current level maybe lower.
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_max",
+ user -> String.valueOf(addon.getManager().getIslandMaxLevel(gm.getOverWorld(), user.getUniqueId())));
+
+ // Visited Island Level
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_visited_island_level",
+ user -> getVisitedIslandLevel(gm, user));
+
+ // Register Top Ten Placeholders
+ for (int i = 1; i < 11; i++) {
+ final int rank = i;
+ // Name
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_name_" + i,
+ u -> getRankName(gm.getOverWorld(), rank, false));
+ // Island Name
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_island_name_" + i,
+ u -> getRankIslandName(gm.getOverWorld(), rank, false));
+ // Members
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_members_" + i,
+ u -> getRankMembers(gm.getOverWorld(), rank, false));
+ // Level
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_value_" + i,
+ u -> getRankLevel(gm.getOverWorld(), rank, false));
+ // Weighted Level Name (Level / number of members)
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_weighted_name_" + i,
+ u -> getRankName(gm.getOverWorld(), rank, true));
+ // Weighted Island Name
+ bpm.registerPlaceholder(addon,
+ gm.getDescription().getName().toLowerCase() + "_top_weighted_island_name_" + i,
+ u -> getRankIslandName(gm.getOverWorld(), rank, true));
+ // Weighted Members
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_weighted_members_" + i,
+ u -> getRankMembers(gm.getOverWorld(), rank, true));
+ // Weighted Level (Level / number of members)
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_top_weighted_value_" + i,
+ u -> getRankLevel(gm.getOverWorld(), rank, true));
+ }
+
+ // Personal rank
+ bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_rank_value",
+ u -> getRankValue(gm.getOverWorld(), u));
}
/**
- * Get the name of the player who holds the rank in this world
- * @param world world
- * @param rank rank 1 to 10
+ * Get the name of the owner of the island who holds the rank in this world.
+ *
+ * @param world world
+ * @param rank rank 1 to 10
+ * @param weighted if true, then the weighted rank name is returned
* @return rank name
*/
- String getRankName(World world, int rank) {
- if (rank < 1) rank = 1;
- if (rank > Level.TEN) rank = Level.TEN;
- return addon.getPlayers().getName(addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L).findFirst().orElse(null));
+ String getRankName(World world, int rank, boolean weighted) {
+ // Ensure rank is within bounds
+ rank = Math.max(1, Math.min(rank, Level.TEN));
+ if (weighted) {
+ return addon.getManager().getWeightedTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L)
+ .findFirst().map(Island::getOwner).map(addon.getPlayers()::getName).orElse("");
+ }
+ @Nullable
+ UUID owner = addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L)
+ .findFirst().flatMap(addon.getIslands()::getIslandById).map(Island::getOwner).orElse(null);
+
+ return addon.getPlayers().getName(owner);
}
/**
* Get the island name for this rank
- * @param world world
- * @param rank rank 1 to 10
+ *
+ * @param world world
+ * @param rank rank 1 to 10
+ * @param weighted if true, then the weighted rank name is returned
* @return name of island or nothing if there isn't one
*/
- String getRankIslandName(World world, int rank) {
- if (rank < 1) rank = 1;
- if (rank > Level.TEN) rank = Level.TEN;
- UUID owner = addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L).findFirst().orElse(null);
- if (owner != null) {
- Island island = addon.getIslands().getIsland(world, owner);
- if (island != null) {
- return island.getName() == null ? "" : island.getName();
- }
- }
- return "";
+ String getRankIslandName(World world, int rank, boolean weighted) {
+ // Ensure rank is within bounds
+ rank = Math.max(1, Math.min(rank, Level.TEN));
+ if (weighted) {
+ return addon.getManager().getWeightedTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L)
+ .findFirst().map(Island::getName).orElse("");
+ }
+ return addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L).findFirst()
+ .flatMap(addon.getIslands()::getIslandById).map(Island::getName).orElse("");
}
/**
* Gets a comma separated string of island member names
- * @param world world
- * @param rank rank to request
+ *
+ * @param world world
+ * @param rank rank to request
+ * @param weighted if true, then the weighted rank name is returned
* @return comma separated string of island member names
*/
- String getRankMembers(World world, int rank) {
- if (rank < 1) rank = 1;
- if (rank > Level.TEN) rank = Level.TEN;
- UUID owner = addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L).findFirst().orElse(null);
- if (owner != null) {
- Island island = addon.getIslands().getIsland(world, owner);
- if (island != null) {
- // Sort members by rank
- return island.getMembers().entrySet().stream()
- .filter(e -> e.getValue() >= RanksManager.MEMBER_RANK)
- .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
- .map(Map.Entry::getKey)
- .map(addon.getPlayers()::getName)
- .collect(Collectors.joining(","));
- }
- }
- return "";
+ String getRankMembers(World world, int rank, boolean weighted) {
+ // Ensure rank is within bounds
+ rank = Math.max(1, Math.min(rank, Level.TEN));
+ if (weighted) {
+ return addon.getManager().getWeightedTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L)
+ .findFirst()
+ .map(is -> is.getMembers().entrySet().stream().filter(e -> e.getValue() >= RanksManager.MEMBER_RANK)
+ .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).map(Map.Entry::getKey)
+ .map(addon.getPlayers()::getName).collect(Collectors.joining(",")))
+ .orElse("");
+ }
+
+ Optional island = addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L)
+ .limit(1L).findFirst().flatMap(addon.getIslands()::getIslandById);
+
+ if (island.isPresent()) {
+ // Sort members by rank
+ return island.get().getMembers().entrySet().stream().filter(e -> e.getValue() >= RanksManager.MEMBER_RANK)
+ .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).map(Map.Entry::getKey)
+ .map(addon.getPlayers()::getName).collect(Collectors.joining(","));
+ }
+ return "";
}
- String getRankLevel(World world, int rank) {
- if (rank < 1) rank = 1;
- if (rank > Level.TEN) rank = Level.TEN;
- return addon.getManager()
- .formatLevel(addon.getManager()
- .getTopTen(world, Level.TEN)
- .values()
- .stream()
- .skip(rank - 1L)
- .limit(1L)
- .findFirst()
- .orElse(null));
+ /**
+ * Get the level for the rank requested
+ *
+ * @param world world
+ * @param rank rank wanted
+ * @param weighted true if weighted (level/number of team members)
+ * @return level for the rank requested
+ */
+ String getRankLevel(World world, int rank, boolean weighted) {
+ // Ensure rank is within bounds
+ rank = Math.max(1, Math.min(rank, Level.TEN));
+ if (weighted) {
+ return addon.getManager().formatLevel(addon.getManager().getWeightedTopTen(world, Level.TEN).values()
+ .stream().skip(rank - 1L).limit(1L).findFirst().orElse(null));
+ }
+ return addon.getManager().formatLevel(addon.getManager().getTopTen(world, Level.TEN).values().stream()
+ .skip(rank - 1L).limit(1L).findFirst().orElse(null));
}
-
+
/**
* Return the rank of the player in a world
+ *
* @param world world
- * @param user player
+ * @param user player
* @return rank where 1 is the top rank.
*/
private String getRankValue(World world, User user) {
- if (user == null) {
- return "";
- }
- // Get the island level for this user
- long level = addon.getManager().getIslandLevel(world, user.getUniqueId());
- return String.valueOf(addon.getManager().getTopTenLists().getOrDefault(world, new TopTenData(world)).getTopTen().values().stream().filter(l -> l > level).count() + 1);
+ if (user == null) {
+ return "";
+ }
+ // Get the island level for this user
+ long level = addon.getManager().getIslandLevel(world, user.getUniqueId());
+ return String.valueOf(addon.getManager().getTopTenLists().getOrDefault(world, new TopTenData(world)).getTopTen()
+ .values().stream().filter(l -> l > level).count() + 1);
}
String getVisitedIslandLevel(GameModeAddon gm, User user) {
- if (user == null || !gm.inWorld(user.getWorld())) return "";
- return addon.getIslands().getIslandAt(user.getLocation())
- .map(island -> addon.getManager().getIslandLevelString(gm.getOverWorld(), island.getOwner()))
- .orElse("0");
+ if (user == null || !gm.inWorld(user.getWorld()))
+ return "";
+ return addon.getIslands().getIslandAt(user.getLocation())
+ .map(island -> addon.getManager().getIslandLevelString(gm.getOverWorld(), island.getOwner()))
+ .orElse("0");
}
}
diff --git a/src/main/java/world/bentobox/level/calculators/EquationEvaluator.java b/src/main/java/world/bentobox/level/calculators/EquationEvaluator.java
new file mode 100644
index 0000000..3014542
--- /dev/null
+++ b/src/main/java/world/bentobox/level/calculators/EquationEvaluator.java
@@ -0,0 +1,121 @@
+package world.bentobox.level.calculators;
+
+import java.text.ParseException;
+
+/**
+ * Utility class to evaluate equations
+ */
+public class EquationEvaluator {
+
+ private static class Parser {
+ private final String input;
+ private int pos = -1;
+ private int currentChar;
+
+ @SuppressWarnings("unused")
+ private Parser() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ public Parser(String input) {
+ this.input = input;
+ moveToNextChar();
+ }
+
+ private void moveToNextChar() {
+ currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
+ }
+
+ private boolean tryToEat(int charToEat) {
+ while (currentChar == ' ') {
+ moveToNextChar();
+ }
+ if (currentChar == charToEat) {
+ moveToNextChar();
+ return true;
+ }
+ return false;
+ }
+
+ public double evaluate() throws ParseException {
+ double result = parseExpression();
+ if (pos < input.length()) {
+ throw new ParseException("Unexpected character: " + (char) currentChar, pos);
+ }
+ return result;
+ }
+
+ private double parseExpression() throws ParseException {
+ double result = parseTerm();
+ while (true) {
+ if (tryToEat('+')) {
+ result += parseTerm();
+ } else if (tryToEat('-')) {
+ result -= parseTerm();
+ } else {
+ return result;
+ }
+ }
+ }
+
+ private double parseFactor() throws ParseException {
+ if (tryToEat('+')) {
+ return parseFactor(); // unary plus
+ }
+ if (tryToEat('-')) {
+ return -parseFactor(); // unary minus
+ }
+ double x;
+ int startPos = this.pos;
+ if (tryToEat('(')) { // parentheses
+ x = parseExpression();
+ tryToEat(')');
+ } else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
+ while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') {
+ moveToNextChar();
+ }
+ x = Double.parseDouble(input.substring(startPos, this.pos));
+ } else if (currentChar >= 'a' && currentChar <= 'z') { // functions
+ while (currentChar >= 'a' && currentChar <= 'z') {
+ moveToNextChar();
+ }
+ String func = input.substring(startPos, this.pos);
+ x = parseFactor();
+ x = switch (func) {
+ case "sqrt" -> Math.sqrt(x);
+ case "sin" -> Math.sin(Math.toRadians(x));
+ case "cos" -> Math.cos(Math.toRadians(x));
+ case "tan" -> Math.tan(Math.toRadians(x));
+ case "log" -> Math.log(x);
+ default -> throw new ParseException("Unknown function: " + func, startPos);
+ };
+ } else {
+ throw new ParseException("Unexpected: " + (char) currentChar, startPos);
+ }
+
+ if (tryToEat('^')) {
+ x = Math.pow(x, parseFactor()); // exponentiation
+ }
+
+ return x;
+ }
+
+ private double parseTerm() throws ParseException {
+ double x = parseFactor();
+ for (;;) {
+ if (tryToEat('*'))
+ x *= parseFactor(); // multiplication
+ else if (tryToEat('/'))
+ x /= parseFactor(); // division
+ else
+ return x;
+ }
+ }
+
+ }
+
+ public static double eval(final String equation) throws ParseException {
+ return new Parser(equation).evaluate();
+ }
+
+}
diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
index 9c2a6cf..7fc1cca 100644
--- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
+++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
@@ -1,6 +1,6 @@
package world.bentobox.level.calculators;
-import java.io.IOException;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -16,9 +16,6 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
-import com.songoda.ultimatestacker.UltimateStacker;
-import com.songoda.ultimatestacker.core.compatibility.CompatibleMaterial;
-import com.songoda.ultimatestacker.stackable.block.BlockStack;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
@@ -27,7 +24,11 @@
import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.World.Environment;
-import org.bukkit.block.*;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.Container;
+import org.bukkit.block.CreatureSpawner;
+import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Slab;
import org.bukkit.inventory.ItemStack;
@@ -39,6 +40,9 @@
import com.google.common.collect.Multiset;
import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets;
+import com.songoda.ultimatestacker.UltimateStacker;
+import com.songoda.ultimatestacker.core.compatibility.CompatibleMaterial;
+import com.songoda.ultimatestacker.stackable.block.BlockStack;
import dev.rosewood.rosestacker.api.RoseStackerAPI;
import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI;
@@ -54,121 +58,21 @@
public class IslandLevelCalculator {
private static final String LINE_BREAK = "==================================";
public static final long MAX_AMOUNT = 10000000;
- private static final List CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART, Material.TRAPPED_CHEST,
- Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX, Material.BROWN_SHULKER_BOX,
- Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX,
- Material.LIGHT_GRAY_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
- Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.RED_SHULKER_BOX,
- Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL, Material.DISPENSER,
- Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
+ private static final List CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART,
+ Material.TRAPPED_CHEST, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
+ Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX,
+ Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX,
+ Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
+ Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.RED_SHULKER_BOX,
+ Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL,
+ Material.DISPENSER, Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
private static final int CHUNKS_TO_SCAN = 100;
-
- /**
- * Method to evaluate a mathematical equation
- * @param str - equation to evaluate
- * @return value of equation
- */
- private static double eval(final String str) throws IOException {
- return new Object() {
- int pos = -1;
- int ch;
-
- boolean eat(int charToEat) {
- while (ch == ' ') nextChar();
- if (ch == charToEat) {
- nextChar();
- return true;
- }
- return false;
- }
-
- void nextChar() {
- ch = (++pos < str.length()) ? str.charAt(pos) : -1;
- }
-
- double parse() throws IOException {
- nextChar();
- double x = parseExpression();
- if (pos < str.length()) throw new IOException("Unexpected: " + (char)ch);
- return x;
- }
-
- // Grammar:
- // expression = term | expression `+` term | expression `-` term
- // term = factor | term `*` factor | term `/` factor
- // factor = `+` factor | `-` factor | `(` expression `)`
- // | number | functionName factor | factor `^` factor
-
- double parseExpression() throws IOException {
- double x = parseTerm();
- for (;;) {
- if (eat('+')) x += parseTerm(); // addition
- else if (eat('-')) x -= parseTerm(); // subtraction
- else return x;
- }
- }
-
- double parseFactor() throws IOException {
- if (eat('+')) return parseFactor(); // unary plus
- if (eat('-')) return -parseFactor(); // unary minus
-
- double x;
- int startPos = this.pos;
- if (eat('(')) { // parentheses
- x = parseExpression();
- eat(')');
- } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
- while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
- x = Double.parseDouble(str.substring(startPos, this.pos));
- } else if (ch >= 'a' && ch <= 'z') { // functions
- while (ch >= 'a' && ch <= 'z') nextChar();
- String func = str.substring(startPos, this.pos);
- x = parseFactor();
- switch (func) {
- case "sqrt":
- x = Math.sqrt(x);
- break;
- case "sin":
- x = Math.sin(Math.toRadians(x));
- break;
- case "cos":
- x = Math.cos(Math.toRadians(x));
- break;
- case "tan":
- x = Math.tan(Math.toRadians(x));
- break;
- case "log":
- x = Math.log(x);
- break;
- default:
- throw new IOException("Unknown function: " + func);
- }
- } else {
- throw new IOException("Unexpected: " + (char)ch);
- }
-
- if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
-
- return x;
- }
-
- double parseTerm() throws IOException {
- double x = parseFactor();
- for (;;) {
- if (eat('*')) x *= parseFactor(); // multiplication
- else if (eat('/')) x /= parseFactor(); // division
- else return x;
- }
- }
- }.parse();
- }
private final Level addon;
private final Queue> chunksToCheck;
private final Island island;
private final Map limitCount;
private final CompletableFuture r;
-
private final Results results;
private long duration;
private final boolean zeroIsland;
@@ -178,569 +82,600 @@ void nextChar() {
private final Set chestBlocks = new HashSet<>();
private BukkitTask finishTask;
-
/**
* Constructor to get the level for an island
- * @param addon - Level addon
- * @param island - the island to scan
- * @param r - completable result that will be completed when the calculation is complete
+ *
+ * @param addon - Level addon
+ * @param island - the island to scan
+ * @param r - completable result that will be completed when the
+ * calculation is complete
* @param zeroIsland - true if the calculation is due to an island zeroing
*/
public IslandLevelCalculator(Level addon, Island island, CompletableFuture r, boolean zeroIsland) {
- this.addon = addon;
- this.island = island;
- this.r = r;
- this.zeroIsland = zeroIsland;
- results = new Results();
- duration = System.currentTimeMillis();
- chunksToCheck = getChunksToScan(island);
- this.limitCount = new EnumMap<>(addon.getBlockConfig().getBlockLimits());
- // Get the initial island level
- results.initialLevel.set(addon.getInitialIslandLevel(island));
- // Set up the worlds
- worlds.put(Environment.NORMAL, Util.getWorld(island.getWorld()));
- // Nether
- if (addon.getSettings().isNether()) {
- World nether = addon.getPlugin().getIWM().getNetherWorld(island.getWorld());
- if (nether != null) {
- worlds.put(Environment.NETHER, nether);
- }
- }
- // End
- if (addon.getSettings().isEnd()) {
- World end = addon.getPlugin().getIWM().getEndWorld(island.getWorld());
- if (end != null) {
- worlds.put(Environment.THE_END, end);
- }
- }
- // Sea Height
- seaHeight = addon.getPlugin().getIWM().getSeaHeight(island.getWorld());
+ this.addon = addon;
+ this.island = island;
+ this.r = r;
+ this.zeroIsland = zeroIsland;
+ results = new Results();
+ duration = System.currentTimeMillis();
+ chunksToCheck = getChunksToScan(island);
+ this.limitCount = new EnumMap<>(addon.getBlockConfig().getBlockLimits());
+ // Get the initial island level
+ results.initialLevel.set(addon.getInitialIslandLevel(island));
+ // Set up the worlds
+ worlds.put(Environment.NORMAL, Util.getWorld(island.getWorld()));
+ // Nether
+ if (addon.getSettings().isNether()) {
+ World nether = addon.getPlugin().getIWM().getNetherWorld(island.getWorld());
+ if (nether != null) {
+ worlds.put(Environment.NETHER, nether);
+ }
+ }
+ // End
+ if (addon.getSettings().isEnd()) {
+ World end = addon.getPlugin().getIWM().getEndWorld(island.getWorld());
+ if (end != null) {
+ worlds.put(Environment.THE_END, end);
+ }
+ }
+ // Sea Height
+ seaHeight = addon.getPlugin().getIWM().getSeaHeight(island.getWorld());
}
/**
* Calculate the level based on the raw points
+ *
* @param blockAndDeathPoints - raw points counted on island
* @return level of island
*/
private long calculateLevel(long blockAndDeathPoints) {
- String calcString = addon.getSettings().getLevelCalc();
- String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost", String.valueOf(this.addon.getSettings().getLevelCost()));
- long evalWithValues;
- try {
- evalWithValues = (long)eval(withValues);
- return evalWithValues - (addon.getSettings().isZeroNewIslandLevels() ? results.initialLevel.get() : 0);
-
- } catch (IOException e) {
- addon.getPlugin().logStacktrace(e);
- return 0L;
- }
+ String calcString = addon.getSettings().getLevelCalc();
+ String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost",
+ String.valueOf(this.addon.getSettings().getLevelCost()));
+ long evalWithValues;
+ try {
+ evalWithValues = (long) EquationEvaluator.eval(withValues);
+ return evalWithValues - (addon.getSettings().isZeroNewIslandLevels() ? results.initialLevel.get() : 0);
+
+ } catch (ParseException e) {
+ addon.getPlugin().logStacktrace(e);
+ return 0L;
+ }
}
/**
- * Adds value to the results based on the material and whether the block is below sea level or not
- * @param mat - material of the block
+ * Adds value to the results based on the material and whether the block is
+ * below sea level or not
+ *
+ * @param mat - material of the block
* @param belowSeaLevel - true if below sea level
*/
private void checkBlock(Material mat, boolean belowSeaLevel) {
- int count = limitCount(mat);
- if (belowSeaLevel) {
- results.underWaterBlockCount.addAndGet(count);
- results.uwCount.add(mat);
- } else {
- results.rawBlockCount.addAndGet(count);
- results.mdCount.add(mat);
- }
+ int count = limitCount(mat);
+ if (belowSeaLevel) {
+ results.underWaterBlockCount.addAndGet(count);
+ results.uwCount.add(mat);
+ } else {
+ results.rawBlockCount.addAndGet(count);
+ results.mdCount.add(mat);
+ }
}
/**
* Get a set of all the chunks in island
+ *
* @param island - island
* @return - set of pairs of x,z coordinates to check
*/
private Queue> getChunksToScan(Island island) {
- Queue> chunkQueue = new ConcurrentLinkedQueue<>();
- for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
- for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
- chunkQueue.add(new Pair<>(x >> 4, z >> 4));
- }
- }
- return chunkQueue;
+ Queue> chunkQueue = new ConcurrentLinkedQueue<>();
+ for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2
+ + 16); x += 16) {
+ for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2
+ + 16); z += 16) {
+ chunkQueue.add(new Pair<>(x >> 4, z >> 4));
+ }
+ }
+ return chunkQueue;
}
-
/**
* @return the island
*/
public Island getIsland() {
- return island;
+ return island;
}
/**
* Get the completable result for this calculation
+ *
* @return the r
*/
public CompletableFuture getR() {
- return r;
+ return r;
}
/**
* Get the full analysis report
+ *
* @return a list of lines
*/
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",results.rawBlockCount.get()));
- reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
- reportLines.add("Level cost = " + addon.getSettings().getLevelCost());
- reportLines.add("Deaths handicap = " + results.deathHandicap.get());
- if (addon.getSettings().isZeroNewIslandLevels()) {
- reportLines.add("Initial island level = " + (0L - addon.getManager().getInitialLevel(island)));
- }
- reportLines.add("Previous level = " + addon.getManager().getIslandLevel(island.getWorld(), island.getOwner()));
- reportLines.add("New level = " + results.getLevel());
- reportLines.add(LINE_BREAK);
- int total = 0;
- if (!results.uwCount.isEmpty()) {
- reportLines.add("Underwater block count (Multiplier = x" + addon.getSettings().getUnderWaterMultiplier() + ") value");
- reportLines.add("Total number of underwater blocks = " + String.format("%,d",results.uwCount.size()));
- reportLines.addAll(sortedReport(total, results.uwCount));
- }
- reportLines.add("Regular block count");
- reportLines.add("Total number of blocks = " + String.format("%,d",results.mdCount.size()));
- reportLines.addAll(sortedReport(total, results.mdCount));
-
- reportLines.add("Blocks not counted because they exceeded limits: " + String.format("%,d",results.ofCount.size()));
- Iterable> entriesSortedByCount = results.ofCount.entrySet();
- Iterator> it = entriesSortedByCount.iterator();
- while (it.hasNext()) {
- Entry type = it.next();
- Integer limit = addon.getBlockConfig().getBlockLimits().get(type.getElement());
- String explain = ")";
- if (limit == null) {
- Material generic = type.getElement();
- limit = addon.getBlockConfig().getBlockLimits().get(generic);
- explain = " - All types)";
- }
- reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks (max " + limit + explain);
- }
- reportLines.add(LINE_BREAK);
- reportLines.add("Blocks on island that are not in config.yml");
- reportLines.add("Total number = " + String.format("%,d",results.ncCount.size()));
- entriesSortedByCount = results.ncCount.entrySet();
- it = entriesSortedByCount.iterator();
- while (it.hasNext()) {
- Entry type = it.next();
- reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks");
- }
- reportLines.add(LINE_BREAK);
-
- return reportLines;
+ 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", results.rawBlockCount.get()));
+ reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
+ reportLines.add("Level cost = " + addon.getSettings().getLevelCost());
+ reportLines.add("Deaths handicap = " + results.deathHandicap.get());
+ if (addon.getSettings().isZeroNewIslandLevels()) {
+ reportLines.add("Initial island level = " + (0L - addon.getManager().getInitialLevel(island)));
+ }
+ reportLines.add("Previous level = " + addon.getManager().getIslandLevel(island.getWorld(), island.getOwner()));
+ reportLines.add("New level = " + results.getLevel());
+ reportLines.add(LINE_BREAK);
+ int total = 0;
+ if (!results.uwCount.isEmpty()) {
+ reportLines.add("Underwater block count (Multiplier = x" + addon.getSettings().getUnderWaterMultiplier()
+ + ") value");
+ reportLines.add("Total number of underwater blocks = " + String.format("%,d", results.uwCount.size()));
+ reportLines.addAll(sortedReport(total, results.uwCount));
+ }
+ reportLines.add("Regular block count");
+ reportLines.add("Total number of blocks = " + String.format("%,d", results.mdCount.size()));
+ reportLines.addAll(sortedReport(total, results.mdCount));
+
+ reportLines.add(
+ "Blocks not counted because they exceeded limits: " + String.format("%,d", results.ofCount.size()));
+ Iterable> entriesSortedByCount = results.ofCount.entrySet();
+ Iterator> it = entriesSortedByCount.iterator();
+ while (it.hasNext()) {
+ Entry type = it.next();
+ Integer limit = addon.getBlockConfig().getBlockLimits().get(type.getElement());
+ String explain = ")";
+ if (limit == null) {
+ Material generic = type.getElement();
+ limit = addon.getBlockConfig().getBlockLimits().get(generic);
+ explain = " - All types)";
+ }
+ reportLines.add(type.getElement().toString() + ": " + String.format("%,d", type.getCount())
+ + " blocks (max " + limit + explain);
+ }
+ reportLines.add(LINE_BREAK);
+ reportLines.add("Blocks on island that are not in config.yml");
+ reportLines.add("Total number = " + String.format("%,d", results.ncCount.size()));
+ entriesSortedByCount = results.ncCount.entrySet();
+ it = entriesSortedByCount.iterator();
+ while (it.hasNext()) {
+ Entry type = it.next();
+ reportLines.add(type.getElement().toString() + ": " + String.format("%,d", type.getCount()) + " blocks");
+ }
+ reportLines.add(LINE_BREAK);
+
+ return reportLines;
}
/**
* @return the results
*/
public Results getResults() {
- return results;
+ return results;
}
+
/**
- * Get value of a material
- * World blocks trump regular block values
+ * Get value of a material World blocks trump regular block values
+ *
* @param md - Material to check
* @return value of a material
*/
private int getValue(Material md) {
- Integer value = addon.getBlockConfig().getValue(island.getWorld(), md);
- if (value == null) {
- // Not in config
- results.ncCount.add(md);
- return 0;
- }
- return value;
+ Integer value = addon.getBlockConfig().getValue(island.getWorld(), md);
+ if (value == null) {
+ // Not in config
+ results.ncCount.add(md);
+ return 0;
+ }
+ return value;
}
/**
* Get a chunk async
- * @param env - the environment
+ *
+ * @param env - the environment
* @param pairList - chunk coordinate
- * @return a future chunk or future null if there is no chunk to load, e.g., there is no island nether
+ * @return a future chunk or future null if there is no chunk to load, e.g.,
+ * there is no island nether
*/
private CompletableFuture> getWorldChunk(Environment env, Queue> pairList) {
- if (worlds.containsKey(env)) {
- CompletableFuture> r2 = new CompletableFuture<>();
- List chunkList = new ArrayList<>();
- World world = worlds.get(env);
- // Get the chunk, and then coincidentally check the RoseStacker
- loadChunks(r2, world, pairList, chunkList);
- return r2;
- }
- return CompletableFuture.completedFuture(Collections.emptyList());
+ if (worlds.containsKey(env)) {
+ CompletableFuture> r2 = new CompletableFuture<>();
+ List chunkList = new ArrayList<>();
+ World world = worlds.get(env);
+ // Get the chunk, and then coincidentally check the RoseStacker
+ loadChunks(r2, world, pairList, chunkList);
+ return r2;
+ }
+ return CompletableFuture.completedFuture(Collections.emptyList());
}
private void loadChunks(CompletableFuture> r2, World world, Queue> pairList,
- List chunkList) {
- if (pairList.isEmpty()) {
- r2.complete(chunkList);
- return;
- }
- Pair p = pairList.poll();
- Util.getChunkAtAsync(world, p.x, p.z, world.getEnvironment().equals(Environment.NETHER)).thenAccept(chunk -> {
- if (chunk != null) {
- chunkList.add(chunk);
- roseStackerCheck(chunk);
- }
- loadChunks(r2, world, pairList, chunkList); // Iteration
- });
+ List chunkList) {
+ if (pairList.isEmpty()) {
+ r2.complete(chunkList);
+ return;
+ }
+ Pair p = pairList.poll();
+ Util.getChunkAtAsync(world, p.x, p.z, world.getEnvironment().equals(Environment.NETHER)).thenAccept(chunk -> {
+ if (chunk != null) {
+ chunkList.add(chunk);
+ roseStackerCheck(chunk);
+ }
+ loadChunks(r2, world, pairList, chunkList); // Iteration
+ });
}
private void roseStackerCheck(Chunk chunk) {
- if (addon.isRoseStackersEnabled()) {
- RoseStackerAPI.getInstance().getStackedBlocks(Collections.singletonList(chunk)).forEach(e -> {
- // Blocks below sea level can be scored differently
- boolean belowSeaLevel = seaHeight > 0 && e.getLocation().getY() <= seaHeight;
- // Check block once because the base block will be counted in the chunk snapshot
- for (int _x = 0; _x < e.getStackSize() - 1; _x++) {
- checkBlock(e.getBlock().getType(), belowSeaLevel);
- }
- });
- }
+ if (addon.isRoseStackersEnabled()) {
+ RoseStackerAPI.getInstance().getStackedBlocks(Collections.singletonList(chunk)).forEach(e -> {
+ // Blocks below sea level can be scored differently
+ boolean belowSeaLevel = seaHeight > 0 && e.getLocation().getY() <= seaHeight;
+ // Check block once because the base block will be counted in the chunk snapshot
+ for (int _x = 0; _x < e.getStackSize() - 1; _x++) {
+ checkBlock(e.getBlock().getType(), belowSeaLevel);
+ }
+ });
+ }
}
/**
- * Checks if a block has been limited or not and whether a block has any value or not
+ * Checks if a block has been limited or not and whether a block has any value
+ * or not
+ *
* @param md Material
* @return value of the block if can be counted
*/
private int limitCount(Material md) {
- if (limitCount.containsKey(md)) {
- int count = limitCount.get(md);
- if (count > 0) {
- limitCount.put(md, --count);
- return getValue(md);
- } else {
- results.ofCount.add(md);
- return 0;
- }
- }
- return getValue(md);
+ if (limitCount.containsKey(md)) {
+ int count = limitCount.get(md);
+ if (count > 0) {
+ limitCount.put(md, --count);
+ return getValue(md);
+ } else {
+ results.ofCount.add(md);
+ return 0;
+ }
+ }
+ return getValue(md);
}
-
/**
* Scan all containers in a chunk and count their blocks
+ *
* @param chunk - the chunk to scan
*/
private void scanChests(Chunk chunk) {
- // Count blocks in chests
- for (BlockState bs : chunk.getTileEntities()) {
- if (bs instanceof Container container) {
- if (addon.isAdvChestEnabled()) {
- AdvancedChest,?> aChest = AdvancedChestsAPI.getChestManager().getAdvancedChest(bs.getLocation());
- if (aChest != null && aChest.getChestType().getName().equals("NORMAL")) {
- aChest.getPages().stream().map(ChestPage::getItems).forEach(c -> {
- for (Object i : c) {
- countItemStack((ItemStack)i);
- }
- });
- continue;
- }
- }
- // Regular chest
- container.getSnapshotInventory().forEach(this::countItemStack);
- }
- }
+ // Count blocks in chests
+ for (BlockState bs : chunk.getTileEntities()) {
+ if (bs instanceof Container container) {
+ if (addon.isAdvChestEnabled()) {
+ AdvancedChest, ?> aChest = AdvancedChestsAPI.getChestManager().getAdvancedChest(bs.getLocation());
+ if (aChest != null && aChest.getChestType().getName().equals("NORMAL")) {
+ aChest.getPages().stream().map(ChestPage::getItems).forEach(c -> {
+ for (Object i : c) {
+ countItemStack((ItemStack) i);
+ }
+ });
+ continue;
+ }
+ }
+ // Regular chest
+ container.getSnapshotInventory().forEach(this::countItemStack);
+ }
+ }
}
private void countItemStack(ItemStack i) {
- if (i == null || !i.getType().isBlock()) return;
-
- for (int c = 0; c < i.getAmount(); c++) {
- if (addon.getSettings().isIncludeShulkersInChest()
- && i.getItemMeta() instanceof BlockStateMeta blockStateMeta
- && blockStateMeta.getBlockState() instanceof ShulkerBox shulkerBox) {
- shulkerBox.getSnapshotInventory().forEach(this::countItemStack);
- }
-
- checkBlock(i.getType(), false);
- }
+ if (i == null || !i.getType().isBlock())
+ return;
+
+ for (int c = 0; c < i.getAmount(); c++) {
+ if (addon.getSettings().isIncludeShulkersInChest()
+ && i.getItemMeta() instanceof BlockStateMeta blockStateMeta
+ && blockStateMeta.getBlockState() instanceof ShulkerBox shulkerBox) {
+ shulkerBox.getSnapshotInventory().forEach(this::countItemStack);
+ }
+
+ checkBlock(i.getType(), false);
+ }
}
/**
- * Scan the chunk chests and count the blocks. Note that the chunks are a list of all the island chunks
- * in a particular world, so the memory usage is high, but I think most servers can handle it.
+ * Scan the chunk chests and count the blocks. Note that the chunks are a list
+ * of all the island chunks in a particular world, so the memory usage is high,
+ * but I think most servers can handle it.
+ *
* @param chunks - a list of chunks to scan
- * @return future that completes when the scan is done and supplies a boolean that will be true if the scan was successful, false if not
+ * @return future that completes when the scan is done and supplies a boolean
+ * that will be true if the scan was successful, false if not
*/
private CompletableFuture scanChunk(List chunks) {
- // If the chunk hasn't been generated, return
- if (chunks == null || chunks.isEmpty()) {
- return CompletableFuture.completedFuture(false);
- }
- // Count blocks in chunk
- CompletableFuture result = new CompletableFuture<>();
- /*
- * At this point, we need to grab a snapshot of each chunk and then scan it async.
- * At the end, we make the CompletableFuture true to show it is done.
- * I'm not sure how much lag this will cause, but as all the chunks are loaded, maybe not that much.
- */
- List preLoad = chunks.stream().map(c -> new ChunkPair(c.getWorld(), c, c.getChunkSnapshot())).toList();
- Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
- preLoad.forEach(this::scanAsync);
- // Once they are all done, return to the main thread.
- Bukkit.getScheduler().runTask(addon.getPlugin(),() -> result.complete(true));
- });
- return result;
+ // If the chunk hasn't been generated, return
+ if (chunks == null || chunks.isEmpty()) {
+ return CompletableFuture.completedFuture(false);
+ }
+ // Count blocks in chunk
+ CompletableFuture result = new CompletableFuture<>();
+ /*
+ * At this point, we need to grab a snapshot of each chunk and then scan it
+ * async. At the end, we make the CompletableFuture true to show it is done. I'm
+ * not sure how much lag this will cause, but as all the chunks are loaded,
+ * maybe not that much.
+ */
+ List preLoad = chunks.stream().map(c -> new ChunkPair(c.getWorld(), c, c.getChunkSnapshot()))
+ .toList();
+ Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
+ preLoad.forEach(this::scanAsync);
+ // Once they are all done, return to the main thread.
+ Bukkit.getScheduler().runTask(addon.getPlugin(), () -> result.complete(true));
+ });
+ return result;
}
- record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {}
+ record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {
+ }
/**
* Count the blocks on the island
+ *
* @param cp chunk to scan
*/
private void scanAsync(ChunkPair cp) {
- for (int x = 0; x< 16; x++) {
- // Check if the block coordinate is inside the protection zone and if not, don't count it
- if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
- continue;
- }
- for (int z = 0; z < 16; z++) {
- // Check if the block coordinate is inside the protection zone and if not, don't count it
- if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
- continue;
- }
- // Only count to the highest block in the world for some optimization
- for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
- BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
- boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
- // Slabs can be doubled, so check them twice
- if (Tag.SLABS.isTagged(blockData.getMaterial())) {
- Slab slab = (Slab)blockData;
- if (slab.getType().equals(Slab.Type.DOUBLE)) {
- checkBlock(blockData.getMaterial(), belowSeaLevel);
- }
- }
- // Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real chunk
- if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON) || blockData.getMaterial().equals(Material.SPAWNER))) {
- stackedBlocks.add(new Location(cp.world, (double)x + cp.chunkSnapshot.getX() * 16, y, (double)z + cp.chunkSnapshot.getZ() * 16));
- }
-
- Block block = cp.chunk.getBlock(x, y, z);
-
- if (addon.isUltimateStackerEnabled()) {
- if (!blockData.getMaterial().equals(Material.AIR)) {
- BlockStack stack = UltimateStacker.getInstance().getBlockStackManager().getBlock(block, CompatibleMaterial.getMaterial(block));
- if (stack != null) {
- int value = limitCount(blockData.getMaterial());
- if (belowSeaLevel) {
- results.underWaterBlockCount.addAndGet((long) stack.getAmount() * value);
- results.uwCount.add(blockData.getMaterial());
- } else {
- results.rawBlockCount.addAndGet((long) stack.getAmount() * value);
- results.mdCount.add(blockData.getMaterial());
- }
- }
- }
- }
-
- // Scan chests
- if (addon.getSettings().isIncludeChests() && CHESTS.contains(blockData.getMaterial())) {
- chestBlocks.add(cp.chunk);
- }
- // Add the value of the block's material
- checkBlock(blockData.getMaterial(), belowSeaLevel);
- }
- }
- }
+ for (int x = 0; x < 16; x++) {
+ // Check if the block coordinate is inside the protection zone and if not, don't
+ // count it
+ if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16
+ + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
+ continue;
+ }
+ for (int z = 0; z < 16; z++) {
+ // Check if the block coordinate is inside the protection zone and if not, don't
+ // count it
+ if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16
+ + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
+ continue;
+ }
+ // Only count to the highest block in the world for some optimization
+ for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
+ BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
+ boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
+ // Slabs can be doubled, so check them twice
+ if (Tag.SLABS.isTagged(blockData.getMaterial())) {
+ Slab slab = (Slab) blockData;
+ if (slab.getType().equals(Slab.Type.DOUBLE)) {
+ checkBlock(blockData.getMaterial(), belowSeaLevel);
+ }
+ }
+ // Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
+ // chunk
+ if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON)
+ || blockData.getMaterial().equals(Material.SPAWNER))) {
+ stackedBlocks.add(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y,
+ (double) z + cp.chunkSnapshot.getZ() * 16));
+ }
+
+ Block block = cp.chunk.getBlock(x, y, z);
+
+ if (addon.isUltimateStackerEnabled()) {
+ if (!blockData.getMaterial().equals(Material.AIR)) {
+ BlockStack stack = UltimateStacker.getInstance().getBlockStackManager().getBlock(block,
+ CompatibleMaterial.getMaterial(block));
+ if (stack != null) {
+ int value = limitCount(blockData.getMaterial());
+ if (belowSeaLevel) {
+ results.underWaterBlockCount.addAndGet((long) stack.getAmount() * value);
+ results.uwCount.add(blockData.getMaterial());
+ } else {
+ results.rawBlockCount.addAndGet((long) stack.getAmount() * value);
+ results.mdCount.add(blockData.getMaterial());
+ }
+ }
+ }
+ }
+
+ // Scan chests
+ if (addon.getSettings().isIncludeChests() && CHESTS.contains(blockData.getMaterial())) {
+ chestBlocks.add(cp.chunk);
+ }
+ // Add the value of the block's material
+ checkBlock(blockData.getMaterial(), belowSeaLevel);
+ }
+ }
+ }
}
/**
* Scan the next chunk on the island
- * @return completable boolean future that will be true if more chunks are left to be scanned, and false if not
+ *
+ * @return completable boolean future that will be true if more chunks are left
+ * to be scanned, and false if not
*/
public CompletableFuture scanNextChunk() {
- if (chunksToCheck.isEmpty()) {
- addon.logError("Unexpected: no chunks to scan!");
- // This should not be needed, but just in case
- return CompletableFuture.completedFuture(false);
- }
- // Retrieve and remove from the queue
- Queue> pairList = new ConcurrentLinkedQueue<>();
- int i = 0;
- while (!chunksToCheck.isEmpty() && i++ < CHUNKS_TO_SCAN) {
- pairList.add(chunksToCheck.poll());
- }
- Queue> endPairList = new ConcurrentLinkedQueue<>(pairList);
- Queue> netherPairList = new ConcurrentLinkedQueue<>(pairList);
- // Set up the result
- CompletableFuture result = new CompletableFuture<>();
- // Get chunks and scan
- // Get chunks and scan
- getWorldChunk(Environment.THE_END, endPairList).thenAccept(endChunks ->
- scanChunk(endChunks).thenAccept(b ->
- getWorldChunk(Environment.NETHER, netherPairList).thenAccept(netherChunks ->
- scanChunk(netherChunks).thenAccept(b2 ->
- getWorldChunk(Environment.NORMAL, pairList).thenAccept(normalChunks ->
- scanChunk(normalChunks).thenAccept(b3 ->
- // Complete the result now that all chunks have been scanned
- result.complete(!chunksToCheck.isEmpty()))))
- )
- )
- );
-
- return result;
+ if (chunksToCheck.isEmpty()) {
+ addon.logError("Unexpected: no chunks to scan!");
+ // This should not be needed, but just in case
+ return CompletableFuture.completedFuture(false);
+ }
+ // Retrieve and remove from the queue
+ Queue> pairList = new ConcurrentLinkedQueue<>();
+ int i = 0;
+ while (!chunksToCheck.isEmpty() && i++ < CHUNKS_TO_SCAN) {
+ pairList.add(chunksToCheck.poll());
+ }
+ Queue> endPairList = new ConcurrentLinkedQueue<>(pairList);
+ Queue> netherPairList = new ConcurrentLinkedQueue<>(pairList);
+ // Set up the result
+ CompletableFuture result = new CompletableFuture<>();
+ // Get chunks and scan
+ // Get chunks and scan
+ getWorldChunk(Environment.THE_END, endPairList).thenAccept(
+ endChunks -> scanChunk(endChunks).thenAccept(b -> getWorldChunk(Environment.NETHER, netherPairList)
+ .thenAccept(netherChunks -> scanChunk(netherChunks)
+ .thenAccept(b2 -> getWorldChunk(Environment.NORMAL, pairList)
+ .thenAccept(normalChunks -> scanChunk(normalChunks).thenAccept(b3 ->
+ // Complete the result now that all chunks have been scanned
+ result.complete(!chunksToCheck.isEmpty())))))));
+
+ return result;
}
private Collection sortedReport(int total, Multiset materialCount) {
- Collection result = new ArrayList<>();
- Iterable> entriesSortedByCount = Multisets.copyHighestCountFirst(materialCount).entrySet();
- for (Entry en : entriesSortedByCount) {
- Material type = en.getElement();
-
- int value = getValue(type);
-
- result.add(type.toString() + ":"
- + String.format("%,d", en.getCount()) + " blocks x " + value + " = " + (value * en.getCount()));
- total += (value * en.getCount());
-
- }
- result.add("Subtotal = " + total);
- result.add(LINE_BREAK);
- return result;
+ Collection result = new ArrayList<>();
+ Iterable> entriesSortedByCount = Multisets.copyHighestCountFirst(materialCount)
+ .entrySet();
+ for (Entry en : entriesSortedByCount) {
+ Material type = en.getElement();
+
+ int value = getValue(type);
+
+ result.add(type.toString() + ":" + String.format("%,d", en.getCount()) + " blocks x " + value + " = "
+ + (value * en.getCount()));
+ total += (value * en.getCount());
+
+ }
+ result.add("Subtotal = " + total);
+ result.add(LINE_BREAK);
+ return result;
}
-
/**
* Finalizes the calculations and makes the report
*/
public void tidyUp() {
- // Finalize calculations
- results.rawBlockCount.addAndGet((long)(results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
-
- // Set the death penalty
- if (this.addon.getSettings().isSumTeamDeaths())
- {
- for (UUID uuid : this.island.getMemberSet())
- {
- this.results.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(island.getWorld(), uuid));
- }
- }
- else
- {
- // At this point, it may be that the island has become unowned.
- this.results.deathHandicap.set(this.island.getOwner() == null ? 0 :
- this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
- }
-
- long blockAndDeathPoints = this.results.rawBlockCount.get();
- this.results.totalPoints.set(blockAndDeathPoints);
-
- if (this.addon.getSettings().getDeathPenalty() > 0)
- {
- // Proper death penalty calculation.
- blockAndDeathPoints -= this.results.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
- }
- this.results.level.set(calculateLevel(blockAndDeathPoints));
-
- // Calculate how many points are required to get to the next level
- long nextLevel = this.results.level.get();
- long blocks = blockAndDeathPoints;
- while (nextLevel < this.results.level.get() + 1 && blocks - blockAndDeathPoints < MAX_AMOUNT) {
- nextLevel = calculateLevel(++blocks);
- }
- this.results.pointsToNextLevel.set(blocks - blockAndDeathPoints);
-
- // Report
- results.report = getReport();
- // Set the duration
- addon.getPipeliner().setTime(System.currentTimeMillis() - duration);
- // All done.
+ // Finalize calculations
+ results.rawBlockCount
+ .addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
+
+ // Set the death penalty
+ if (this.addon.getSettings().isSumTeamDeaths()) {
+ for (UUID uuid : this.island.getMemberSet()) {
+ this.results.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(island.getWorld(), uuid));
+ }
+ } else {
+ // At this point, it may be that the island has become unowned.
+ this.results.deathHandicap.set(this.island.getOwner() == null ? 0
+ : this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
+ }
+
+ long blockAndDeathPoints = this.results.rawBlockCount.get();
+ this.results.totalPoints.set(blockAndDeathPoints);
+
+ if (this.addon.getSettings().getDeathPenalty() > 0) {
+ // Proper death penalty calculation.
+ blockAndDeathPoints -= this.results.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
+ }
+ this.results.level.set(calculateLevel(blockAndDeathPoints));
+
+ // Calculate how many points are required to get to the next level
+ long nextLevel = this.results.level.get();
+ long blocks = blockAndDeathPoints;
+ while (nextLevel < this.results.level.get() + 1 && blocks - blockAndDeathPoints < MAX_AMOUNT) {
+ nextLevel = calculateLevel(++blocks);
+ }
+ this.results.pointsToNextLevel.set(blocks - blockAndDeathPoints);
+
+ // Report
+ results.report = getReport();
+ // Set the duration
+ addon.getPipeliner().setTime(System.currentTimeMillis() - duration);
+ // All done.
}
/**
* @return the zeroIsland
*/
boolean isNotZeroIsland() {
- return !zeroIsland;
+ return !zeroIsland;
}
public void scanIsland(Pipeliner pipeliner) {
- // Scan the next chunk
- scanNextChunk().thenAccept(result -> {
- if (!Bukkit.isPrimaryThread()) {
- addon.getPlugin().logError("scanChunk not on Primary Thread!");
- }
- // Timeout check
- if (System.currentTimeMillis() - pipeliner.getInProcessQueue().get(this) > addon.getSettings().getCalculationTimeout() * 60000) {
- // Done
- pipeliner.getInProcessQueue().remove(this);
- getR().complete(new Results(Result.TIMEOUT));
- addon.logError("Level calculation timed out after " + addon.getSettings().getCalculationTimeout() + "m for island: " + getIsland());
- if (!isNotZeroIsland()) {
- addon.logError("Island level was being zeroed.");
- }
- return;
- }
- if (Boolean.TRUE.equals(result) && !pipeliner.getTask().isCancelled()) {
- // scanNextChunk returns true if there are more chunks to scan
- scanIsland(pipeliner);
- } else {
- // Done
- pipeliner.getInProcessQueue().remove(this);
- // Chunk finished
- // This was the last chunk
- handleStackedBlocks();
- handleChests();
- long checkTime = System.currentTimeMillis();
- finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
- // Check every half second if all the chests and stacks have been cleared
- if ((chestBlocks.isEmpty() && stackedBlocks.isEmpty()) || System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
- this.tidyUp();
- this.getR().complete(getResults());
- finishTask.cancel();
- }
- }, 0, 10L);
-
- }
- });
+ // Scan the next chunk
+ scanNextChunk().thenAccept(result -> {
+ if (!Bukkit.isPrimaryThread()) {
+ addon.getPlugin().logError("scanChunk not on Primary Thread!");
+ }
+ // Timeout check
+ if (System.currentTimeMillis()
+ - pipeliner.getInProcessQueue().get(this) > addon.getSettings().getCalculationTimeout() * 60000) {
+ // Done
+ pipeliner.getInProcessQueue().remove(this);
+ getR().complete(new Results(Result.TIMEOUT));
+ addon.logError("Level calculation timed out after " + addon.getSettings().getCalculationTimeout()
+ + "m for island: " + getIsland());
+ if (!isNotZeroIsland()) {
+ addon.logError("Island level was being zeroed.");
+ }
+ return;
+ }
+ if (Boolean.TRUE.equals(result) && !pipeliner.getTask().isCancelled()) {
+ // scanNextChunk returns true if there are more chunks to scan
+ scanIsland(pipeliner);
+ } else {
+ // Done
+ pipeliner.getInProcessQueue().remove(this);
+ // Chunk finished
+ // This was the last chunk
+ handleStackedBlocks();
+ handleChests();
+ long checkTime = System.currentTimeMillis();
+ finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
+ // Check every half second if all the chests and stacks have been cleared
+ if ((chestBlocks.isEmpty() && stackedBlocks.isEmpty())
+ || System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
+ this.tidyUp();
+ this.getR().complete(getResults());
+ finishTask.cancel();
+ }
+ }, 0, 10L);
+
+ }
+ });
}
private void handleChests() {
- Iterator it = chestBlocks.iterator();
- while(it.hasNext()) {
- Chunk v = it.next();
- Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
- scanChests(c);
- it.remove();
- });
- }
+ Iterator it = chestBlocks.iterator();
+ while (it.hasNext()) {
+ Chunk v = it.next();
+ Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
+ scanChests(c);
+ it.remove();
+ });
+ }
}
private void handleStackedBlocks() {
- // Deal with any stacked blocks
- Iterator it = stackedBlocks.iterator();
- while (it.hasNext()) {
- Location v = it.next();
- Util.getChunkAtAsync(v).thenAccept(c -> {
- Block stackedBlock = v.getBlock();
- boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
- if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
- StackedBarrel barrel = WildStackerAPI.getStackedBarrel(stackedBlock);
- int barrelAmt = WildStackerAPI.getBarrelAmount(stackedBlock);
- for (int _x = 0; _x < barrelAmt; _x++) {
- checkBlock(barrel.getType(), belowSeaLevel);
- }
- } else if (WildStackerAPI.getWildStacker().getSystemManager().isStackedSpawner(stackedBlock)) {
- int spawnerAmt = WildStackerAPI.getSpawnersAmount((CreatureSpawner) stackedBlock.getState());
- for (int _x = 0; _x < spawnerAmt; _x++) {
- checkBlock(stackedBlock.getType(), belowSeaLevel);
- }
- }
- it.remove();
- });
- }
+ // Deal with any stacked blocks
+ Iterator it = stackedBlocks.iterator();
+ while (it.hasNext()) {
+ Location v = it.next();
+ Util.getChunkAtAsync(v).thenAccept(c -> {
+ Block stackedBlock = v.getBlock();
+ boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
+ if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
+ StackedBarrel barrel = WildStackerAPI.getStackedBarrel(stackedBlock);
+ int barrelAmt = WildStackerAPI.getBarrelAmount(stackedBlock);
+ for (int _x = 0; _x < barrelAmt; _x++) {
+ checkBlock(barrel.getType(), belowSeaLevel);
+ }
+ } else if (WildStackerAPI.getWildStacker().getSystemManager().isStackedSpawner(stackedBlock)) {
+ int spawnerAmt = WildStackerAPI.getSpawnersAmount((CreatureSpawner) stackedBlock.getState());
+ for (int _x = 0; _x < spawnerAmt; _x++) {
+ checkBlock(stackedBlock.getType(), belowSeaLevel);
+ }
+ }
+ it.remove();
+ });
+ }
}
}
diff --git a/src/main/java/world/bentobox/level/commands/AdminStatsCommand.java b/src/main/java/world/bentobox/level/commands/AdminStatsCommand.java
new file mode 100644
index 0000000..180c41e
--- /dev/null
+++ b/src/main/java/world/bentobox/level/commands/AdminStatsCommand.java
@@ -0,0 +1,98 @@
+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.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 args) {
+ user.sendMessage("admin.stats.title");
+ Map topTenLists = level.getManager().getTopTenLists();
+ if (topTenLists.isEmpty()) {
+ user.sendMessage("admin.stats.no-data");
+ return false;
+ }
+ for (Entry en : topTenLists.entrySet()) {
+ user.sendMessage("admin.stats.world", TextVariables.NAME,
+ level.getPlugin().getIWM().getWorldName(en.getKey()));
+ Map 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 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 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 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 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;
+ }
+}
diff --git a/src/main/java/world/bentobox/level/commands/AdminTopCommand.java b/src/main/java/world/bentobox/level/commands/AdminTopCommand.java
index 9144107..9287e14 100644
--- a/src/main/java/world/bentobox/level/commands/AdminTopCommand.java
+++ b/src/main/java/world/bentobox/level/commands/AdminTopCommand.java
@@ -2,7 +2,7 @@
import java.util.List;
import java.util.Map;
-import java.util.UUID;
+import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
@@ -14,36 +14,32 @@ public class AdminTopCommand extends CompositeCommand {
private final Level levelPlugin;
public AdminTopCommand(Level addon, CompositeCommand parent) {
- super(parent, "top", "topten");
- this.levelPlugin = addon;
- new AdminTopRemoveCommand(addon, this);
+ super(parent, "top", "topten");
+ this.levelPlugin = addon;
+ new AdminTopRemoveCommand(addon, this);
}
@Override
public void setup() {
- this.setPermission("admin.top");
- this.setOnlyPlayer(false);
- this.setDescription("admin.top.description");
+ this.setPermission("admin.top");
+ this.setOnlyPlayer(false);
+ this.setDescription("admin.top.description");
}
@Override
public boolean execute(User user, String label, List args) {
- user.sendMessage("island.top.gui-title");
- int rank = 0;
- for (Map.Entry topTen : levelPlugin.getManager().getTopTen(getWorld(), Level.TEN).entrySet()) {
- Island island = getPlugin().getIslands().getIsland(getWorld(), topTen.getKey());
- if (island != null) {
- rank++;
- user.sendMessage("admin.top.display",
- "[rank]",
- String.valueOf(rank),
- "[name]",
- this.getPlugin().getPlayers().getUser(island.getOwner()).getName(),
- "[level]",
- String.valueOf(topTen.getValue()));
- }
- }
-
- return true;
+ user.sendMessage("island.top.gui-title");
+ int rank = 0;
+ for (Map.Entry topTen : levelPlugin.getManager().getTopTen(getWorld(), Level.TEN).entrySet()) {
+ Optional is = getPlugin().getIslands().getIslandById(topTen.getKey());
+ if (is.isPresent()) {
+ Island island = is.get();
+ rank++;
+ user.sendMessage("admin.top.display", "[rank]", String.valueOf(rank), "[name]",
+ this.getPlugin().getPlayers().getUser(island.getOwner()).getName(), "[level]",
+ String.valueOf(topTen.getValue()));
+ }
+ }
+ return true;
}
}
diff --git a/src/main/java/world/bentobox/level/commands/AdminTopRemoveCommand.java b/src/main/java/world/bentobox/level/commands/AdminTopRemoveCommand.java
index b54ca3e..1c51fb9 100644
--- a/src/main/java/world/bentobox/level/commands/AdminTopRemoveCommand.java
+++ b/src/main/java/world/bentobox/level/commands/AdminTopRemoveCommand.java
@@ -6,10 +6,12 @@
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.level.Level;
/**
* Removes a player from the top ten
+ *
* @author tastybento
*
*/
@@ -19,46 +21,53 @@ public class AdminTopRemoveCommand extends CompositeCommand {
private User target;
public AdminTopRemoveCommand(Level addon, CompositeCommand parent) {
- super(parent, "remove", "delete");
- this.addon = addon;
+ 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");
+ 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)
+ /*
+ * (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;
- }
+ 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;
+ return true;
}
@Override
public boolean execute(User user, String label, List args) {
- addon.getManager().removeEntry(getWorld(), target.getUniqueId());
- user.sendMessage("general.success");
- return true;
+ // Removes islands that this target is an owner of
+ getIslands().getIslands(getWorld(), target.getUniqueId()).stream()
+ .filter(is -> target.getUniqueId().equals(is.getOwner()))
+ .forEach(island -> addon.getManager().removeEntry(getWorld(), island.getUniqueId()));
+ user.sendMessage("general.success");
+ return true;
}
@Override
public Optional> tabComplete(User user, String alias, List args) {
- return Optional.of(addon.getManager().getTopTen(getWorld(), Level.TEN).keySet().stream().map(addon.getPlayers()::getName)
- .filter(n -> !n.isEmpty()).toList());
+ return Optional.of(addon.getManager().getTopTen(getWorld(), Level.TEN).keySet().stream()
+ .map(getIslands()::getIslandById).flatMap(Optional::stream).map(Island::getOwner)
+ .map(addon.getPlayers()::getName).filter(n -> !n.isEmpty()).toList());
}
}
diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java
index 2180048..efcb3ee 100644
--- a/src/main/java/world/bentobox/level/config/BlockConfig.java
+++ b/src/main/java/world/bentobox/level/config/BlockConfig.java
@@ -60,10 +60,16 @@ private void loadWorlds(YamlConfiguration blockValues2) {
if (bWorld != null) {
ConfigurationSection worldValues = worlds.getConfigurationSection(world);
for (String material : Objects.requireNonNull(worldValues).getKeys(false)) {
- Material mat = Material.valueOf(material);
- Map values = worldBlockValues.getOrDefault(bWorld, new EnumMap<>(Material.class));
- values.put(mat, worldValues.getInt(material));
- worldBlockValues.put(bWorld, values);
+ try {
+ Material mat = Material.valueOf(material);
+ Map values = worldBlockValues.getOrDefault(bWorld,
+ new EnumMap<>(Material.class));
+ values.put(mat, worldValues.getInt(material));
+ worldBlockValues.put(bWorld, values);
+ } catch (Exception e) {
+ addon.logError(
+ "Unknown material (" + material + ") in blockconfig.yml worlds section. Skipping...");
+ }
}
} else {
addon.logWarning("Level Addon: No such world in blockconfig.yml : " + world);
@@ -97,7 +103,7 @@ private Map loadBlockLimits(YamlConfiguration blockValues2) {
Material mat = Material.valueOf(material);
bl.put(mat, limits.getInt(material, 0));
} catch (Exception e) {
- addon.logWarning("Unknown material (" + material + ") in blockconfig.yml Limits section. Skipping...");
+ addon.logError("Unknown material (" + material + ") in blockconfig.yml Limits section. Skipping...");
}
}
return bl;
diff --git a/src/main/java/world/bentobox/level/config/ConfigSettings.java b/src/main/java/world/bentobox/level/config/ConfigSettings.java
index 7be3364..c287d2a 100644
--- a/src/main/java/world/bentobox/level/config/ConfigSettings.java
+++ b/src/main/java/world/bentobox/level/config/ConfigSettings.java
@@ -1,5 +1,6 @@
package world.bentobox.level.config;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -126,6 +127,12 @@ public class ConfigSettings implements ConfigObject {
@ConfigEntry(path = "include-shulkers-in-chest")
private boolean includeShulkersInChest = false;
+ @ConfigComment("")
+ @ConfigComment("Disables hooking with other plugins.")
+ @ConfigComment("Example: disabled-plugin-hooks: [UltimateStacker, RoseStacker]")
+ @ConfigEntry(path = "disabled-plugin-hooks")
+ private List disabledPluginHooks = new ArrayList<>();
+
/**
* @return the gameModes
@@ -404,4 +411,12 @@ public boolean isIncludeShulkersInChest() {
public void setIncludeShulkersInChest(boolean includeShulkersInChest) {
this.includeShulkersInChest = includeShulkersInChest;
}
+
+ public List getDisabledPluginHooks() {
+ return disabledPluginHooks;
+ }
+
+ public void setDisabledPluginHooks(List disabledPluginHooks) {
+ this.disabledPluginHooks = disabledPluginHooks;
+ }
}
diff --git a/src/main/java/world/bentobox/level/listeners/IslandActivitiesListeners.java b/src/main/java/world/bentobox/level/listeners/IslandActivitiesListeners.java
index 05bdfc2..4a75f5c 100644
--- a/src/main/java/world/bentobox/level/listeners/IslandActivitiesListeners.java
+++ b/src/main/java/world/bentobox/level/listeners/IslandActivitiesListeners.java
@@ -1,7 +1,5 @@
package world.bentobox.level.listeners;
-import java.util.UUID;
-
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -21,7 +19,9 @@
import world.bentobox.level.Level;
/**
- * Listens for new islands or ownership changes and sets the level to zero automatically
+ * Listens for new islands or ownership changes and sets the level to zero
+ * automatically
+ *
* @author tastybento
*
*/
@@ -33,93 +33,89 @@ public class IslandActivitiesListeners implements Listener {
* @param addon - addon
*/
public IslandActivitiesListeners(Level addon) {
- this.addon = addon;
+ this.addon = addon;
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onNewIsland(IslandCreatedEvent e) {
- if (addon.getSettings().isZeroNewIslandLevels()) {
- zeroIsland(e.getIsland());
- }
+ if (addon.getSettings().isZeroNewIslandLevels()) {
+ zeroIsland(e.getIsland());
+ }
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onNewIsland(IslandResettedEvent e) {
- if (addon.getSettings().isZeroNewIslandLevels()) {
- zeroIsland(e.getIsland());
- }
+ if (addon.getSettings().isZeroNewIslandLevels()) {
+ zeroIsland(e.getIsland());
+ }
}
private void zeroIsland(final Island island) {
- // Clear the island setting
- if (island.getOwner() != null && island.getWorld() != null) {
- addon.getPipeliner().zeroIsland(island).thenAccept(results ->
- addon.getManager().setInitialIslandLevel(island, results.getLevel()));
- }
+ // Clear the island setting
+ if (island.getOwner() != null && island.getWorld() != null) {
+ addon.getPipeliner().zeroIsland(island)
+ .thenAccept(results -> addon.getManager().setInitialIslandLevel(island, results.getLevel()));
+ }
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onIslandDelete(IslandPreclearEvent e) {
-
- // Remove player from the top ten and level
- UUID uuid = e.getIsland().getOwner();
- World world = e.getIsland().getWorld();
- remove(world, uuid);
+ remove(e.getIsland().getWorld(), e.getIsland().getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onIslandDeleted(IslandDeleteEvent e) {
- // Remove island
- addon.getManager().deleteIsland(e.getIsland().getUniqueId());
+ // Remove island
+ addon.getManager().deleteIsland(e.getIsland().getUniqueId());
}
- private void remove(World world, UUID uuid) {
- if (uuid != null && world != null) {
- addon.getManager().removeEntry(world, uuid);
- }
+ private void remove(World world, String uuid) {
+ if (uuid != null && world != null) {
+ addon.getManager().removeEntry(world, uuid);
+ }
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onNewIslandOwner(TeamSetownerEvent e) {
- // Remove player from the top ten and level
- remove(e.getIsland().getWorld(), e.getIsland().getOwner());
+ // Remove island from the top ten and level
+ remove(e.getIsland().getWorld(), e.getIsland().getUniqueId());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(TeamJoinedEvent e) {
-
- // Remove player from the top ten and level
- remove(e.getIsland().getWorld(), e.getPlayerUUID());
+ // TODO: anything to do here?
+ // Remove player from the top ten and level
+ // remove(e.getIsland().getWorld(), e.getPlayerUUID());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(IslandUnregisteredEvent e) {
- // Remove player from the top ten
- remove(e.getIsland().getWorld(), e.getPlayerUUID());
+ // Remove island from the top ten
+ remove(e.getIsland().getWorld(), e.getIsland().getUniqueId());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(IslandRegisteredEvent e) {
-
- // Remove player from the top ten
- remove(e.getIsland().getWorld(), e.getPlayerUUID());
+ // TODO: anything to do here?
+ // Remove player from the top ten
+ // remove(e.getIsland().getWorld(), e.getPlayerUUID());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(TeamLeaveEvent e) {
-
- // Remove player from the top ten and level
- remove(e.getIsland().getWorld(), e.getPlayerUUID());
+ // TODO: anything to do here?
+ // Remove player from the top ten and level
+ // remove(e.getIsland().getWorld(), e.getPlayerUUID());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(TeamKickEvent e) {
-
- // Remove player from the top ten and level
- remove(e.getIsland().getWorld(), e.getPlayerUUID());
+ //// TODO: anything to do here?
+ // Remove player from the top ten and level
+ // remove(e.getIsland().getWorld(), e.getPlayerUUID());
}
}
diff --git a/src/main/java/world/bentobox/level/objects/TopTenData.java b/src/main/java/world/bentobox/level/objects/TopTenData.java
index e18e8bd..3d50b1f 100644
--- a/src/main/java/world/bentobox/level/objects/TopTenData.java
+++ b/src/main/java/world/bentobox/level/objects/TopTenData.java
@@ -3,56 +3,41 @@
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
-import java.util.UUID;
import org.bukkit.World;
import com.google.gson.annotations.Expose;
-import world.bentobox.bentobox.database.objects.DataObject;
-import world.bentobox.bentobox.database.objects.Table;
-
/**
* This class stores the top ten.
+ *
* @author tastybento
*
*/
-@Table(name = "TopTenData")
-public class TopTenData implements DataObject {
+public class TopTenData {
// UniqueId is the world name
@Expose
private String uniqueId = "";
@Expose
- private Map topTen = new LinkedHashMap<>();
+ private Map topTen = new LinkedHashMap<>();
public TopTenData(World k) {
- uniqueId = k.getName().toLowerCase(Locale.ENGLISH);
- }
-
- @Override
- public String getUniqueId() {
- // This is the world name
- return uniqueId;
+ uniqueId = k.getName().toLowerCase(Locale.ENGLISH);
}
- @Override
- public void setUniqueId(String uniqueId) {
- // This is the world name - make it always lowercase
- this.uniqueId = uniqueId.toLowerCase(Locale.ENGLISH);
- }
/**
* @return the topTen
*/
- public Map getTopTen() {
- return topTen;
+ public Map getTopTen() {
+ return topTen;
}
+
/**
* @param topTen the topTen to set
*/
- public void setTopTen(Map topTen) {
- this.topTen = topTen;
+ public void setTopTen(Map topTen) {
+ this.topTen = topTen;
}
-
}
diff --git a/src/main/java/world/bentobox/level/panels/DetailsPanel.java b/src/main/java/world/bentobox/level/panels/DetailsPanel.java
index 395e5ed..fad4988 100644
--- a/src/main/java/world/bentobox/level/panels/DetailsPanel.java
+++ b/src/main/java/world/bentobox/level/panels/DetailsPanel.java
@@ -1,6 +1,5 @@
package world.bentobox.level.panels;
-
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
@@ -30,775 +29,659 @@
import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.util.Utils;
-
/**
* This class opens GUI that shows generator view for user.
*/
-public class DetailsPanel
-{
- // ---------------------------------------------------------------------
- // Section: Internal Constructor
- // ---------------------------------------------------------------------
-
-
- /**
- * This is internal constructor. It is used internally in current class to avoid creating objects everywhere.
- *
- * @param addon Level object
- * @param world World where user is operating
- * @param user User who opens panel
- */
- private DetailsPanel(Level addon,
- World world,
- User user)
- {
- this.addon = addon;
- this.world = world;
- this.user = user;
-
- this.island = this.addon.getIslands().getIsland(world, user);
-
- if (this.island != null)
- {
- this.levelsData = this.addon.getManager().getLevelsData(this.island);
- }
- else
- {
- this.levelsData = null;
- }
-
- // By default no-filters are active.
- this.activeTab = Tab.ALL_BLOCKS;
- this.activeFilter = Filter.NAME;
- this.materialCountList = new ArrayList<>(Material.values().length);
-
- this.updateFilters();
- }
-
-
- /**
- * This method builds this GUI.
- */
- private void build()
- {
- if (this.island == null || this.levelsData == null)
- {
- // Nothing to see.
- Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-island"));
- return;
- }
-
- if (this.levelsData.getMdCount().isEmpty() && this.levelsData.getUwCount().isEmpty())
- {
- // Nothing to see.
- Utils.sendMessage(this.user, this.user.getTranslation("level.conversations.no-data"));
- return;
- }
-
- // Start building panel.
- TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
- panelBuilder.user(this.user);
- panelBuilder.world(this.user.getWorld());
-
- panelBuilder.template("detail_panel", new File(this.addon.getDataFolder(), "panels"));
-
- panelBuilder.parameters("[name]", this.user.getName());
-
- panelBuilder.registerTypeBuilder("NEXT", this::createNextButton);
- panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton);
- panelBuilder.registerTypeBuilder("BLOCK", this::createMaterialButton);
-
- panelBuilder.registerTypeBuilder("FILTER", this::createFilterButton);
-
- // Register tabs
- panelBuilder.registerTypeBuilder("TAB", this::createTabButton);
-
- // Register unknown type builder.
- panelBuilder.build();
- }
-
-
- /**
- * This method updates filter of elements based on tabs.
- */
- private void updateFilters()
- {
- this.materialCountList.clear();
-
- switch (this.activeTab)
- {
- case ALL_BLOCKS -> {
- Map materialCountMap = new EnumMap<>(Material.class);
-
- materialCountMap.putAll(this.levelsData.getMdCount());
-
- // Add underwater blocks.
- this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material,
- materialCountMap.computeIfAbsent(material, key -> 0) + count));
-
- materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).
- forEachOrdered(entry ->
- this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
- }
- case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream().sorted((Map.Entry.comparingByKey()))
- .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
-
- case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream().sorted((Map.Entry.comparingByKey()))
- .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
-
- case SPAWNER -> {
- int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0);
- int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0);
-
- // TODO: spawners need some touch...
- this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater));
- }
- }
-
- Comparator> sorter;
-
- switch (this.activeFilter)
- {
- case COUNT ->
- {
- sorter = (o1, o2) ->
- {
- if (o1.getValue().equals(o2.getValue()))
- {
- String o1Name = Utils.prettifyObject(o1.getKey(), this.user);
- String o2Name = Utils.prettifyObject(o2.getKey(), this.user);
-
- return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name);
- }
- else
- {
- return Integer.compare(o2.getValue(), o1.getValue());
- }
- };
- }
- case VALUE ->
- {
- sorter = (o1, o2) ->
- {
- int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o1.getKey(), 0);
- int o1Count = blockLimit > 0 ? Math.min(o1.getValue(), blockLimit) : o1.getValue();
-
- blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o2.getKey(), 0);
- int o2Count = blockLimit > 0 ? Math.min(o2.getValue(), blockLimit) : o2.getValue();
-
- long o1Value = (long) o1Count *
- this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.getKey(), 0);
- long o2Value = (long) o2Count *
- this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.getKey(), 0);
-
- if (o1Value == o2Value)
- {
- String o1Name = Utils.prettifyObject(o1.getKey(), this.user);
- String o2Name = Utils.prettifyObject(o2.getKey(), this.user);
-
- return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name);
- }
- else
- {
- return Long.compare(o2Value, o1Value);
- }
- };
- }
- default ->
- {
- sorter = (o1, o2) ->
- {
- String o1Name = Utils.prettifyObject(o1.getKey(), this.user);
- String o2Name = Utils.prettifyObject(o2.getKey(), this.user);
-
- return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name);
- };
- }
- }
-
- this.materialCountList.sort(sorter);
-
- this.pageIndex = 0;
- }
-
-
- // ---------------------------------------------------------------------
- // Section: Tab Button Type
- // ---------------------------------------------------------------------
-
-
- /**
- * Create tab button panel item.
- *
- * @param template the template
- * @param slot the slot
- * @return the panel item
- */
- private PanelItem createTabButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
- {
- PanelItemBuilder builder = new PanelItemBuilder();
-
- if (template.icon() != null)
- {
- // Set icon
- builder.icon(template.icon().clone());
- }
-
- if (template.title() != null)
- {
- // Set title
- builder.name(this.user.getTranslation(this.world, template.title()));
- }
-
- if (template.description() != null)
- {
- // Set description
- builder.description(this.user.getTranslation(this.world, template.description()));
- }
-
- Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.ALL_BLOCKS);
-
- // Get only possible actions, by removing all inactive ones.
- List activeActions = new ArrayList<>(template.actions());
-
- activeActions.removeIf(action ->
- "VIEW".equalsIgnoreCase(action.actionType()) && this.activeTab == tab);
-
- // Add Click handler
- builder.clickHandler((panel, user, clickType, i) ->
- {
- for (ItemTemplateRecord.ActionRecords action : activeActions)
- {
- if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType()))
- && "VIEW".equalsIgnoreCase(action.actionType()))
- {
- this.activeTab = tab;
-
- // Update filters.
- this.updateFilters();
- this.build();
- }
- }
-
- return true;
- });
-
- // Collect tooltips.
- List tooltips = activeActions.stream().
- filter(action -> action.tooltip() != null).
- map(action -> this.user.getTranslation(this.world, action.tooltip())).
- filter(text -> !text.isBlank()).
- collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
-
- // Add tooltips.
- if (!tooltips.isEmpty())
- {
- // Empty line and tooltips.
- builder.description("");
- builder.description(tooltips);
- }
-
- builder.glow(this.activeTab == tab);
-
- return builder.build();
- }
-
-
- /**
- * Create next button panel item.
- *
- * @param template the template
- * @param slot the slot
- * @return the panel item
- */
- private PanelItem createFilterButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
- {
- PanelItemBuilder builder = new PanelItemBuilder();
-
- if (template.icon() != null)
- {
- // Set icon
- builder.icon(template.icon().clone());
- }
-
- Filter filter;
-
- if (slot.amountMap().getOrDefault("FILTER", 0) > 1)
- {
- filter = Enums.getIfPresent(Filter.class, String.valueOf(template.dataMap().get("filter"))).or(Filter.NAME);
- }
- else
- {
- filter = this.activeFilter;
- }
-
- final String reference = "level.gui.buttons.filters.";
-
- if (template.title() != null)
- {
- // Set title
- builder.name(this.user.getTranslation(this.world, template.title().replace("[filter]", filter.name().toLowerCase())));
- }
- else
- {
- builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".name"));
- }
-
- if (template.description() != null)
- {
- // Set description
- builder.description(this.user.getTranslation(this.world, template.description().replace("[filter]", filter.name().toLowerCase())));
- }
- else
- {
- builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".description"));
- }
-
- // Get only possible actions, by removing all inactive ones.
- List activeActions = new ArrayList<>(template.actions());
-
- // Add Click handler
- builder.clickHandler((panel, user, clickType, i) ->
- {
- for (ItemTemplateRecord.ActionRecords action : activeActions)
- {
- if (clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType()))
- {
- if ("UP".equalsIgnoreCase(action.actionType()))
- {
- this.activeFilter = Utils.getNextValue(Filter.values(), filter);
-
- // Update filters.
- this.updateFilters();
- this.build();
- }
- else if ("DOWN".equalsIgnoreCase(action.actionType()))
- {
- this.activeFilter = Utils.getPreviousValue(Filter.values(), filter);
-
- // Update filters.
- this.updateFilters();
- this.build();
- }
- else if ("SELECT".equalsIgnoreCase(action.actionType()))
- {
- this.activeFilter = filter;
-
- // Update filters.
- this.updateFilters();
- this.build();
- }
- }
- }
-
- return true;
- });
-
- // Collect tooltips.
- List tooltips = activeActions.stream().
- filter(action -> action.tooltip() != null).
- map(action -> this.user.getTranslation(this.world, action.tooltip())).
- filter(text -> !text.isBlank()).
- collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
-
- // Add tooltips.
- if (!tooltips.isEmpty())
- {
- // Empty line and tooltips.
- builder.description("");
- builder.description(tooltips);
- }
-
- builder.glow(this.activeFilter == filter);
-
- return builder.build();
- }
-
-
- // ---------------------------------------------------------------------
- // Section: Create common buttons
- // ---------------------------------------------------------------------
-
-
- /**
- * Create next button panel item.
- *
- * @param template the template
- * @param slot the slot
- * @return the panel item
- */
- private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
- {
- long size = this.materialCountList.size();
-
- if (size <= slot.amountMap().getOrDefault("BLOCK", 1) ||
- 1.0 * size / slot.amountMap().getOrDefault("BLOCK", 1) <= this.pageIndex + 1)
- {
- // There are no next elements
- return null;
- }
-
- int nextPageIndex = this.pageIndex + 2;
-
- PanelItemBuilder builder = new PanelItemBuilder();
-
- if (template.icon() != null)
- {
- ItemStack clone = template.icon().clone();
-
- if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false)))
- {
- clone.setAmount(nextPageIndex);
- }
-
- builder.icon(clone);
- }
-
- if (template.title() != null)
- {
- builder.name(this.user.getTranslation(this.world, template.title()));
- }
-
- if (template.description() != null)
- {
- builder.description(this.user.getTranslation(this.world, template.description(),
- TextVariables.NUMBER, String.valueOf(nextPageIndex)));
- }
-
- // Add ClickHandler
- builder.clickHandler((panel, user, clickType, i) ->
- {
- for (ItemTemplateRecord.ActionRecords action : template.actions())
- {
- if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) &&
- "NEXT".equalsIgnoreCase(action.actionType()))
- {
- this.pageIndex++;
- this.build();
- }
- }
-
- // Always return true.
- return true;
- });
-
- // Collect tooltips.
- List tooltips = template.actions().stream().
- filter(action -> action.tooltip() != null).
- map(action -> this.user.getTranslation(this.world, action.tooltip())).
- filter(text -> !text.isBlank()).
- collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
-
- // Add tooltips.
- if (!tooltips.isEmpty())
- {
- // Empty line and tooltips.
- builder.description("");
- builder.description(tooltips);
- }
-
- return builder.build();
- }
-
-
- /**
- * Create previous button panel item.
- *
- * @param template the template
- * @param slot the slot
- * @return the panel item
- */
- private PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
- {
- if (this.pageIndex == 0)
- {
- // There are no next elements
- return null;
- }
-
- int previousPageIndex = this.pageIndex;
-
- PanelItemBuilder builder = new PanelItemBuilder();
-
- if (template.icon() != null)
- {
- ItemStack clone = template.icon().clone();
-
- if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false)))
- {
- clone.setAmount(previousPageIndex);
- }
-
- builder.icon(clone);
- }
-
- if (template.title() != null)
- {
- builder.name(this.user.getTranslation(this.world, template.title()));
- }
-
- if (template.description() != null)
- {
- builder.description(this.user.getTranslation(this.world, template.description(),
- TextVariables.NUMBER, String.valueOf(previousPageIndex)));
- }
-
- // Add ClickHandler
- builder.clickHandler((panel, user, clickType, i) ->
- {
- for (ItemTemplateRecord.ActionRecords action : template.actions())
- {
- if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType()))
- && "PREVIOUS".equalsIgnoreCase(action.actionType()))
- {
- this.pageIndex--;
- this.build();
- }
- }
-
- // Always return true.
- return true;
- });
-
- // Collect tooltips.
- List tooltips = template.actions().stream().
- filter(action -> action.tooltip() != null).
- map(action -> this.user.getTranslation(this.world, action.tooltip())).
- filter(text -> !text.isBlank()).
- collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
-
- // Add tooltips.
- if (!tooltips.isEmpty())
- {
- // Empty line and tooltips.
- builder.description("");
- builder.description(tooltips);
- }
-
- return builder.build();
- }
-
-
- // ---------------------------------------------------------------------
- // Section: Create Material Button
- // ---------------------------------------------------------------------
-
-
- /**
- * Create material button panel item.
- *
- * @param template the template
- * @param slot the slot
- * @return the panel item
- */
- private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
- {
- if (this.materialCountList.isEmpty())
- {
- // Does not contain any generators.
- return null;
- }
-
- int index = this.pageIndex * slot.amountMap().getOrDefault("BLOCK", 1) + slot.slot();
-
- if (index >= this.materialCountList.size())
- {
- // Out of index.
- return null;
- }
-
- return this.createMaterialButton(template, this.materialCountList.get(index));
- }
-
-
- /**
- * This method creates button for material.
- *
- * @param template the template of the button
- * @param materialCount materialCount which button must be created.
- * @return PanelItem for generator tier.
- */
- private PanelItem createMaterialButton(ItemTemplateRecord template,
- Pair materialCount)
- {
- PanelItemBuilder builder = new PanelItemBuilder();
-
- if (template.icon() != null)
- {
- builder.icon(template.icon().clone());
- }
- else
- {
- builder.icon(PanelUtils.getMaterialItem(materialCount.getKey()));
- }
-
- if (materialCount.getValue() < 64)
- {
- builder.amount(materialCount.getValue());
- }
-
- if (template.title() != null)
- {
- builder.name(this.user.getTranslation(this.world, template.title(),
- TextVariables.NUMBER, String.valueOf(materialCount.getValue()),
- "[material]", Utils.prettifyObject(materialCount.getKey(), this.user)));
- }
-
- String description = Utils.prettifyDescription(materialCount.getKey(), this.user);
-
- final String reference = "level.gui.buttons.material.";
- String blockId = this.user.getTranslationOrNothing(reference + "id",
- "[id]", materialCount.getKey().name());
-
- int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(materialCount.getKey(), 0);
- String value = blockValue > 0 ? this.user.getTranslationOrNothing(reference + "value",
- TextVariables.NUMBER, String.valueOf(blockValue)) : "";
-
- int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(materialCount.getKey(), 0);
- String limit = blockLimit > 0 ? this.user.getTranslationOrNothing(reference + "limit",
- TextVariables.NUMBER, String.valueOf(blockLimit)) : "";
-
- String count = this.user.getTranslationOrNothing(reference + "count",
- TextVariables.NUMBER, String.valueOf(materialCount.getValue()));
-
- long calculatedValue = (long) Math.min(blockLimit > 0 ? blockLimit : Integer.MAX_VALUE, materialCount.getValue()) * blockValue;
- String valueText = calculatedValue > 0 ? this.user.getTranslationOrNothing(reference + "calculated",
- TextVariables.NUMBER, String.valueOf(calculatedValue)) : "";
-
- if (template.description() != null)
- {
- builder.description(this.user.getTranslation(this.world, template.description(),
- "[description]", description,
- "[id]", blockId,
- "[value]", value,
- "[calculated]", valueText,
- "[limit]", limit,
- "[count]", count).
- replaceAll("(?m)^[ \\t]*\\r?\\n", "").
- replaceAll("(?> materialCountList;
-
- /**
- * This variable holds current pageIndex for multi-page generator choosing.
- */
- private int pageIndex;
-
- /**
- * This variable stores which tab currently is active.
- */
- private Tab activeTab;
-
- /**
- * This variable stores active filter for items.
- */
- private Filter activeFilter;
+public class DetailsPanel {
+ // ---------------------------------------------------------------------
+ // Section: Internal Constructor
+ // ---------------------------------------------------------------------
+
+ /**
+ * This is internal constructor. It is used internally in current class to avoid
+ * creating objects everywhere.
+ *
+ * @param addon Level object
+ * @param world World where user is operating
+ * @param user User who opens panel
+ */
+ private DetailsPanel(Level addon, World world, User user) {
+ this.addon = addon;
+ this.world = world;
+ this.user = user;
+
+ this.island = this.addon.getIslands().getIsland(world, user);
+
+ if (this.island != null) {
+ this.levelsData = this.addon.getManager().getLevelsData(this.island);
+ } else {
+ this.levelsData = null;
+ }
+
+ // By default no-filters are active.
+ this.activeTab = Tab.ALL_BLOCKS;
+ this.activeFilter = Filter.NAME;
+ this.materialCountList = new ArrayList<>(Material.values().length);
+
+ this.updateFilters();
+ }
+
+ /**
+ * This method builds this GUI.
+ */
+ private void build() {
+ if (this.island == null || this.levelsData == null) {
+ // Nothing to see.
+ Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-island"));
+ return;
+ }
+
+ if (this.levelsData.getMdCount().isEmpty() && this.levelsData.getUwCount().isEmpty()) {
+ // Nothing to see.
+ Utils.sendMessage(this.user, this.user.getTranslation("level.conversations.no-data"));
+ return;
+ }
+
+ // Start building panel.
+ TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
+ panelBuilder.user(this.user);
+ panelBuilder.world(this.user.getWorld());
+
+ panelBuilder.template("detail_panel", new File(this.addon.getDataFolder(), "panels"));
+
+ panelBuilder.parameters("[name]", this.user.getName());
+
+ panelBuilder.registerTypeBuilder("NEXT", this::createNextButton);
+ panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton);
+ panelBuilder.registerTypeBuilder("BLOCK", this::createMaterialButton);
+
+ panelBuilder.registerTypeBuilder("FILTER", this::createFilterButton);
+
+ // Register tabs
+ panelBuilder.registerTypeBuilder("TAB", this::createTabButton);
+
+ // Register unknown type builder.
+ panelBuilder.build();
+ }
+
+ /**
+ * This method updates filter of elements based on tabs.
+ */
+ private void updateFilters() {
+ this.materialCountList.clear();
+
+ switch (this.activeTab) {
+ case ALL_BLOCKS -> {
+ Map materialCountMap = new EnumMap<>(Material.class);
+
+ materialCountMap.putAll(this.levelsData.getMdCount());
+
+ // Add underwater blocks.
+ this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material,
+ materialCountMap.computeIfAbsent(material, key -> 0) + count));
+
+ materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey()))
+ .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
+ }
+ case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream().sorted((Map.Entry.comparingByKey()))
+ .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
+
+ case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream().sorted((Map.Entry.comparingByKey()))
+ .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
+
+ case SPAWNER -> {
+ int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0);
+ int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0);
+
+ // TODO: spawners need some touch...
+ this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater));
+ }
+ }
+
+ Comparator> sorter;
+
+ switch (this.activeFilter) {
+ case COUNT -> {
+ sorter = (o1, o2) -> {
+ if (o1.getValue().equals(o2.getValue())) {
+ String o1Name = Utils.prettifyObject(o1.getKey(), this.user);
+ String o2Name = Utils.prettifyObject(o2.getKey(), this.user);
+
+ return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name);
+ } else {
+ return Integer.compare(o2.getValue(), o1.getValue());
+ }
+ };
+ }
+ case VALUE -> {
+ sorter = (o1, o2) -> {
+ int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o1.getKey(), 0);
+ int o1Count = blockLimit > 0 ? Math.min(o1.getValue(), blockLimit) : o1.getValue();
+
+ blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o2.getKey(), 0);
+ int o2Count = blockLimit > 0 ? Math.min(o2.getValue(), blockLimit) : o2.getValue();
+
+ long o1Value = (long) o1Count
+ * this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.getKey(), 0);
+ long o2Value = (long) o2Count
+ * this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.getKey(), 0);
+
+ if (o1Value == o2Value) {
+ String o1Name = Utils.prettifyObject(o1.getKey(), this.user);
+ String o2Name = Utils.prettifyObject(o2.getKey(), this.user);
+
+ return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name);
+ } else {
+ return Long.compare(o2Value, o1Value);
+ }
+ };
+ }
+ default -> {
+ sorter = (o1, o2) -> {
+ String o1Name = Utils.prettifyObject(o1.getKey(), this.user);
+ String o2Name = Utils.prettifyObject(o2.getKey(), this.user);
+
+ return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name);
+ };
+ }
+ }
+
+ this.materialCountList.sort(sorter);
+
+ this.pageIndex = 0;
+ }
+
+ // ---------------------------------------------------------------------
+ // Section: Tab Button Type
+ // ---------------------------------------------------------------------
+
+ /**
+ * Create tab button panel item.
+ *
+ * @param template the template
+ * @param slot the slot
+ * @return the panel item
+ */
+ private PanelItem createTabButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ if (template.icon() != null) {
+ // Set icon
+ builder.icon(template.icon().clone());
+ }
+
+ if (template.title() != null) {
+ // Set title
+ builder.name(this.user.getTranslation(this.world, template.title()));
+ }
+
+ if (template.description() != null) {
+ // Set description
+ builder.description(this.user.getTranslation(this.world, template.description()));
+ }
+
+ Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.ALL_BLOCKS);
+
+ // Get only possible actions, by removing all inactive ones.
+ List activeActions = new ArrayList<>(template.actions());
+
+ activeActions.removeIf(action -> "VIEW".equalsIgnoreCase(action.actionType()) && this.activeTab == tab);
+
+ // Add Click handler
+ builder.clickHandler((panel, user, clickType, i) -> {
+ for (ItemTemplateRecord.ActionRecords action : activeActions) {
+ if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType()))
+ && "VIEW".equalsIgnoreCase(action.actionType())) {
+ this.activeTab = tab;
+
+ // Update filters.
+ this.updateFilters();
+ this.build();
+ }
+ }
+
+ return true;
+ });
+
+ // Collect tooltips.
+ List tooltips = activeActions.stream().filter(action -> action.tooltip() != null)
+ .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank())
+ .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
+
+ // Add tooltips.
+ if (!tooltips.isEmpty()) {
+ // Empty line and tooltips.
+ builder.description("");
+ builder.description(tooltips);
+ }
+
+ builder.glow(this.activeTab == tab);
+
+ return builder.build();
+ }
+
+ /**
+ * Create next button panel item.
+ *
+ * @param template the template
+ * @param slot the slot
+ * @return the panel item
+ */
+ private PanelItem createFilterButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ if (template.icon() != null) {
+ // Set icon
+ builder.icon(template.icon().clone());
+ }
+
+ Filter filter;
+
+ if (slot.amountMap().getOrDefault("FILTER", 0) > 1) {
+ filter = Enums.getIfPresent(Filter.class, String.valueOf(template.dataMap().get("filter"))).or(Filter.NAME);
+ } else {
+ filter = this.activeFilter;
+ }
+
+ final String reference = "level.gui.buttons.filters.";
+
+ if (template.title() != null) {
+ // Set title
+ builder.name(this.user.getTranslation(this.world,
+ template.title().replace("[filter]", filter.name().toLowerCase())));
+ } else {
+ builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".name"));
+ }
+
+ if (template.description() != null) {
+ // Set description
+ builder.description(this.user.getTranslation(this.world,
+ template.description().replace("[filter]", filter.name().toLowerCase())));
+ } else {
+ builder.name(
+ this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".description"));
+ }
+
+ // Get only possible actions, by removing all inactive ones.
+ List activeActions = new ArrayList<>(template.actions());
+
+ // Add Click handler
+ builder.clickHandler((panel, user, clickType, i) -> {
+ for (ItemTemplateRecord.ActionRecords action : activeActions) {
+ if (clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) {
+ if ("UP".equalsIgnoreCase(action.actionType())) {
+ this.activeFilter = Utils.getNextValue(Filter.values(), filter);
+
+ // Update filters.
+ this.updateFilters();
+ this.build();
+ } else if ("DOWN".equalsIgnoreCase(action.actionType())) {
+ this.activeFilter = Utils.getPreviousValue(Filter.values(), filter);
+
+ // Update filters.
+ this.updateFilters();
+ this.build();
+ } else if ("SELECT".equalsIgnoreCase(action.actionType())) {
+ this.activeFilter = filter;
+
+ // Update filters.
+ this.updateFilters();
+ this.build();
+ }
+ }
+ }
+
+ return true;
+ });
+
+ // Collect tooltips.
+ List tooltips = activeActions.stream().filter(action -> action.tooltip() != null)
+ .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank())
+ .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
+
+ // Add tooltips.
+ if (!tooltips.isEmpty()) {
+ // Empty line and tooltips.
+ builder.description("");
+ builder.description(tooltips);
+ }
+
+ builder.glow(this.activeFilter == filter);
+
+ return builder.build();
+ }
+
+ // ---------------------------------------------------------------------
+ // Section: Create common buttons
+ // ---------------------------------------------------------------------
+
+ /**
+ * Create next button panel item.
+ *
+ * @param template the template
+ * @param slot the slot
+ * @return the panel item
+ */
+ private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
+ long size = this.materialCountList.size();
+
+ if (size <= slot.amountMap().getOrDefault("BLOCK", 1)
+ || 1.0 * size / slot.amountMap().getOrDefault("BLOCK", 1) <= this.pageIndex + 1) {
+ // There are no next elements
+ return null;
+ }
+
+ int nextPageIndex = this.pageIndex + 2;
+
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ if (template.icon() != null) {
+ ItemStack clone = template.icon().clone();
+
+ if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false))) {
+ clone.setAmount(nextPageIndex);
+ }
+
+ builder.icon(clone);
+ }
+
+ if (template.title() != null) {
+ builder.name(this.user.getTranslation(this.world, template.title()));
+ }
+
+ if (template.description() != null) {
+ builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER,
+ String.valueOf(nextPageIndex)));
+ }
+
+ // Add ClickHandler
+ builder.clickHandler((panel, user, clickType, i) -> {
+ for (ItemTemplateRecord.ActionRecords action : template.actions()) {
+ if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType()))
+ && "NEXT".equalsIgnoreCase(action.actionType())) {
+ this.pageIndex++;
+ this.build();
+ }
+ }
+
+ // Always return true.
+ return true;
+ });
+
+ // Collect tooltips.
+ List tooltips = template.actions().stream().filter(action -> action.tooltip() != null)
+ .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank())
+ .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
+
+ // Add tooltips.
+ if (!tooltips.isEmpty()) {
+ // Empty line and tooltips.
+ builder.description("");
+ builder.description(tooltips);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Create previous button panel item.
+ *
+ * @param template the template
+ * @param slot the slot
+ * @return the panel item
+ */
+ private PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
+ if (this.pageIndex == 0) {
+ // There are no next elements
+ return null;
+ }
+
+ int previousPageIndex = this.pageIndex;
+
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ if (template.icon() != null) {
+ ItemStack clone = template.icon().clone();
+
+ if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false))) {
+ clone.setAmount(previousPageIndex);
+ }
+
+ builder.icon(clone);
+ }
+
+ if (template.title() != null) {
+ builder.name(this.user.getTranslation(this.world, template.title()));
+ }
+
+ if (template.description() != null) {
+ builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER,
+ String.valueOf(previousPageIndex)));
+ }
+
+ // Add ClickHandler
+ builder.clickHandler((panel, user, clickType, i) -> {
+ for (ItemTemplateRecord.ActionRecords action : template.actions()) {
+ if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType()))
+ && "PREVIOUS".equalsIgnoreCase(action.actionType())) {
+ this.pageIndex--;
+ this.build();
+ }
+ }
+
+ // Always return true.
+ return true;
+ });
+
+ // Collect tooltips.
+ List tooltips = template.actions().stream().filter(action -> action.tooltip() != null)
+ .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank())
+ .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
+
+ // Add tooltips.
+ if (!tooltips.isEmpty()) {
+ // Empty line and tooltips.
+ builder.description("");
+ builder.description(tooltips);
+ }
+
+ return builder.build();
+ }
+
+ // ---------------------------------------------------------------------
+ // Section: Create Material Button
+ // ---------------------------------------------------------------------
+
+ /**
+ * Create material button panel item.
+ *
+ * @param template the template
+ * @param slot the slot
+ * @return the panel item
+ */
+ private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
+ if (this.materialCountList.isEmpty()) {
+ // Does not contain any generators.
+ return null;
+ }
+
+ int index = this.pageIndex * slot.amountMap().getOrDefault("BLOCK", 1) + slot.slot();
+
+ if (index >= this.materialCountList.size()) {
+ // Out of index.
+ return null;
+ }
+
+ return this.createMaterialButton(template, this.materialCountList.get(index));
+ }
+
+ /**
+ * This method creates button for material.
+ *
+ * @param template the template of the button
+ * @param materialCount materialCount which button must be created.
+ * @return PanelItem for generator tier.
+ */
+ private PanelItem createMaterialButton(ItemTemplateRecord template, Pair materialCount) {
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ if (template.icon() != null) {
+ builder.icon(template.icon().clone());
+ } else {
+ builder.icon(PanelUtils.getMaterialItem(materialCount.getKey()));
+ }
+
+ if (materialCount.getValue() < 64) {
+ builder.amount(materialCount.getValue());
+ }
+
+ if (template.title() != null) {
+ builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER,
+ String.valueOf(materialCount.getValue()), "[material]",
+ Utils.prettifyObject(materialCount.getKey(), this.user)));
+ }
+
+ String description = Utils.prettifyDescription(materialCount.getKey(), this.user);
+
+ final String reference = "level.gui.buttons.material.";
+ String blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", materialCount.getKey().name());
+
+ int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(materialCount.getKey(), 0);
+ String value = blockValue > 0
+ ? this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER,
+ String.valueOf(blockValue))
+ : "";
+
+ int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(materialCount.getKey(), 0);
+ String limit = blockLimit > 0
+ ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER,
+ String.valueOf(blockLimit))
+ : "";
+
+ String count = this.user.getTranslationOrNothing(reference + "count", TextVariables.NUMBER,
+ String.valueOf(materialCount.getValue()));
+
+ long calculatedValue = (long) Math.min(blockLimit > 0 ? blockLimit : Integer.MAX_VALUE,
+ materialCount.getValue()) * blockValue;
+ String valueText = calculatedValue > 0 ? this.user.getTranslationOrNothing(reference + "calculated",
+ TextVariables.NUMBER, String.valueOf(calculatedValue)) : "";
+
+ if (template.description() != null) {
+ builder.description(this.user
+ .getTranslation(this.world, template.description(), "[description]", description, "[id]", blockId,
+ "[value]", value, "[calculated]", valueText, "[limit]", limit, "[count]", count)
+ .replaceAll("(?m)^[ \\t]*\\r?\\n", "").replaceAll("(?> materialCountList;
+
+ /**
+ * This variable holds current pageIndex for multi-page generator choosing.
+ */
+ private int pageIndex;
+
+ /**
+ * This variable stores which tab currently is active.
+ */
+ private Tab activeTab;
+
+ /**
+ * This variable stores active filter for items.
+ */
+ private Filter activeFilter;
}
diff --git a/src/main/java/world/bentobox/level/panels/TopLevelPanel.java b/src/main/java/world/bentobox/level/panels/TopLevelPanel.java
index e60d377..81910b8 100644
--- a/src/main/java/world/bentobox/level/panels/TopLevelPanel.java
+++ b/src/main/java/world/bentobox/level/panels/TopLevelPanel.java
@@ -5,10 +5,11 @@
package world.bentobox.level.panels;
-
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -29,474 +30,390 @@
import world.bentobox.level.Level;
import world.bentobox.level.util.Utils;
-
/**
* This panel opens top likes panel
*/
-public class TopLevelPanel
-{
-
+public class TopLevelPanel {
// ---------------------------------------------------------------------
// Section: Internal Constructor
// ---------------------------------------------------------------------
-
/**
- * This is internal constructor. It is used internally in current class to avoid creating objects everywhere.
+ * This is internal constructor. It is used internally in current class to avoid
+ * creating objects everywhere.
*
- * @param addon Level object.
- * @param user User who opens Panel.
- * @param world World where gui is opened
+ * @param addon Level object.
+ * @param user User who opens Panel.
+ * @param world World where gui is opened
* @param permissionPrefix Permission Prefix
*/
- private TopLevelPanel(Level addon, User user, World world, String permissionPrefix)
- {
- this.addon = addon;
- this.user = user;
- this.world = world;
-
- this.iconPermission = permissionPrefix + "level.icon";
-
- this.topIslands = this.addon.getManager().getTopTen(this.world, 10).entrySet().stream().
- map(entry -> {
- Island island = this.addon.getIslandsManager().getIsland(this.world, entry.getKey());
- return new IslandTopRecord(island, entry.getValue());
- }).
- collect(Collectors.toList());
+ private TopLevelPanel(Level addon, User user, World world, String permissionPrefix) {
+ this.addon = addon;
+ this.user = user;
+ this.world = world;
+
+ this.iconPermission = permissionPrefix + "level.icon";
+ topIslands = new ArrayList<>();
+ for (Map.Entry en : addon.getManager().getTopTen(this.world, Level.TEN).entrySet()) {
+ Optional is = addon.getIslands().getIslandById(en.getKey());
+ if (is.isPresent()) {
+ topIslands.add(new IslandTopRecord(is.get(), en.getValue()));
+ }
+ }
}
-
/**
- * Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
- * panels.
+ * Build method manages current panel opening. It uses BentoBox PanelAPI that is
+ * easy to use and users can get nice panels.
*/
- public void build()
- {
- TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
+ public void build() {
+ TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
- panelBuilder.user(this.user);
- panelBuilder.world(this.world);
+ panelBuilder.user(this.user);
+ panelBuilder.world(this.world);
- panelBuilder.template("top_panel", new File(this.addon.getDataFolder(), "panels"));
+ panelBuilder.template("top_panel", new File(this.addon.getDataFolder(), "panels"));
- panelBuilder.registerTypeBuilder("VIEW", this::createViewerButton);
- panelBuilder.registerTypeBuilder("TOP", this::createPlayerButton);
+ panelBuilder.registerTypeBuilder("VIEW", this::createViewerButton);
+ panelBuilder.registerTypeBuilder("TOP", this::createPlayerButton);
- // Register unknown type builder.
- panelBuilder.build();
+ // Register unknown type builder.
+ panelBuilder.build();
}
-
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
-
/**
* Creates fallback based on template.
+ *
* @param template Template record for fallback button.
- * @param index Place of the fallback.
+ * @param index Place of the fallback.
* @return Fallback panel item.
*/
- private PanelItem createFallback(ItemTemplateRecord template, long index)
- {
- if (template == null)
- {
- return null;
- }
-
- PanelItemBuilder builder = new PanelItemBuilder();
-
- if (template.icon() != null)
- {
- builder.icon(template.icon().clone());
- }
-
- if (template.title() != null)
- {
- builder.name(this.user.getTranslation(this.world, template.title(),
- TextVariables.NAME, String.valueOf(index)));
- }
- else
- {
- builder.name(this.user.getTranslation(this.world, REFERENCE,
- TextVariables.NAME, String.valueOf(index)));
- }
-
- if (template.description() != null)
- {
- builder.description(this.user.getTranslation(this.world, template.description(),
- TextVariables.NUMBER, String.valueOf(index)));
- }
-
- builder.amount(index != 0 ? (int) index : 1);
-
- return builder.build();
- }
+ private PanelItem createFallback(ItemTemplateRecord template, long index) {
+ if (template == null) {
+ return null;
+ }
+
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ if (template.icon() != null) {
+ builder.icon(template.icon().clone());
+ }
+
+ if (template.title() != null) {
+ builder.name(
+ this.user.getTranslation(this.world, template.title(), TextVariables.NAME, String.valueOf(index)));
+ } else {
+ builder.name(this.user.getTranslation(this.world, REFERENCE, TextVariables.NAME, String.valueOf(index)));
+ }
+
+ if (template.description() != null) {
+ builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER,
+ String.valueOf(index)));
+ }
+ builder.amount(index != 0 ? (int) index : 1);
+
+ return builder.build();
+ }
/**
* This method creates player icon with warp functionality.
*
* @return PanelItem for PanelBuilder.
*/
- private PanelItem createPlayerButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot)
- {
- int index = (int) template.dataMap().getOrDefault("index", 0);
+ private PanelItem createPlayerButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) {
+ int index = (int) template.dataMap().getOrDefault("index", 0);
- if (index < 1)
- {
- return this.createFallback(template.fallback(), index);
- }
+ if (index < 1) {
+ return this.createFallback(template.fallback(), index);
+ }
- IslandTopRecord islandTopRecord = this.topIslands.size() < index ? null : this.topIslands.get(index - 1);
+ IslandTopRecord islandTopRecord = this.topIslands.size() < index ? null : this.topIslands.get(index - 1);
- if (islandTopRecord == null)
- {
- return this.createFallback(template.fallback(), index);
- }
+ if (islandTopRecord == null) {
+ return this.createFallback(template.fallback(), index);
+ }
- return this.createIslandIcon(template, islandTopRecord, index);
+ return this.createIslandIcon(template, islandTopRecord, index);
}
-
/**
* This method creates button from template for given island top record.
- * @param template Icon Template.
+ *
+ * @param template Icon Template.
* @param islandTopRecord Island Top Record.
- * @param index Place Index.
+ * @param index Place Index.
* @return PanelItem for PanelBuilder.
*/
- private PanelItem createIslandIcon(ItemTemplateRecord template, IslandTopRecord islandTopRecord, int index)
- {
- // Get player island.
- Island island = islandTopRecord.island();
-
- if (island == null)
- {
- return this.createFallback(template.fallback(), index);
- }
-
- PanelItemBuilder builder = new PanelItemBuilder();
-
- this.populateIslandIcon(builder, template, island);
- this.populateIslandTitle(builder, template, island);
- this.populateIslandDescription(builder, template, island, islandTopRecord, index);
-
- builder.amount(index);
-
- // Get only possible actions, by removing all inactive ones.
- List activeActions = new ArrayList<>(template.actions());
-
- activeActions.removeIf(action ->
- {
- switch (action.actionType().toUpperCase())
- {
- case "WARP" -> {
- return island.getOwner() == null ||
- this.addon.getWarpHook() == null ||
- !this.addon.getWarpHook().getWarpSignsManager().hasWarp(this.world, island.getOwner());
- }
- case "VISIT" -> {
- return island.getOwner() == null ||
- this.addon.getVisitHook() == null ||
- !this.addon.getVisitHook().getAddonManager().preprocessTeleportation(this.user, island);
- }
- case "VIEW" -> {
- return island.getOwner() == null ||
- !island.getMemberSet(RanksManager.MEMBER_RANK).contains(this.user.getUniqueId());
- }
- default -> {
- return false;
- }
- }
- });
-
- // Add Click handler
- builder.clickHandler((panel, user, clickType, i) ->
- {
- for (ItemTemplateRecord.ActionRecords action : activeActions)
- {
- if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
- {
- switch (action.actionType().toUpperCase())
- {
- case "WARP" -> {
- this.user.closeInventory();
- this.addon.getWarpHook().getWarpSignsManager().warpPlayer(this.world, this.user, island.getOwner());
- }
- case "VISIT" ->
- // The command call implementation solves necessity to check for all visits options,
- // like cool down, confirmation and preprocess in single go. Would it be better to write
- // all logic here?
-
- this.addon.getPlugin().getIWM().getAddon(this.world).
- flatMap(GameModeAddon::getPlayerCommand).ifPresent(command ->
- {
- String mainCommand =
- this.addon.getVisitHook().getSettings().getPlayerMainCommand();
-
- if (!mainCommand.isBlank())
- {
- this.user.closeInventory();
- this.user.performCommand(command.getTopLabel() + " " + mainCommand + " " + island.getOwner());
- }
- });
-
- case "VIEW" -> {
- this.user.closeInventory();
- // Open Detailed GUI.
- DetailsPanel.openPanel(this.addon, this.world, this.user);
- }
- // Catch default
- default -> {
- this.user.closeInventory();
- addon.logError("Unknown action type " + action.actionType().toUpperCase());
- }
- }
- }
- }
-
- return true;
- });
-
- // Collect tooltips.
- List tooltips = activeActions.stream().
- filter(action -> action.tooltip() != null).
- map(action -> this.user.getTranslation(this.world, action.tooltip())).
- filter(text -> !text.isBlank()).
- collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
-
- // Add tooltips.
- if (!tooltips.isEmpty())
- {
- // Empty line and tooltips.
- builder.description("");
- builder.description(tooltips);
- }
-
- return builder.build();
+ private PanelItem createIslandIcon(ItemTemplateRecord template, IslandTopRecord islandTopRecord, int index) {
+ // Get player island.
+ Island island = islandTopRecord.island();
+
+ if (island == null) {
+ return this.createFallback(template.fallback(), index);
+ }
+
+ PanelItemBuilder builder = new PanelItemBuilder();
+
+ this.populateIslandIcon(builder, template, island);
+ this.populateIslandTitle(builder, template, island);
+ this.populateIslandDescription(builder, template, island, islandTopRecord, index);
+
+ builder.amount(index);
+
+ // Get only possible actions, by removing all inactive ones.
+ List activeActions = new ArrayList<>(template.actions());
+
+ activeActions.removeIf(action -> {
+ switch (action.actionType().toUpperCase()) {
+ case "WARP" -> {
+ return island.getOwner() == null || this.addon.getWarpHook() == null
+ || !this.addon.getWarpHook().getWarpSignsManager().hasWarp(this.world, island.getOwner());
+ }
+ case "VISIT" -> {
+ return island.getOwner() == null || this.addon.getVisitHook() == null
+ || !this.addon.getVisitHook().getAddonManager().preprocessTeleportation(this.user, island, true);
+ }
+ case "VIEW" -> {
+ return island.getOwner() == null
+ || !island.getMemberSet(RanksManager.MEMBER_RANK).contains(this.user.getUniqueId());
+ }
+ default -> {
+ return false;
+ }
+ }
+ });
+
+ // Add Click handler
+ builder.clickHandler((panel, user, clickType, i) -> {
+ for (ItemTemplateRecord.ActionRecords action : activeActions) {
+ if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN) {
+ switch (action.actionType().toUpperCase()) {
+ case "WARP" -> {
+ this.user.closeInventory();
+ this.addon.getWarpHook().getWarpSignsManager().warpPlayer(this.world, this.user,
+ island.getOwner());
+ }
+ case "VISIT" ->
+ // The command call implementation solves necessity to check for all visits
+ // options,
+ // like cool down, confirmation and preprocess in single go. Would it be better
+ // to write
+ // all logic here?
+
+ this.addon.getPlugin().getIWM().getAddon(this.world).flatMap(GameModeAddon::getPlayerCommand)
+ .ifPresent(command -> {
+ String mainCommand = this.addon.getVisitHook().getSettings().getPlayerMainCommand();
+
+ if (!mainCommand.isBlank()) {
+ this.user.closeInventory();
+ this.user.performCommand(
+ command.getTopLabel() + " " + mainCommand + " " + island.getOwner());
+ }
+ });
+
+ case "VIEW" -> {
+ this.user.closeInventory();
+ // Open Detailed GUI.
+ DetailsPanel.openPanel(this.addon, this.world, this.user);
+ }
+ // Catch default
+ default -> {
+ this.user.closeInventory();
+ addon.logError("Unknown action type " + action.actionType().toUpperCase());
+ }
+ }
+ }
+ }
+
+ return true;
+ });
+
+ // Collect tooltips.
+ List tooltips = activeActions.stream().filter(action -> action.tooltip() != null)
+ .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank())
+ .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
+
+ // Add tooltips.
+ if (!tooltips.isEmpty()) {
+ // Empty line and tooltips.
+ builder.description("");
+ builder.description(tooltips);
+ }
+
+ return builder.build();
}
-
/**
- * Populate given panel item builder name with values from template and island objects.
+ * Populate given panel item builder name with values from template and island
+ * objects.
*
- * @param builder the builder
+ * @param builder the builder
* @param template the template
- * @param island the island
+ * @param island the island
*/
- private void populateIslandTitle(PanelItemBuilder builder,
- ItemTemplateRecord template,
- Island island)
- {
-
- // Get Island Name
- String nameText;
-
- if (island.getName() == null || island.getName().isEmpty())
- {
- nameText = this.user.getTranslation(REFERENCE + "owners-island",
- PLAYER,
- island.getOwner() == null ?
- this.user.getTranslation(REFERENCE + "unknown") :
- this.addon.getPlayers().getName(island.getOwner()));
- }
- else
- {
- nameText = island.getName();
- }
-
- // Template specific title is always more important than custom one.
- if (template.title() != null && !template.title().isBlank())
- {
- builder.name(this.user.getTranslation(this.world, template.title(),
- TextVariables.NAME, nameText));
- }
- else
- {
- builder.name(this.user.getTranslation(REFERENCE + "name", TextVariables.NAME, nameText));
- }
+ private void populateIslandTitle(PanelItemBuilder builder, ItemTemplateRecord template, Island island) {
+
+ // Get Island Name
+ String nameText;
+
+ if (island.getName() == null || island.getName().isEmpty()) {
+ nameText = this.user.getTranslation(REFERENCE + "owners-island", PLAYER,
+ island.getOwner() == null ? this.user.getTranslation(REFERENCE + "unknown")
+ : this.addon.getPlayers().getName(island.getOwner()));
+ } else {
+ nameText = island.getName();
+ }
+
+ // Template specific title is always more important than custom one.
+ if (template.title() != null && !template.title().isBlank()) {
+ builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NAME, nameText));
+ } else {
+ builder.name(this.user.getTranslation(REFERENCE + "name", TextVariables.NAME, nameText));
+ }
}
-
/**
- * Populate given panel item builder icon with values from template and island objects.
+ * Populate given panel item builder icon with values from template and island
+ * objects.
*
- * @param builder the builder
+ * @param builder the builder
* @param template the template
- * @param island the island
+ * @param island the island
*/
- private void populateIslandIcon(PanelItemBuilder builder,
- ItemTemplateRecord template,
- Island island)
- {
- User owner = island.getOwner() == null ? null : User.getInstance(island.getOwner());
-
- // Get permission or island icon
- String permissionIcon = owner == null ? null :
- Utils.getPermissionValue(owner, this.iconPermission, null);
-
- Material material;
-
- if (permissionIcon != null && !permissionIcon.equals("*"))
- {
- material = Material.matchMaterial(permissionIcon);
- }
- else
- {
- material = null;
- }
-
- if (material != null)
- {
- if (!material.equals(Material.PLAYER_HEAD))
- {
- builder.icon(material);
- }
- else
- {
- builder.icon(owner.getName());
- }
- }
- else if (template.icon() != null)
- {
- builder.icon(template.icon().clone());
- }
- else if (owner != null)
- {
- builder.icon(owner.getName());
- }
- else
- {
- builder.icon(Material.PLAYER_HEAD);
- }
+ private void populateIslandIcon(PanelItemBuilder builder, ItemTemplateRecord template, Island island) {
+ User owner = island.getOwner() == null ? null : User.getInstance(island.getOwner());
+
+ // Get permission or island icon
+ String permissionIcon = owner == null ? null : Utils.getPermissionValue(owner, this.iconPermission, null);
+
+ Material material;
+
+ if (permissionIcon != null && !permissionIcon.equals("*")) {
+ material = Material.matchMaterial(permissionIcon);
+ } else {
+ material = null;
+ }
+
+ if (material != null) {
+ if (!material.equals(Material.PLAYER_HEAD)) {
+ builder.icon(material);
+ } else {
+ builder.icon(owner.getName());
+ }
+ } else if (template.icon() != null) {
+ builder.icon(template.icon().clone());
+ } else if (owner != null) {
+ builder.icon(owner.getName());
+ } else {
+ builder.icon(Material.PLAYER_HEAD);
+ }
}
-
/**
- * Populate given panel item builder description with values from template and island objects.
+ * Populate given panel item builder description with values from template and
+ * island objects.
*
- * @param builder the builder
- * @param template the template
- * @param island the island
+ * @param builder the builder
+ * @param template the template
+ * @param island the island
* @param islandTopRecord the top record object
- * @param index place index.
+ * @param index place index.
*/
- private void populateIslandDescription(PanelItemBuilder builder,
- ItemTemplateRecord template,
- Island island,
- IslandTopRecord islandTopRecord,
- int index)
- {
- // Get Owner Name
- String ownerText = this.user.getTranslation(REFERENCE + "owner",
- PLAYER,
- island.getOwner() == null ?
- this.user.getTranslation(REFERENCE + "unknown") :
- this.addon.getPlayers().getName(island.getOwner()));
-
- // Get Members Text
- String memberText;
-
- if (island.getMemberSet().size() > 1)
- {
- StringBuilder memberBuilder = new StringBuilder(
- this.user.getTranslationOrNothing(REFERENCE + "members-title"));
-
- for (UUID uuid : island.getMemberSet())
- {
- User member = User.getInstance(uuid);
-
- if (memberBuilder.length() > 0)
- {
- memberBuilder.append("\n");
- }
-
- memberBuilder.append(
- this.user.getTranslationOrNothing(REFERENCE + "member",
- PLAYER, member.getName()));
- }
-
- memberText = memberBuilder.toString();
- }
- else
- {
- memberText = "";
- }
-
- String placeText = this.user.getTranslation(REFERENCE + "place",
- TextVariables.NUMBER, String.valueOf(index));
-
- String levelText = this.user.getTranslation(REFERENCE + "level",
- TextVariables.NUMBER, this.addon.getManager().formatLevel(islandTopRecord.level()));
-
- // Template specific description is always more important than custom one.
- if (template.description() != null && !template.description().isBlank())
- {
- builder.description(this.user.getTranslation(this.world, template.description(),
- "[owner]", ownerText,
- "[members]", memberText,
- "[level]", levelText,
- "[place]", placeText).
- replaceAll("(?m)^[ \\t]*\\r?\\n", "").
- replaceAll("(? 1) {
+ StringBuilder memberBuilder = new StringBuilder(
+ this.user.getTranslationOrNothing(REFERENCE + "members-title"));
+
+ for (UUID uuid : island.getMemberSet()) {
+ User member = User.getInstance(uuid);
+
+ if (memberBuilder.length() > 0) {
+ memberBuilder.append("\n");
+ }
+
+ memberBuilder.append(this.user.getTranslationOrNothing(REFERENCE + "member", PLAYER, member.getName()));
+ }
+
+ memberText = memberBuilder.toString();
+ } else {
+ memberText = "";
+ }
+
+ String placeText = this.user.getTranslation(REFERENCE + "place", TextVariables.NUMBER, String.valueOf(index));
+
+ String levelText = this.user.getTranslation(REFERENCE + "level", TextVariables.NUMBER,
+ this.addon.getManager().formatLevel(islandTopRecord.level()));
+
+ // Template specific description is always more important than custom one.
+ if (template.description() != null && !template.description().isBlank()) {
+ builder.description(this.user
+ .getTranslation(this.world, template.description(), "[owner]", ownerText, "[members]", memberText,
+ "[level]", levelText, "[place]", placeText)
+ .replaceAll("(?m)^[ \\t]*\\r?\\n", "").replaceAll("(? level to island -> level.
+ *
+ * @param island island
+ * @param level level
*/
- private record IslandTopRecord(Island island, Long level) {}
-
+ private record IslandTopRecord(Island island, Long level) {
+ }
// ---------------------------------------------------------------------
// Section: Variables
diff --git a/src/main/resources/blockconfig.yml b/src/main/resources/blockconfig.yml
index d1580ac..3af622c 100644
--- a/src/main/resources/blockconfig.yml
+++ b/src/main/resources/blockconfig.yml
@@ -287,7 +287,7 @@ blocks:
GRANITE_SLAB: 1
GRANITE_STAIRS: 1
GRANITE_WALL: 1
- GRASS: 4
+ SHORT_GRASS: 4
GRASS_BLOCK: 4
GRAVEL: 1
GRAY_BANNER: 2
diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml
index 6456818..f71b52d 100755
--- a/src/main/resources/locales/en-US.yml
+++ b/src/main/resources/locales/en-US.yml
@@ -22,8 +22,18 @@ admin:
remove:
description: "remove player from Top Ten"
parameters: ""
-
-
+ 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:
level:
parameters: "[player]"
diff --git a/src/main/resources/locales/uk.yml b/src/main/resources/locales/uk.yml
new file mode 100644
index 0000000..14962a0
--- /dev/null
+++ b/src/main/resources/locales/uk.yml
@@ -0,0 +1,184 @@
+---
+admin:
+ level:
+ parameters: ""
+ description: розрахувати рівень острова для гравця
+ sethandicap:
+ parameters: " "
+ description: встановити гандикап острова, як правило, рівень острова стартера
+ changed: "&a Початковий гандикап острова змінено з [number] на [new_number]."
+ invalid-level: "&c Недійсний гандикап. Використовуйте ціле число."
+ levelstatus:
+ description: показати, скільки островів у черзі на сканування
+ islands-in-queue: "&a Острови в черзі: [number]"
+ top:
+ description: показати першу десятку списку
+ unknown-world: "&c Невідомий світ!"
+ display: "&f[rank]. &a[name] &7- &b[level]"
+ remove:
+ description: видалити гравця з першої десятки
+ parameters: ""
+ stats:
+ description: показати статистику островів на цьому сервері
+ title: Статистика острова сервера
+ world: "&a [name]"
+ no-data: "&c Немає даних для обробки."
+ average-level: 'Середній рівень острова: [number]'
+ median-level: 'Середній рівень острова: [number]'
+ mode-level: 'Рівень острова режиму: [number]'
+ highest-level: 'Найвищий рівень острова: [number]'
+ lowest-level: 'Найнижчий рівень острова: [number]'
+ distribution: 'Розподіл на рівні острова:'
+ islands: острови
+island:
+ level:
+ parameters: "[player]"
+ description: обчисліть свій рівень острова або покажіть рівень [player]
+ calculating: "&a Розрахунок рівня..."
+ estimated-wait: "&a Приблизне очікування: [number] секунд"
+ in-queue: "&a Ви номер [number] у черзі"
+ island-level-is: "&a Рівень острова &b[level]"
+ required-points-to-next-level: "&a [points] потрібні бали до наступного рівня"
+ deaths: "&c([number] смерті)"
+ cooldown: "&c Ви повинні зачекати &b[time] &c секунд, поки ви зможете зробити
+ це знову"
+ in-progress: "&6 Розрахунок рівня острова триває..."
+ time-out: "&c Розрахунок рівня тривав занадто довго. Будь-ласка спробуйте пізніше."
+ top:
+ description: показати першу десятку
+ gui-title: "& Десятка Кращих"
+ gui-heading: "&6[name]: &B[rank]"
+ island-level: "&b Рівень [level]"
+ warp-to: "&A Варп на острів [name]."
+ level-details:
+ above-sea-level-blocks: Блоки над рівнем моря
+ spawners: Спавера
+ underwater-blocks: Підводні блоки
+ all-blocks: Всі блоки
+ no-island: "&c Немає острова!"
+ names-island: острів [name].
+ syntax: "[name] x [number]"
+ hint: "&c Запустіть рівень, щоб переглянути звіт про блокування"
+level:
+ commands:
+ value:
+ parameters: "[hand|]"
+ description: показує значення блоків. Додайте 'hand' в кінці, щоб відобразити
+ значення предмета в руках.
+ gui:
+ titles:
+ top: "&0&l Топ островів"
+ detail-panel: "&0&l острів [name]."
+ value-panel: "&0&l Значення блоку"
+ buttons:
+ island:
+ empty: "&f&l [name]. місце"
+ name: "&f&l [name]"
+ description: |-
+ [owner]
+ [members]
+ [place]
+ [level]
+ owners-island: Острів [player].
+ owner: "&7&l Власник: &r&b [player]"
+ members-title: "&7&l Члени:"
+ member: "&b - [player]"
+ unknown: невідомий
+ place: "&7&o [number]. &r&7 місце"
+ level: "&7 Рівень: &o [number]"
+ material:
+ name: "&f&l [number] x [material]"
+ description: |-
+ [description]
+ [count]
+ [value]
+ [calculated]
+ [limit]
+ [id]
+ id: "&7 Ідентифікатор блоку: &e [id]"
+ value: "&7 Значення блоку: &e [number]"
+ limit: "&7 Обмеження блоку: &e [number]"
+ count: "&7 Кількість блоків: &e [number]"
+ calculated: "&7 Розраховане значення: &e [number]"
+ all_blocks:
+ name: "&f&l Усі блоки"
+ description: |-
+ &7 Показати всі блоки
+ &7 на острові.
+ above_sea_level:
+ name: "&f&l Блоки над рівнем моря"
+ description: |-
+ &7 Показувати лише блоки
+ &7, які знаходяться над морем
+ &7 рівень.
+ underwater:
+ name: "&f&l Блоки під рівнем моря"
+ description: |-
+ &7 Показувати лише блоки
+ &7, які знаходяться нижче моря
+ &7 рівень.
+ spawner:
+ name: "&f&l Спанера"
+ description: "&7 Відображати лише спавнери."
+ filters:
+ name:
+ name: "&f&l Сортувати за назвою"
+ description: "&7 Сортувати всі блоки за назвою."
+ value:
+ name: "&f&l Сортувати за значенням"
+ description: "&7 Сортувати всі блоки за їх значенням."
+ count:
+ name: "&f&l Сортувати за кількістю"
+ description: "&7 Відсортуйте всі блоки за їх кількістю."
+ value:
+ name: "&f&l [material]"
+ description: |-
+ [description]
+ [value]
+ [underwater]
+ [limit]
+ [id]
+ id: "&7 Ідентифікатор блоку: &e [id]"
+ value: "&7 Значення блоку: &e [number]"
+ underwater: "&7 Нижче рівня моря: &e [number]"
+ limit: "&7 Обмеження блоку: &e [number]"
+ previous:
+ name: "&f&l Попередня сторінка"
+ description: "&7 Перейти на сторінку [number]."
+ next:
+ name: "&f&l Наступна сторінка"
+ description: "&7 Перейти на сторінку [number]."
+ search:
+ name: "&f&l Пошук"
+ description: |-
+ &7 Пошук конкретного
+ &7 значення.
+ search: "&b Значення: [value]"
+ tips:
+ click-to-view: "&e Натисніть &7, щоб переглянути."
+ click-to-previous: "&e Натисніть &7, щоб переглянути попередню сторінку."
+ click-to-next: "&e Натисніть &7, щоб переглянути наступну сторінку."
+ click-to-select: "&e Натисніть &7, щоб вибрати."
+ left-click-to-cycle-up: "&e Клацніть лівою кнопкою миші &7, щоб перейти вгору."
+ right-click-to-cycle-down: "&e Клацніть правою кнопкою миші &7, щоб перейти
+ вниз."
+ left-click-to-change: "&e Клацніть лівою кнопкою миші &7 для редагування."
+ right-click-to-clear: "&e Клацніть правою кнопкою миші &7, щоб очистити."
+ click-to-asc: "&e Клацніть &7, щоб відсортувати в порядку збільшення."
+ click-to-desc: "&e Клацніть &7, щоб відсортувати в порядку зменшення."
+ click-to-warp: "&e Натисніть &7, щоб деформувати."
+ click-to-visit: "&e Натисніть &7, щоб відвідати."
+ right-click-to-visit: "&e Клацніть правою кнопкою миші &7, щоб відвідати."
+ conversations:
+ prefix: "&l&6 [BentoBox]: &r"
+ no-data: "&c Запустіть рівень, щоб переглянути звіт про блокування."
+ cancel-string: cancel
+ exit-string: cancel, exit, quit
+ write-search: "&e Введіть пошукове значення. (Напишіть 'cancel', щоб вийти)"
+ search-updated: "&a Значення пошуку оновлено."
+ cancelled: "&c Розмова скасована!"
+ no-value: "&c Цей предмет не має цінності."
+ unknown-item: "&c '[material]' не існує в грі."
+ value: "&7 Значення '[material]' таке: &e[value]"
+ value-underwater: "&7 Значення '[material]' нижче рівня моря: &e[value]"
+ empty-hand: "&c У вашій руці немає блоків"
diff --git a/src/main/resources/panels/detail_panel.yml b/src/main/resources/panels/detail_panel.yml
index 927a788..da08f6c 100644
--- a/src/main/resources/panels/detail_panel.yml
+++ b/src/main/resources/panels/detail_panel.yml
@@ -1,25 +1,49 @@
+# Name of panel used for indentification in the code
detail_panel:
+ # Title of the panel shown to the user. This is a reference and the reference will be translatable in the locale file
title: level.gui.titles.detail-panel
+ # The type of panel to show. Options are INVENTORY, HOPPER, DROPPER. INVENTORY is that standard chest inventory and
+ # the others refer to the inventories shown for those items.
type: INVENTORY
+ # The background of the panel. These items will be shown if other items are not there. STAINED_GLASS_PANEs give a good effect.
background:
icon: BLACK_STAINED_GLASS_PANE
- title: "&b&r" # Empty text
+ # Each item may have text applied to it, but usually for background items, nothing is shown.
+ title: "&b&r" # Empty text. This is using the Bukkit chat color coding with &'s.
border:
+ # The border of each panel may be shown as a different item.
+ # It can be used to provide a contrast to items in the panel.
icon: BLACK_STAINED_GLASS_PANE
title: "&b&r" # Empty text
+ # This tag indicates which rows in the panel must be shown. The panel will be sized vertically accordingly. This does not include the borders.
+ # This can be a list and rows must be between 1 and 6, if used.
force-shown: []
+ # The content section contains details of each item/button in the panel. The numbers indicate the rows and then then columns of each item.
content:
+ # Row number
1:
+ # Column number
2:
+ # Icon is a Bukkit Material.
icon: STONE
+ # Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file
title: level.gui.buttons.all_blocks.name
+ # Description of the button shown to the user in the lore. This is a reference and the reference will be translatable in the locale file
description: level.gui.buttons.all_blocks.description
+ # The data section is a key-value list of data relavent for this button. It is interpreted by the code implemented the panel.
+ # The convention is to specify the type and the panel tab that will open if pressed. These are Enums in the code.
data:
+ # Type button will go to the ALL_BLOCKS tab when clicked.
type: TAB
tab: ALL_BLOCKS
+ # Actions cover what happens if the button is clicked or the mouse is moved over it. There can be multiple actions possible for different
+ # click-types.
actions:
+ # Each action has an arbitrary descriptive name to define it.
view:
+ # The click-type is the same as the bukkit {@link org.bukkit.event.inventory.ClickType}. UNKNOWN is the default.
click-type: unknown
+ # tooltip is a locale reference that will be translated for the user and shown when they hover over the button.
tooltip: level.gui.tips.click-to-view
3:
icon: GRASS_BLOCK
@@ -57,12 +81,12 @@ detail_panel:
9:
# You can create multiple buttons. By default it is one.
icon: IRON_TRAPDOOR
- # [filter] is placeholder for different filter types. It will be replaced with name, value, count.
+ # [filter] is a placeholder for different filter types. It will be replaced with name, value, count.
title: level.gui.buttons.filters.[filter].name
description: level.gui.buttons.filters.[filter].description
data:
type: FILTER
- # the value of filter button. Suggestion is to leave fist value to name if you use single button.
+ # the value of filter button. Suggestion is to leave first value to name if you use single button.
filter: NAME
actions:
up:
@@ -76,6 +100,7 @@ detail_panel:
# click-type: unknown
# tooltip: level.gui.tips.click-to-select
2:
+ # If a button is used repeatedly then it can be mentioned by name and then defined in the 'reusable' section
2: material_button
3: material_button
4: material_button
@@ -85,6 +110,17 @@ detail_panel:
8: material_button
3:
1:
+ # In this case, the icon is defined as a TIPPED_ARROW with and enchantment applied. The format for the enchantment is
+ # define in {@link world.bentobox.bentobox.util.ItemParser} and available for POTIONS or TIPPED_ARROWs.
+ # Format TIPPED_ARROW:NAME::::QTY
+ # LEVEL, EXTENDED, SPLASH, LINGER are optional.
+ # LEVEL is a number, 1 or 2
+ # LINGER is for V1.9 servers and later
+ # Examples:
+ # TIPPED_ARROW:STRENGTH:1:EXTENDED:SPLASH:1
+ # TIPPED_ARROW:INSTANT_DAMAGE:2::LINGER:2
+ # TIPPED_ARROW:JUMP:2:NOTEXTENDED:NOSPLASH:1
+ # TIPPED_ARROW:WEAKNESS::::1 - any weakness enchantment
icon: TIPPED_ARROW:INSTANT_HEAL::::1
title: level.gui.buttons.previous.name
description: level.gui.buttons.previous.description
@@ -121,8 +157,12 @@ detail_panel:
6: material_button
7: material_button
8: material_button
+ # This is where reuable buttons are defined.
reusable:
+ # This is the name of the button that is referenced
material_button:
+ # If the icon for a button is not defined, it defaults to AIR and so effectively will not be shown.
+ # icons are usually not defined if the icon is going to be dynamically set in the panel, e.g. in this case the material will vary
#icon: STONE
title: level.gui.buttons.material.name
description: level.gui.buttons.material.description
diff --git a/src/main/resources/panels/top_panel.yml b/src/main/resources/panels/top_panel.yml
index 3b80784..05d6151 100644
--- a/src/main/resources/panels/top_panel.yml
+++ b/src/main/resources/panels/top_panel.yml
@@ -1,15 +1,28 @@
+# Name of panel used for indentification in the code
top_panel:
+ # Title of the panel shown to the user. This is a reference and the reference will be translatable in the locale file
title: level.gui.titles.top
+ # The type of panel to show. Options are INVENTORY, HOPPER, DROPPER. INVENTORY is that standard chest inventory and
+ # the others refer to the inventories shown for those items.
type: INVENTORY
+ # The background of the panel. These items will be shown if other items are not there. STAINED_GLASS_PANEs give a good effect.
background:
icon: BLACK_STAINED_GLASS_PANE
+ # Each item may have text applied to it, but usually for background items, nothing is shown.
title: "&b&r" # Empty text
border:
+ # The border of each panel may be shown as a different item.
+ # It can be used to provide a contrast to items in the panel.
icon: BLACK_STAINED_GLASS_PANE
title: "&b&r" # Empty text
+ # This tag indicates which rows in the panel must be shown. The panel will be sized vertically accordingly. This does not include the borders.
+ # This can be a list and rows must be between 1 and 6, if used.
force-shown: [2,3,4,5]
+ # The content section contains details of each item/button in the panel. The numbers indicate the rows and then then columns of each item.
content:
+ # Row number
2:
+ # Column number
5:
#icon: PLAYER_HEAD
title: level.gui.buttons.island.name
diff --git a/src/test/java/world/bentobox/level/LevelTest.java b/src/test/java/world/bentobox/level/LevelTest.java
index 656c865..5f8074a 100644
--- a/src/test/java/world/bentobox/level/LevelTest.java
+++ b/src/test/java/world/bentobox/level/LevelTest.java
@@ -72,232 +72,229 @@
*/
@SuppressWarnings("deprecation")
@RunWith(PowerMockRunner.class)
-@PrepareForTest({Bukkit.class, BentoBox.class, User.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 pluginSettings;
-
- 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;
-
- @Mock
- private PluginManager pim;
- @Mock
- private BlockConfig blockConfig;
-
- @BeforeClass
- public static void beforeClass() throws IOException {
- // Make the addon jar
- 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);
- // Copy over block config file from src folder
- fromPath = Paths.get("src/main/resources/blockconfig.yml");
- path = Paths.get("blockconfig.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
- */
- @Before
- public void setUp() throws Exception {
- // Set up plugin
- Whitebox.setInternalState(BentoBox.class, "instance", plugin);
- when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());
-
- // The database type has to be created one line before the thenReturn() to work!
- DatabaseType value = DatabaseType.JSON;
- when(plugin.getSettings()).thenReturn(pluginSettings);
- when(pluginSettings.getDatabaseType()).thenReturn(value);
-
- //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);
- addon.setSettings(new ConfigSettings());
- // 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);
- when(gameMode.getOverWorld()).thenReturn(world);
-
- // 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());
-
-
- // 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);
- when(Bukkit.getPluginManager()).thenReturn(pim);
-
- // 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();
- new File("blockconfig.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 blockconfig.yml : acidisland_world");
- verify(plugin).log("[Level] Level hooking into BSkyBlock");
- verify(cmd, times(3)).getAddon(); // 3 commands
- verify(adminCmd, times(4)).getAddon(); // Four commands
- // Placeholders
- verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_island_level"), any());
- verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_visited_island_level"), any());
- verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_points_to_next_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(IslandActivitiesListeners.class));
- verify(am).registerListener(eq(addon), any(JoinLeaveListener.class));
- }
-
- /**
- * Test method for {@link world.bentobox.level.Level#getSettings()}.
- */
- @Test
- public void testGetSettings() {
- addon.onEnable();
- ConfigSettings s = addon.getSettings();
- assertEquals(100, s.getLevelCost());
- }
+ 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 pluginSettings;
+
+ 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;
+
+ @Mock
+ private PluginManager pim;
+ @Mock
+ private BlockConfig blockConfig;
+
+ @BeforeClass
+ public static void beforeClass() throws IOException {
+ // Make the addon jar
+ 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);
+ // Copy over block config file from src folder
+ fromPath = Paths.get("src/main/resources/blockconfig.yml");
+ path = Paths.get("blockconfig.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
+ */
+ @Before
+ public void setUp() throws Exception {
+ // Set up plugin
+ Whitebox.setInternalState(BentoBox.class, "instance", plugin);
+ when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());
+
+ // The database type has to be created one line before the thenReturn() to work!
+ DatabaseType value = DatabaseType.JSON;
+ when(plugin.getSettings()).thenReturn(pluginSettings);
+ when(pluginSettings.getDatabaseType()).thenReturn(value);
+
+ // 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);
+ addon.setSettings(new ConfigSettings());
+ // 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);
+ when(gameMode.getOverWorld()).thenReturn(world);
+
+ // 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());
+
+ // 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);
+ when(Bukkit.getPluginManager()).thenReturn(pim);
+
+ // 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();
+ new File("blockconfig.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 blockconfig.yml : acidisland_world");
+ verify(plugin).log("[Level] Level hooking into BSkyBlock");
+ verify(cmd, times(3)).getAddon(); // 3 commands
+ verify(adminCmd, times(5)).getAddon(); // Five commands
+ // Placeholders
+ verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_island_level"), any());
+ verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_visited_island_level"), any());
+ verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_points_to_next_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(IslandActivitiesListeners.class));
+ verify(am).registerListener(eq(addon), any(JoinLeaveListener.class));
+ }
+
+ /**
+ * Test method for {@link world.bentobox.level.Level#getSettings()}.
+ */
+ @Test
+ public void testGetSettings() {
+ addon.onEnable();
+ ConfigSettings s = addon.getSettings();
+ assertEquals(100, s.getLevelCost());
+ }
}
diff --git a/src/test/java/world/bentobox/level/LevelsManagerTest.java b/src/test/java/world/bentobox/level/LevelsManagerTest.java
index 5b18aa5..551c573 100644
--- a/src/test/java/world/bentobox/level/LevelsManagerTest.java
+++ b/src/test/java/world/bentobox/level/LevelsManagerTest.java
@@ -70,7 +70,7 @@
*
*/
@RunWith(PowerMockRunner.class)
-@PrepareForTest({Bukkit.class, BentoBox.class, DatabaseSetup.class, PanelBuilder.class})
+@PrepareForTest({ Bukkit.class, BentoBox.class, DatabaseSetup.class, PanelBuilder.class })
public class LevelsManagerTest {
@Mock
@@ -82,7 +82,6 @@ public class LevelsManagerTest {
@Mock
private Settings pluginSettings;
-
// Class under test
private LevelsManager lm;
@Mock
@@ -114,18 +113,17 @@ public class LevelsManagerTest {
@Mock
private BukkitScheduler scheduler;
-
-
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() {
- // This has to be done beforeClass otherwise the tests will interfere with each other
- handler = mock(AbstractDatabaseHandler.class);
- // Database
- PowerMockito.mockStatic(DatabaseSetup.class);
- DatabaseSetup dbSetup = mock(DatabaseSetup.class);
- when(DatabaseSetup.getDatabase()).thenReturn(dbSetup);
- when(dbSetup.getHandler(any())).thenReturn(handler);
+ // This has to be done beforeClass otherwise the tests will interfere with each
+ // other
+ handler = mock(AbstractDatabaseHandler.class);
+ // Database
+ PowerMockito.mockStatic(DatabaseSetup.class);
+ DatabaseSetup dbSetup = mock(DatabaseSetup.class);
+ when(DatabaseSetup.getDatabase()).thenReturn(dbSetup);
+ when(dbSetup.getHandler(any())).thenReturn(handler);
}
/**
@@ -162,10 +160,9 @@ public void setUp() throws Exception {
when(island.getMemberSet()).thenReturn(iset);
when(island.getOwner()).thenReturn(uuid);
when(island.getWorld()).thenReturn(world);
- when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString());
+ when(island.getUniqueId()).thenReturn(uuid.toString());
// Default to uuid's being island owners
- when(im.isOwner(eq(world), any())).thenReturn(true);
- when(im.getOwner(any(), any(UUID.class))).thenAnswer(in -> in.getArgument(1, UUID.class));
+ when(im.hasIsland(eq(world), any(UUID.class))).thenReturn(true);
when(im.getIsland(world, uuid)).thenReturn(island);
when(im.getIslandById(anyString())).thenReturn(Optional.of(island));
@@ -240,89 +237,92 @@ public void setUp() throws Exception {
*/
@After
public void tearDown() throws Exception {
- deleteAll(new File("database"));
- User.clearUsers();
- Mockito.framework().clearInlineMocks();
+ deleteAll(new File("database"));
+ User.clearUsers();
+ Mockito.framework().clearInlineMocks();
}
private static void deleteAll(File file) throws IOException {
- if (file.exists()) {
- Files.walk(file.toPath())
- .sorted(Comparator.reverseOrder())
- .map(Path::toFile)
- .forEach(File::delete);
- }
+ if (file.exists()) {
+ Files.walk(file.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+ }
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#calculateLevel(UUID, world.bentobox.bentobox.database.objects.Island)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#calculateLevel(UUID, world.bentobox.bentobox.database.objects.Island)}.
*/
@Test
public void testCalculateLevel() {
- Results results = new Results();
- results.setLevel(10000);
- results.setInitialLevel(3);
- lm.calculateLevel(uuid, island);
- // Complete the pipelined completable future
- cf.complete(results);
-
- assertEquals(10000L, lm.getLevelsData(island).getLevel());
- //Map tt = lm.getTopTen(world, 10);
- //assertEquals(1, tt.size());
- //assertTrue(tt.get(uuid) == 10000);
- assertEquals(10000L, lm.getIslandMaxLevel(world, uuid));
-
- results.setLevel(5000);
- lm.calculateLevel(uuid, island);
- // Complete the pipelined completable future
- cf.complete(results);
- assertEquals(5000L, lm.getLevelsData(island).getLevel());
- // Still should be 10000
- assertEquals(10000L, lm.getIslandMaxLevel(world, uuid));
+ Results results = new Results();
+ results.setLevel(10000);
+ results.setInitialLevel(3);
+ lm.calculateLevel(uuid, island);
+ // Complete the pipelined completable future
+ cf.complete(results);
+
+ assertEquals(10000L, lm.getLevelsData(island).getLevel());
+ // Map tt = lm.getTopTen(world, 10);
+ // assertEquals(1, tt.size());
+ // assertTrue(tt.get(uuid) == 10000);
+ assertEquals(10000L, lm.getIslandMaxLevel(world, uuid));
+
+ results.setLevel(5000);
+ lm.calculateLevel(uuid, island);
+ // Complete the pipelined completable future
+ cf.complete(results);
+ assertEquals(5000L, lm.getLevelsData(island).getLevel());
+ // Still should be 10000
+ assertEquals(10000L, lm.getIslandMaxLevel(world, uuid));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getInitialLevel(world.bentobox.bentobox.database.objects.Island)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getInitialLevel(world.bentobox.bentobox.database.objects.Island)}.
*/
@Test
public void testGetInitialLevel() {
- assertEquals(0,lm.getInitialLevel(island));
+ assertEquals(0, lm.getInitialLevel(island));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getIslandLevel(org.bukkit.World, java.util.UUID)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getIslandLevel(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetIslandLevel() {
- assertEquals(-5, lm.getIslandLevel(world, uuid));
+ assertEquals(-5, lm.getIslandLevel(world, uuid));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getPointsToNextString(org.bukkit.World, java.util.UUID)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getPointsToNextString(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetPointsToNextString() {
- // No island player
- assertEquals("", lm.getPointsToNextString(world, UUID.randomUUID()));
- // Player has island
- assertEquals("0", lm.getPointsToNextString(world, uuid));
+ // No island player
+ assertEquals("", lm.getPointsToNextString(world, UUID.randomUUID()));
+ // Player has island
+ assertEquals("0", lm.getPointsToNextString(world, uuid));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getIslandLevelString(org.bukkit.World, java.util.UUID)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getIslandLevelString(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetIslandLevelString() {
- assertEquals("-5", lm.getIslandLevelString(world, uuid));
+ assertEquals("-5", lm.getIslandLevelString(world, uuid));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getLevelsData(java.util.UUID)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getLevelsData(java.util.UUID)}.
*/
@Test
public void testGetLevelsData() {
- assertEquals(levelsData, lm.getLevelsData(island));
+ assertEquals(levelsData, lm.getLevelsData(island));
}
@@ -331,54 +331,59 @@ public void testGetLevelsData() {
*/
@Test
public void testFormatLevel() {
- assertEquals("123456789", lm.formatLevel(123456789L));
- when(settings.isShorthand()).thenReturn(true);
- assertEquals("123.5M", lm.formatLevel(123456789L));
- assertEquals("1.2k", lm.formatLevel(1234L));
- assertEquals("123.5G", lm.formatLevel(123456789352L));
- assertEquals("1.2T", lm.formatLevel(1234567893524L));
- assertEquals("12345.7T", lm.formatLevel(12345678345345349L));
+ assertEquals("123456789", lm.formatLevel(123456789L));
+ when(settings.isShorthand()).thenReturn(true);
+ assertEquals("123.5M", lm.formatLevel(123456789L));
+ assertEquals("1.2k", lm.formatLevel(1234L));
+ assertEquals("123.5G", lm.formatLevel(123456789352L));
+ assertEquals("1.2T", lm.formatLevel(1234567893524L));
+ assertEquals("12345.7T", lm.formatLevel(12345678345345349L));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
*/
@Test
public void testGetTopTenEmpty() {
- Map tt = lm.getTopTen(world, Level.TEN);
- assertTrue(tt.isEmpty());
+ Map tt = lm.getTopTen(world, Level.TEN);
+ assertTrue(tt.isEmpty());
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
*/
@Test
public void testGetTopTen() {
- testLoadTopTens();
- Map tt = lm.getTopTen(world, Level.TEN);
- assertFalse(tt.isEmpty());
- assertEquals(1, tt.size());
- assertEquals(1, lm.getTopTen(world, 1).size());
+ testLoadTopTens();
+ Map tt = lm.getTopTen(world, Level.TEN);
+ assertFalse(tt.isEmpty());
+ assertEquals(1, tt.size());
+ assertEquals(1, lm.getTopTen(world, 1).size());
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getWeightedTopTen(org.bukkit.World, int)}.
*/
@Test
- public void testGetTopTenNoOwners() {
- when(im.isOwner(eq(world), any())).thenReturn(false);
- testLoadTopTens();
- Map tt = lm.getTopTen(world, Level.TEN);
- assertTrue(tt.isEmpty());
+ public void testGetWeightedTopTen() {
+ testLoadTopTens();
+ Map tt = lm.getWeightedTopTen(world, Level.TEN);
+ assertFalse(tt.isEmpty());
+ assertEquals(1, tt.size());
+ assertEquals(1, lm.getTopTen(world, 1).size());
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#hasTopTenPerm(org.bukkit.World, java.util.UUID)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#hasTopTenPerm(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testHasTopTenPerm() {
- assertTrue(lm.hasTopTenPerm(world, uuid));
+ assertTrue(lm.hasTopTenPerm(world, uuid));
}
/**
@@ -386,69 +391,72 @@ public void testHasTopTenPerm() {
*/
@Test
public void testLoadTopTens() {
- ArgumentCaptor task = ArgumentCaptor.forClass(Runnable.class);
- lm.loadTopTens();
- PowerMockito.verifyStatic(Bukkit.class); // 1
- Bukkit.getScheduler();
- verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture());
- task.getValue().run();
- verify(addon).log("Generating rankings");
- verify(addon).log("Generated rankings for bskyblock-world");
+ ArgumentCaptor task = ArgumentCaptor.forClass(Runnable.class);
+ lm.loadTopTens();
+ PowerMockito.verifyStatic(Bukkit.class); // 1
+ Bukkit.getScheduler();
+ verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture());
+ task.getValue().run();
+ verify(addon).log("Generating rankings");
+ verify(addon).log("Generated rankings for bskyblock-world");
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#removeEntry(org.bukkit.World, java.util.UUID)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#removeEntry(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testRemoveEntry() {
- testLoadTopTens();
- Map tt = lm.getTopTen(world, Level.TEN);
- assertTrue(tt.containsKey(uuid));
- lm.removeEntry(world, uuid);
- tt = lm.getTopTen(world, Level.TEN);
- assertFalse(tt.containsKey(uuid));
+ testLoadTopTens();
+ Map tt = lm.getTopTen(world, Level.TEN);
+ assertTrue(tt.containsKey(uuid.toString()));
+ lm.removeEntry(world, uuid.toString());
+ tt = lm.getTopTen(world, Level.TEN);
+ assertFalse(tt.containsKey(uuid.toString()));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#setInitialIslandLevel(world.bentobox.bentobox.database.objects.Island, long)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#setInitialIslandLevel(world.bentobox.bentobox.database.objects.Island, long)}.
*/
@Test
public void testSetInitialIslandLevel() {
- lm.setInitialIslandLevel(island, Level.TEN);
- assertEquals(Level.TEN, lm.getInitialLevel(island));
+ lm.setInitialIslandLevel(island, Level.TEN);
+ assertEquals(Level.TEN, lm.getInitialLevel(island));
}
/**
- * Test method for {@link world.bentobox.level.LevelsManager#setIslandLevel(org.bukkit.World, java.util.UUID, long)}.
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#setIslandLevel(org.bukkit.World, java.util.UUID, long)}.
*/
@Test
public void testSetIslandLevel() {
- lm.setIslandLevel(world, uuid, 1234);
- assertEquals(1234, lm.getIslandLevel(world, uuid));
+ lm.setIslandLevel(world, uuid, 1234);
+ assertEquals(1234, lm.getIslandLevel(world, uuid));
}
-
/**
- * Test method for {@link world.bentobox.level.LevelsManager#getRank(World, UUID)}
+ * Test method for
+ * {@link world.bentobox.level.LevelsManager#getRank(World, UUID)}
*/
@Test
public void testGetRank() {
- lm.createAndCleanRankings(world);
- Map ttl = lm.getTopTenLists();
- Map tt = ttl.get(world).getTopTen();
- for (long i = 100; i < 150; i++) {
- tt.put(UUID.randomUUID(), i);
- }
- // Put player as lowest rank
- tt.put(uuid, 10L);
- assertEquals(51, lm.getRank(world, uuid));
- // Put player as highest rank
- tt.put(uuid, 1000L);
- assertEquals(1, lm.getRank(world, uuid));
- // Unknown UUID - lowest rank + 1
- assertEquals(52, lm.getRank(world, UUID.randomUUID()));
+ lm.createAndCleanRankings(world);
+ Map ttl = lm.getTopTenLists();
+ Map tt = ttl.get(world).getTopTen();
+ for (long i = 100; i < 150; i++) {
+ tt.put(UUID.randomUUID().toString(), i);
+ }
+ // Put island as lowest rank
+ tt.put(uuid.toString(), 10L);
+ assertEquals(51, lm.getRank(world, uuid));
+ // Put island as highest rank
+ tt.put(uuid.toString(), 1000L);
+ assertEquals(1, lm.getRank(world, uuid));
+ // Unknown UUID - lowest rank + 1
+ assertEquals(52, lm.getRank(world, UUID.randomUUID()));
}
}
diff --git a/src/test/java/world/bentobox/level/PlaceholderManagerTest.java b/src/test/java/world/bentobox/level/PlaceholderManagerTest.java
index b780b8e..56f0a1d 100644
--- a/src/test/java/world/bentobox/level/PlaceholderManagerTest.java
+++ b/src/test/java/world/bentobox/level/PlaceholderManagerTest.java
@@ -3,6 +3,7 @@
import static org.junit.Assert.assertEquals;
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;
@@ -10,9 +11,10 @@
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -44,7 +46,7 @@
*
*/
@RunWith(PowerMockRunner.class)
-@PrepareForTest({BentoBox.class})
+@PrepareForTest({ BentoBox.class })
public class PlaceholderManagerTest {
@Mock
@@ -54,7 +56,7 @@ public class PlaceholderManagerTest {
@Mock
private BentoBox plugin;
- private PlaceholderManager pm;
+ private PlaceholderManager phm;
@Mock
private PlaceholdersManager bpm;
@Mock
@@ -67,13 +69,25 @@ public class PlaceholderManagerTest {
private Island island;
@Mock
private User user;
- private Map names = new HashMap<>();
- private static final List NAMES = List.of("tasty", "bento", "fred", "bonne", "cyprien", "mael", "joe", "horacio", "steph", "vicky");
- private Map islands = new HashMap<>();
- private Map map = new HashMap<>();
+ private static final Map names = new LinkedHashMap<>();
+ static {
+ names.put(UUID.randomUUID(), "tasty");
+ names.put(UUID.randomUUID(), "bento");
+ names.put(UUID.randomUUID(), "fred");
+ names.put(UUID.randomUUID(), "bonne");
+ names.put(UUID.randomUUID(), "cyprien");
+ names.put(UUID.randomUUID(), "mael");
+ names.put(UUID.randomUUID(), "joe");
+ names.put(UUID.randomUUID(), "horacio");
+ names.put(UUID.randomUUID(), "steph");
+ names.put(UUID.randomUUID(), "vicky");
+ }
+ private Map islands = new HashMap<>();
+ private Map map = new LinkedHashMap<>();
+ private Map map2 = new LinkedHashMap<>();
private @NonNull IslandLevels data;
@Mock
- private PlayersManager players;
+ private PlayersManager pm;
/**
* @throws java.lang.Exception
@@ -83,29 +97,32 @@ public void setUp() throws Exception {
when(addon.getPlugin()).thenReturn(plugin);
// Users
- when(addon.getPlayers()).thenReturn(players);
+ when(addon.getPlayers()).thenReturn(pm);
+
// Users
when(user.getWorld()).thenReturn(world);
when(user.getLocation()).thenReturn(mock(Location.class));
- for (int i = 0; i < Level.TEN; i++) {
- UUID uuid = UUID.randomUUID();
- names.put(uuid, NAMES.get(i));
- map.put(uuid, (long)(100 - i));
+ int i = 0;
+ for (Entry n : names.entrySet()) {
+ UUID uuid = UUID.randomUUID(); // Random island ID
+ Long value = (long)(100 - i++);
+ map.put(uuid.toString(), value); // level
Island is = new Island();
- is.setOwner(uuid);
- is.setName(NAMES.get(i) + "'s island");
- islands.put(uuid, is);
-
+ is.setUniqueId(uuid.toString());
+ is.setOwner(n.getKey());
+ is.setName(n.getValue() + "'s island");
+ islands.put(uuid.toString(), is);
+ map2.put(is, value);
}
// Sort
map = map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
- when(players.getName(any())).thenAnswer((Answer) invocation -> names.getOrDefault(invocation.getArgument(0, UUID.class), "unknown"));
+ when(pm.getName(any())).thenAnswer((Answer) invocation -> names.getOrDefault(invocation.getArgument(0, UUID.class), "unknown"));
Map members = new HashMap<>();
- map.forEach((uuid, l) -> members.put(uuid, RanksManager.MEMBER_RANK));
- islands.values().forEach(i -> i.setMembers(members));
+ names.forEach((uuid, l) -> members.put(uuid, RanksManager.MEMBER_RANK));
+ islands.values().forEach(is -> is.setMembers(members));
// Placeholders manager for plugin
@@ -120,7 +137,8 @@ public void setUp() throws Exception {
// Islands
when(im.getIsland(any(World.class), any(User.class))).thenReturn(island);
when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island));
- when(im.getIsland(any(World.class), any(UUID.class))).thenAnswer((Answer) invocation -> islands.get(invocation.getArgument(1, UUID.class)));
+ when(im.getIslandById(anyString())).thenAnswer((Answer>) invocation -> Optional.of(islands.get(invocation.getArgument(0, String.class))));
+ when(im.getIslands(any(), any(UUID.class))).thenReturn(new HashSet<>(islands.values()));
when(addon.getIslands()).thenReturn(im);
// Levels Manager
@@ -129,6 +147,7 @@ public void setUp() throws Exception {
when(lm.getPointsToNextString(any(), any())).thenReturn("1234567");
when(lm.getIslandMaxLevel(any(), any())).thenReturn(987654L);
when(lm.getTopTen(world, Level.TEN)).thenReturn(map);
+ when(lm.getWeightedTopTen(world, Level.TEN)).thenReturn(map2);
when(lm.formatLevel(any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, Long.class).toString());
data = new IslandLevels("uniqueId");
@@ -136,127 +155,150 @@ public void setUp() throws Exception {
when(lm.getLevelsData(island)).thenReturn(data);
when(addon.getManager()).thenReturn(lm);
- pm = new PlaceholderManager(addon);
+ phm = new PlaceholderManager(addon);
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#PlaceholderManager(world.bentobox.level.Level)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#PlaceholderManager(world.bentobox.level.Level)}.
*/
@Test
public void testPlaceholderManager() {
- verify(addon).getPlugin();
+ verify(addon).getPlugin();
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#registerPlaceholders(world.bentobox.bentobox.api.addons.GameModeAddon)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#registerPlaceholders(world.bentobox.bentobox.api.addons.GameModeAddon)}.
*/
@Test
public void testRegisterPlaceholders() {
- pm.registerPlaceholders(gm);
- // Island Level
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_level"), any());
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_level_raw"), any());
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_total_points"), any());
-
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_points_to_next_level"), any());
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_level_max"), any());
-
- // Visited Island Level
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_visited_island_level"), any());
-
- // Register Top Ten Placeholders
- for (int i = 1; i < 11; i++) {
- // Name
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_name_" + i), any());
- // Island Name
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_island_name_" + i), any());
- // Members
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_members_" + i), any());
- // Level
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_value_" + i), any());
- }
+ phm.registerPlaceholders(gm);
+ // Island Level
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_level"), any());
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_level_raw"), any());
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_total_points"), any());
+
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_points_to_next_level"), any());
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_island_level_max"), any());
+
+ // Visited Island Level
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_visited_island_level"), any());
+
+ // Register Top Ten Placeholders
+ for (int i = 1; i < 11; i++) {
+ // Name
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_name_" + i), any());
+ // Island Name
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_island_name_" + i), any());
+ // Members
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_members_" + i), any());
+ // Level
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_top_value_" + i), any());
+ }
+
+ // Personal rank
+ verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_rank_value"), any());
- // Personal rank
- verify(bpm).registerPlaceholder(eq(addon), eq("aoneblock_rank_value"), any());
-
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#getRankName(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getRankName(org.bukkit.World, int)}.
*/
@Test
public void testGetRankName() {
- // Test extremes
- assertEquals("tasty", pm.getRankName(world, 0));
- assertEquals("vicky", pm.getRankName(world, 100));
- // Test the ranks
- int rank = 1;
- for (String name : NAMES) {
- assertEquals(name, pm.getRankName(world, rank++));
- }
-
+ // Test extremes
+ assertEquals("tasty", phm.getRankName(world, 0, false));
+ assertEquals("vicky", phm.getRankName(world, 100, false));
+ // Test the ranks
+ int rank = 1;
+ for (String name : names.values()) {
+ assertEquals(name, phm.getRankName(world, rank++, false));
+ }
+
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#getRankIslandName(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getRankIslandName(org.bukkit.World, int)}.
*/
@Test
public void testGetRankIslandName() {
- // Test extremes
- assertEquals("tasty's island", pm.getRankIslandName(world, 0));
- assertEquals("vicky's island", pm.getRankIslandName(world, 100));
- // Test the ranks
- int rank = 1;
- for (String name : NAMES) {
- assertEquals(name + "'s island", pm.getRankIslandName(world, rank++));
- }
-
+ // Test extremes
+ assertEquals("tasty's island", phm.getRankIslandName(world, 0, false));
+ assertEquals("vicky's island", phm.getRankIslandName(world, 100, false));
+ // Test the ranks
+ int rank = 1;
+ for (String name : names.values()) {
+ assertEquals(name + "'s island", phm.getRankIslandName(world, rank++, false));
+ }
+
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#getRankMembers(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getRankMembers(org.bukkit.World, int)}.
*/
@Test
public void testGetRankMembers() {
- // Test extremes
- check(1, pm.getRankMembers(world, 0));
- check(2, pm.getRankMembers(world, 100));
- // Test the ranks
- for (int rank = 1; rank < 11; rank++) {
- check(3, pm.getRankMembers(world, rank));
- }
+ // Test extremes
+ check(1, phm.getRankMembers(world, 0, false));
+ check(2, phm.getRankMembers(world, 100, false));
+ // Test the ranks
+ for (int rank = 1; rank < 11; rank++) {
+ check(3, phm.getRankMembers(world, rank, false));
+ }
}
-
+
void check(int indicator, String list) {
- for (String n : NAMES) {
- assertTrue(n + " is missing for twst " + indicator, list.contains(n));
- }
+ for (String n : names.values()) {
+ assertTrue(n + " is missing for test " + indicator, list.contains(n));
+ }
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#getRankLevel(org.bukkit.World, int)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getRankLevel(org.bukkit.World, int)}.
*/
@Test
public void testGetRankLevel() {
- // Test extremes
- assertEquals("100", pm.getRankLevel(world, 0));
- assertEquals("91", pm.getRankLevel(world, 100));
- // Test the ranks
- for (int rank = 1; rank < 11; rank++) {
- assertEquals(String.valueOf(101 - rank), pm.getRankLevel(world, rank));
- }
-
+ // Test extremes
+ assertEquals("100", phm.getRankLevel(world, 0, false));
+ assertEquals("91", phm.getRankLevel(world, 100, false));
+ // Test the ranks
+ for (int rank = 1; rank < 11; rank++) {
+ assertEquals(String.valueOf(101 - rank), phm.getRankLevel(world, rank, false));
+ }
+
}
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#getVisitedIslandLevel(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.api.user.User)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getRankLevel(org.bukkit.World, int)}.
+ */
+ @Test
+ public void testGetWeightedRankLevel() {
+ // Test extremes
+ assertEquals("100", phm.getRankLevel(world, 0, true));
+ assertEquals("91", phm.getRankLevel(world, 100, true));
+ // Test the ranks
+ for (int rank = 1; rank < 11; rank++) {
+ assertEquals(String.valueOf(101 - rank), phm.getRankLevel(world, rank, true));
+ }
+
+ }
+
+ /**
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getVisitedIslandLevel(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.api.user.User)}.
*/
@Test
public void testGetVisitedIslandLevelNullUser() {
- assertEquals("", pm.getVisitedIslandLevel(gm, null));
-
+ assertEquals("", phm.getVisitedIslandLevel(gm, null));
+
}
-
+
/**
* Test method for {@link world.bentobox.level.PlaceholderManager#getVisitedIslandLevel(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.api.user.User)}.
*/
@@ -264,26 +306,27 @@ public void testGetVisitedIslandLevelNullUser() {
public void testGetVisitedIslandLevelUserNotInWorld() {
// Another world
when(user.getWorld()).thenReturn(mock(World.class));
- assertEquals("", pm.getVisitedIslandLevel(gm, user));
+ assertEquals("", phm.getVisitedIslandLevel(gm, user));
}
-
+
/**
- * Test method for {@link world.bentobox.level.PlaceholderManager#getVisitedIslandLevel(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.api.user.User)}.
+ * Test method for
+ * {@link world.bentobox.level.PlaceholderManager#getVisitedIslandLevel(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.api.user.User)}.
*/
@Test
public void testGetVisitedIslandLevel() {
- assertEquals("1234567", pm.getVisitedIslandLevel(gm, user));
-
+ assertEquals("1234567", phm.getVisitedIslandLevel(gm, user));
+
}
-
+
/**
* Test method for {@link world.bentobox.level.PlaceholderManager#getVisitedIslandLevel(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.api.user.User)}.
*/
@Test
public void testGetVisitedIslandLevelNoIsland() {
when(im.getIslandAt(any(Location.class))).thenReturn(Optional.empty());
- assertEquals("0", pm.getVisitedIslandLevel(gm, user));
+ assertEquals("0", phm.getVisitedIslandLevel(gm, user));
}
diff --git a/src/test/java/world/bentobox/level/calculators/EquationEvaluatorTest.java b/src/test/java/world/bentobox/level/calculators/EquationEvaluatorTest.java
new file mode 100644
index 0000000..9bcb5e2
--- /dev/null
+++ b/src/test/java/world/bentobox/level/calculators/EquationEvaluatorTest.java
@@ -0,0 +1,37 @@
+package world.bentobox.level.calculators;
+
+import static org.junit.Assert.assertEquals;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+/**
+ * Test the equation evaluation
+ */
+public class EquationEvaluatorTest {
+
+ /**
+ * Test method for {@link world.bentobox.level.calculators.EquationEvaluator#eval(java.lang.String)}.
+ * @throws ParseException
+ */
+ @Test
+ public void testEval() throws ParseException {
+ assertEquals(4D, EquationEvaluator.eval("2+2"), 0D);
+ assertEquals(0D, EquationEvaluator.eval("2-2"), 0D);
+ assertEquals(1D, EquationEvaluator.eval("2/2"), 0D);
+ assertEquals(4D, EquationEvaluator.eval("2*2"), 0D);
+ assertEquals(8D, EquationEvaluator.eval("2+2+2+2"), 0D);
+ assertEquals(5D, EquationEvaluator.eval("2.5+2.5"), 0D);
+ assertEquals(1.414, EquationEvaluator.eval("sqrt(2)"), 0.001D);
+ assertEquals(3.414, EquationEvaluator.eval("2 + sqrt(2)"), 0.001D);
+ assertEquals(0D, EquationEvaluator.eval("sin(0)"), 0.1D);
+ assertEquals(1D, EquationEvaluator.eval("cos(0)"), 0.1D);
+ assertEquals(0D, EquationEvaluator.eval("tan(0)"), 0.1D);
+ assertEquals(0D, EquationEvaluator.eval("log(1)"), 0.1D);
+ assertEquals(27D, EquationEvaluator.eval("3^3"), 0.D);
+ assertEquals(84.70332D, EquationEvaluator.eval("3^3 + 2 + 2.65 * (3 / 4) - sin(45) * log(10) + 55.344"),
+ 0.0001D);
+
+ }
+}
diff --git a/src/test/java/world/bentobox/level/commands/AdminStatsCommandTest.java b/src/test/java/world/bentobox/level/commands/AdminStatsCommandTest.java
new file mode 100644
index 0000000..21d26d0
--- /dev/null
+++ b/src/test/java/world/bentobox/level/commands/AdminStatsCommandTest.java
@@ -0,0 +1,183 @@
+package world.bentobox.level.commands;
+
+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.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemFactory;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+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.api.addons.GameModeAddon;
+import world.bentobox.bentobox.api.commands.CompositeCommand;
+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.LevelsManager;
+import world.bentobox.level.objects.TopTenData;
+
+/**
+ * @author tastybento
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ Bukkit.class, BentoBox.class })
+public class AdminStatsCommandTest {
+
+ @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 AdminStatsCommand asc;
+ private TopTenData ttd;
+ @Mock
+ private LevelsManager manager;
+ @Mock
+ private Server server;
+
+ @Before
+ public void setUp() {
+ // Set up plugin
+ BentoBox plugin = mock(BentoBox.class);
+ Whitebox.setInternalState(BentoBox.class, "instance", plugin);
+ User.setPlugin(plugin);
+ when(addon.getPlugin()).thenReturn(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.getManager()).thenReturn(manager);
+ // User
+ uuid = UUID.randomUUID();
+ when(user.getUniqueId()).thenReturn(uuid);
+ when(user.getTranslation(any())).thenAnswer(invocation -> invocation.getArgument(0, String.class));
+
+ // Bukkit
+ PowerMockito.mockStatic(Bukkit.class);
+ when(Bukkit.getServer()).thenReturn(server);
+ // Mock item factory (for itemstacks)
+ ItemFactory itemFactory = mock(ItemFactory.class);
+ ItemMeta itemMeta = mock(ItemMeta.class);
+ when(itemFactory.getItemMeta(any())).thenReturn(itemMeta);
+ when(server.getItemFactory()).thenReturn(itemFactory);
+ when(Bukkit.getItemFactory()).thenReturn(itemFactory);
+
+ // Top ten
+ ttd = new TopTenData(world);
+ Map topten = new HashMap<>();
+ Random r = new Random();
+ for (int i = 0; i < 1000; i++) {
+ topten.put(UUID.randomUUID().toString(), r.nextLong(20000));
+ }
+ ttd.setTopTen(topten);
+ asc = new AdminStatsCommand(addon, ic);
+ }
+
+ @After
+ public void tearDown() {
+ User.clearUsers();
+ }
+
+ /**
+ * Test method for
+ * {@link world.bentobox.level.commands.AdminStatsCommand#setup()}.
+ */
+ @Test
+ public void testSetup() {
+ assertEquals("bskyblock.admin.stats", asc.getPermission());
+ assertFalse(asc.isOnlyPlayer());
+ assertEquals("admin.stats.description", asc.getDescription());
+
+ }
+
+ /**
+ * Test method for
+ * {@link world.bentobox.level.commands.AdminStatsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
+ */
+ @Test
+ public void testExecuteUserStringListOfString() {
+ assertFalse(asc.execute(user, "", List.of()));
+ verify(user).sendMessage("admin.stats.title");
+ verify(user).sendMessage("admin.stats.no-data");
+ }
+
+ /**
+ * Test method for
+ * {@link world.bentobox.level.commands.AdminStatsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
+ */
+ @Test
+ public void testExecuteUserStringListOfStringLevels() {
+ Map map = new HashMap<>();
+ map.put(world, ttd);
+ when(manager.getTopTenLists()).thenReturn(map);
+ assertTrue(asc.execute(user, "", List.of()));
+ verify(user).sendMessage("admin.stats.title");
+ verify(user, never()).sendMessage("admin.stats.no-data");
+ }
+
+}
diff --git a/src/test/java/world/bentobox/level/commands/AdminTopRemoveCommandTest.java b/src/test/java/world/bentobox/level/commands/AdminTopRemoveCommandTest.java
new file mode 100644
index 0000000..9e82384
--- /dev/null
+++ b/src/test/java/world/bentobox/level/commands/AdminTopRemoveCommandTest.java
@@ -0,0 +1,208 @@
+package world.bentobox.level.commands;
+
+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.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemFactory;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+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.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.LevelsManager;
+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 TopTenData ttd;
+ @Mock
+ private LevelsManager manager;
+ @Mock
+ private Server server;
+
+ @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.getManager()).thenReturn(manager);
+ // User
+ uuid = UUID.randomUUID();
+ when(user.getUniqueId()).thenReturn(uuid);
+ when(user.getTranslation(any())).thenAnswer(invocation -> invocation.getArgument(0, String.class));
+ // Island
+ when(island.getUniqueId()).thenReturn(uuid.toString());
+ when(island.getOwner()).thenReturn(uuid);
+ // Island Manager
+ when(plugin.getIslands()).thenReturn(im);
+ when(im.getIslands(any(), any(User.class))).thenReturn(Set.of(island));
+ when(im.getIslands(any(), any(UUID.class))).thenReturn(Set.of(island));
+
+ // Bukkit
+ PowerMockito.mockStatic(Bukkit.class);
+ when(Bukkit.getServer()).thenReturn(server);
+ // Mock item factory (for itemstacks)
+ ItemFactory itemFactory = mock(ItemFactory.class);
+ ItemMeta itemMeta = mock(ItemMeta.class);
+ when(itemFactory.getItemMeta(any())).thenReturn(itemMeta);
+ when(server.getItemFactory()).thenReturn(itemFactory);
+ when(Bukkit.getItemFactory()).thenReturn(itemFactory);
+
+ 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("commands.help.header", TextVariables.LABEL, "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("general.errors.unknown-player", TextVariables.NAME, "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(manager).removeEntry(world, uuid.toString());
+ verify(user).sendMessage("general.success");
+ }
+
+}
diff --git a/src/test/java/world/bentobox/level/commands/admin/AdminTopRemoveCommandTest.java b/src/test/java/world/bentobox/level/commands/admin/AdminTopRemoveCommandTest.java
deleted file mode 100644
index 71070a4..0000000
--- a/src/test/java/world/bentobox/level/commands/admin/AdminTopRemoveCommandTest.java
+++ /dev/null
@@ -1,199 +0,0 @@
-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.Server;
-import org.bukkit.World;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemFactory;
-import org.bukkit.inventory.meta.ItemMeta;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-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.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.LevelsManager;
-import world.bentobox.level.commands.AdminTopRemoveCommand;
-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 TopTenData ttd;
- @Mock
- private LevelsManager manager;
- @Mock
- private Server server;
-
- @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.getManager()).thenReturn(manager);
- // User
- uuid = UUID.randomUUID();
- when(user.getUniqueId()).thenReturn(uuid);
- when(user.getTranslation(any())).thenAnswer(invocation -> invocation.getArgument(0, String.class));
-
- // Bukkit
- PowerMockito.mockStatic(Bukkit.class);
- when(Bukkit.getServer()).thenReturn(server);
- // Mock item factory (for itemstacks)
- ItemFactory itemFactory = mock(ItemFactory.class);
- ItemMeta itemMeta = mock(ItemMeta.class);
- when(itemFactory.getItemMeta(any())).thenReturn(itemMeta);
- when(server.getItemFactory()).thenReturn(itemFactory);
- when(Bukkit.getItemFactory()).thenReturn(itemFactory);
-
-
- 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("commands.help.header", TextVariables.LABEL, "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("general.errors.unknown-player", TextVariables.NAME, "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(manager).removeEntry(any(World.class), eq(uuid));
- verify(user).sendMessage("general.success");
- }
-
-}