diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7bf088..31e7bf1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,24 +1,23 @@ -name: SonarCloud +name: Build on: push: branches: - develop - - master pull_request: types: [opened, synchronize, reopened] jobs: build: - name: Build and analyze + name: Build runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: 17 - distribution: 'zulu' # Alternative distribution options are available. + distribution: 'adopt' + java-version: '21' - name: Cache SonarCloud packages uses: actions/cache@v3 with: @@ -36,3 +35,12 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=BentoBoxWorld_Upgrades + - run: mvn --batch-mode clean org.jacoco:jacoco-maven-plugin:prepare-agent install + - run: mkdir staging && cp target/*.jar staging + - name: Save artifacts + uses: actions/upload-artifact@v3 + with: + name: Package + path: staging + + diff --git a/pom.xml b/pom.xml index 19d26ff..57487dd 100644 --- a/pom.xml +++ b/pom.xml @@ -39,13 +39,13 @@ UTF-8 UTF-8 - 16 + 21 2.0.9 - 1.16.5-R0.1-SNAPSHOT - 1.20.0-SNAPSHOT - 2.9.0 + 1.21.3-R0.1-SNAPSHOT + 3.0.0-SNAPSHOT + 2.17.0 1.19.1-SNAPSHOT 1.7 @@ -216,17 +216,20 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.8.1 ${java.version} + org.apache.maven.plugins maven-surefire-plugin 3.0.0-M5 + + ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED @@ -244,13 +247,17 @@ --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.logging/java.util.logging=ALL-UNNAMED + --add-opens java.base/java.lang.ref=ALL-UNNAMED + --add-opens java.base/java.util.jar=ALL-UNNAMED + --add-opens java.base/java.util.zip=ALL-UNNAMED + --add-opens=java.base/java.security=ALL-UNNAMED org.apache.maven.plugins maven-jar-plugin - 3.1.0 + 3.2.0 org.apache.maven.plugins @@ -262,30 +269,37 @@ maven-deploy-plugin 2.8.2 - + org.jacoco jacoco-maven-plugin - 0.8.3 + 0.8.10 true **/*Names* + + org/bukkit/Material* - pre-unit-test + prepare-agent prepare-agent - post-unit-test + report report + + + XML + + diff --git a/src/main/java/world/bentobox/upgrades/UpgradesAddon.java b/src/main/java/world/bentobox/upgrades/UpgradesAddon.java index dc23006..6ea2675 100644 --- a/src/main/java/world/bentobox/upgrades/UpgradesAddon.java +++ b/src/main/java/world/bentobox/upgrades/UpgradesAddon.java @@ -33,6 +33,29 @@ public class UpgradesAddon extends Addon { + private Settings settings; + + private boolean hooked; + + private UpgradesManager upgradesManager; + + private Set upgrade = new HashSet<>(); + + private Database database = new Database<>(this, UpgradesData.class); + + private Map upgradesCache = new HashMap<>(); + + private Level levelAddon; + + private Limits limitsAddon; + + private VaultHook vault; + + public final static Flag UPGRADES_RANK_RIGHT = new Flag.Builder("UPGRADES_RANK_RIGHT", Material.GOLD_INGOT) + .type(Flag.Type.PROTECTION).mode(Flag.Mode.BASIC) + .clickHandler(new CycleClick("UPGRADES_RANK_RIGHT", RanksManager.MEMBER_RANK, RanksManager.OWNER_RANK)) + .defaultRank(RanksManager.MEMBER_RANK).build(); + @Override public void onLoad() { super.onLoad(); @@ -67,11 +90,6 @@ public void onEnable() { this.upgradesManager = new UpgradesManager(this); this.upgradesManager.addGameModes(hookedGameModes); - this.upgrade = new HashSet<>(); - - this.database = new Database<>(this, UpgradesData.class); - this.upgradesCache = new HashMap<>(); - Optional level = this.getAddonByName("Level"); if (!level.isPresent()) { @@ -204,30 +222,4 @@ public void registerUpgrade(Upgrade upgrade) { this.upgrade.add(upgrade); } - private Settings settings; - - private boolean hooked; - - private UpgradesManager upgradesManager; - - private Set upgrade; - - private Database database; - - private Map upgradesCache; - - private Level levelAddon; - - private Limits limitsAddon; - - private VaultHook vault; - - public final static Flag UPGRADES_RANK_RIGHT = - new Flag.Builder("UPGRADES_RANK_RIGHT", Material.GOLD_INGOT) - .type(Flag.Type.PROTECTION) - .mode(Flag.Mode.BASIC) - .clickHandler(new CycleClick("UPGRADES_RANK_RIGHT", RanksManager.MEMBER_RANK, RanksManager.OWNER_RANK)) - .defaultRank(RanksManager.MEMBER_RANK) - .build(); - } \ No newline at end of file diff --git a/src/main/java/world/bentobox/upgrades/UpgradesManager.java b/src/main/java/world/bentobox/upgrades/UpgradesManager.java index 881e1f8..87746ce 100644 --- a/src/main/java/world/bentobox/upgrades/UpgradesManager.java +++ b/src/main/java/world/bentobox/upgrades/UpgradesManager.java @@ -21,23 +21,59 @@ import world.bentobox.limits.objects.IslandBlockCount; import world.bentobox.upgrades.config.Settings; +/** + * Manages upgrades in the BentoBox Upgrades addon. + *

+ * The {@code UpgradesManager} class handles the retrieval and management of upgrade tiers, + * permissions, and configurations for different upgrade types. It provides methods to + * interact with upgrade data, such as determining upgrade effects, costs, and eligibility. + *

+ * + *

+ * This class supports multiple upgrade types, including range upgrades, block limits upgrades, + * entity limits upgrades, entity group limits upgrades, and command upgrades. It also manages + * customization for different game modes and tracks compatibility with specific worlds. + *

+ */ public class UpgradesManager { + /** + * Constructs a new {@code UpgradesManager}. + * + * @param addon The {@link UpgradesAddon} instance associated with this manager. + */ public UpgradesManager(UpgradesAddon addon) { this.addon = addon; this.hookedGameModes = new HashSet<>(); } + /** + * Adds the specified game modes to the list of hooked game modes. + * + * @param gameModes A list of game mode names to be hooked. + */ protected void addGameModes(List gameModes) { this.hookedGameModes.addAll(gameModes); } + /** + * Checks if the manager can operate in the specified world. + * + * @param world The world to check. + * @return {@code true} if operations are allowed in the world; {@code false} otherwise. + */ public boolean canOperateInWorld(World world) { Optional addon = this.addon.getPlugin().getIWM().getAddon(world); return addon.isPresent() && this.hookedGameModes.contains(addon.get().getDescription().getName()); } + /** + * Retrieves the level of the specified island. Level addon must be available. + * + * @param island The island whose level is to be retrieved. + * @return The level of the island, or 0 if invalid or not found. + */ public int getIslandLevel(Island island) { if (!this.addon.isLevelProvided()) return 0; @@ -46,17 +82,17 @@ public int getIslandLevel(Island island) { this.addon.logError("Island couldn't be found"); return 0; } + // Get the island's level + return (int) this.addon.getLevelAddon().getManager().getLevelsData(island).getLevel(); - int islandLevel = (int) this.addon.getLevelAddon().getIslandLevel(island.getWorld(), island.getOwner()); - - if (islandLevel < 0) { - this.addon.logWarning("Island " + island.getUniqueId() + " has an invalid level: " + islandLevel); - islandLevel = 0; - } - - return islandLevel; } + /** + * Retrieves all range upgrade tiers for the specified world. + * + * @param world The world for which range upgrade tiers are requested. + * @return A list of {@link Settings.UpgradeTier} representing the range upgrade tiers. + */ public List getAllRangeUpgradeTiers(World world) { String name = this.addon.getPlugin().getIWM().getAddon(world).map(a -> a.getDescription().getName()) .orElse(null); @@ -86,6 +122,12 @@ public List getAllRangeUpgradeTiers(World world) { return tierList; } + /** + * Retrieves all block limits upgrade tiers for the specified world. + * + * @param world The world for which block limits upgrade tiers are requested. + * @return A map of {@link Material} to a list of {@link Settings.UpgradeTier}. + */ public Map> getAllBlockLimitsUpgradeTiers(World world) { String name = this.addon.getPlugin().getIWM().getAddon(world).map(a -> a.getDescription().getName()) .orElse(null); @@ -125,6 +167,12 @@ public Map> getAllBlockLimitsUpgradeTiers(W return tierList; } + /** + * Retrieves all entity limits upgrade tiers for the specified world. + * + * @param world The world for which entity limits upgrade tiers are requested. + * @return A map of {@link EntityType} to a list of {@link Settings.UpgradeTier}. + */ public Map> getAllEntityLimitsUpgradeTiers(World world) { String name = this.addon.getPlugin().getIWM().getAddon(world).map(a -> a.getDescription().getName()) .orElse(null); @@ -164,6 +212,12 @@ public Map> getAllEntityLimitsUpgradeTier return tierList; } + /** + * Retrieves all entity group limits upgrade tiers for the specified world. + * + * @param world The world for which entity group limits upgrade tiers are requested. + * @return A map of entity group names to a list of {@link Settings.UpgradeTier}. + */ public Map> getAllEntityGroupLimitsUpgradeTiers(World world) { String name = this.addon.getPlugin().getIWM().getAddon(world).map(a -> a.getDescription().getName()) .orElse(null); @@ -203,6 +257,12 @@ public Map> getAllEntityGroupLimitsUpgradeTie return tierList; } + /** + * Retrieves all command upgrade tiers for the specified world. + * + * @param world The world for which command upgrade tiers are requested. + * @return A map of command names to a list of {@link Settings.CommandUpgradeTier}. + */ public Map> getAllCommandUpgradeTiers(World world) { String name = this.addon.getPlugin().getIWM().getAddon(world).map(a -> a.getDescription().getName()) .orElse(null); @@ -242,6 +302,13 @@ public Map> getAllCommandUpgradeTiers( return tierList; } + /** + * Retrieves the tier information for a range upgrade at the specified level and world. + * + * @param rangeLevel The level of the range upgrade. + * @param world The world in which the upgrade is being applied. + * @return The {@link Settings.UpgradeTier} for the specified level and world, or {@code null} if not found. + */ public Settings.UpgradeTier getRangeUpgradeTier(int rangeLevel, World world) { List tierList = this.getAllRangeUpgradeTiers(world); @@ -344,6 +411,15 @@ public Settings.CommandUpgradeTier getCommandUpgradeTier(String cmd, int cmdLeve return null; } + /** + * Retrieves detailed information about the range upgrade tier, such as costs and effects. + * + * @param rangeLevel The level of the range upgrade. + * @param islandLevel The level of the island. + * @param numberPeople The number of people on the island. + * @param world The world in which the upgrade is being applied. + * @return A map containing information about the range upgrade tier. + */ public Map getRangeUpgradeInfos(int rangeLevel, int islandLevel, int numberPeople, World world) { Settings.UpgradeTier rangeUpgradeTier = this.getRangeUpgradeTier(rangeLevel, world); @@ -553,6 +629,12 @@ public Boolean isCommantConsole(String cmd, int cmdLevel, World world) { return cmdUpgradeTier.getConsole(); } + /** + * Retrieves the current entity limits for the specified island. + * + * @param island The island for which entity limits are requested. + * @return A map of {@link EntityType} to their respective limits. + */ public Map getEntityLimits(Island island) { if (!this.addon.isLimitsProvided()) return Collections.emptyMap(); @@ -564,6 +646,12 @@ public Map getEntityLimits(Island island) { return entityLimits; } + /** + * Retrieves the current entity group limits for the specified island. + * + * @param island The island for which entity group limits are requested. + * @return A map of group names to their respective limits. + */ public Map getEntityGroupLimits(Island island) { if (!this.addon.isLimitsProvided()) return Collections.emptyMap(); diff --git a/src/main/java/world/bentobox/upgrades/api/Upgrade.java b/src/main/java/world/bentobox/upgrades/api/Upgrade.java index 08850c0..3efef4d 100644 --- a/src/main/java/world/bentobox/upgrades/api/Upgrade.java +++ b/src/main/java/world/bentobox/upgrades/api/Upgrade.java @@ -2,7 +2,6 @@ import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.TreeMap; import java.util.UUID; @@ -10,7 +9,6 @@ import net.milkbowl.vault.economy.EconomyResponse; import world.bentobox.bentobox.api.addons.Addon; -import world.bentobox.bentobox.api.addons.Addon.State; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.upgrades.UpgradesAddon; @@ -24,259 +22,253 @@ */ public abstract class Upgrade { - /** - * Initialize the upgrade object you should call it in your init methode - * - * @param addon This should be your addon - * @param name This is the name for the upgrade that will be used in the - * DataBase - * @param displayName This is the name that is shown to the user - * @param icon This is the icon shown to the user - */ - public Upgrade(Addon addon, String name, String displayName, Material icon) { - this.name = name; - this.displayName = displayName; - this.icon = icon; - this.addon = addon; - - this.playerCache = new TreeMap<>(); - this.ownDescription = new TreeMap<>(); - - Optional islandUpgrade = this.addon.getAddonByName("upgrades"); - if (!islandUpgrade.isPresent()) { - this.addon.logError("Island Upgrade Addon couldn't be found"); - this.addon.setState(State.DISABLED); - } else { - this.upgradesAddon = (UpgradesAddon) islandUpgrade.get(); - this.addon.log("Added upgrade -> " + name); - } - } - - /** - * This function is called every times a user open the interface You should make - * it update the upgradeValues - * - * @param user This is the user that ask for the interface - * @param island This is the island concerned by the interface - */ - public abstract void updateUpgradeValue(User user, Island island); - - /** - * This function is called every times a user open the interface If it return - * false, the upgrade won't be showed to the user - * - * @param user This is the user that ask for the interface - * @param island This is the island concerned by the interface - * @return If true, then upgrade is shown else, it is hided - */ - public boolean isShowed(User user, Island island) { - return true; - } - - /** - * This function return true if the user can upgrade for this island. You can - * override it and call the super. - * - * The super test for islandLevel and for money - * - * @param user This is the user that try to upgrade - * @param island This is the island that is concerned - * @return Can upgrade - */ - public boolean canUpgrade(User user, Island island) { - UpgradeValues upgradeValues = this.getUpgradeValues(user); - boolean can = true; - - if (this.upgradesAddon.isLevelProvided() - && this.upgradesAddon.getUpgradesManager().getIslandLevel(island) < upgradeValues.getIslandLevel()) { - - can = false; - } - - if (this.upgradesAddon.isVaultProvided() - && !this.upgradesAddon.getVaultHook().has(user, upgradeValues.getMoneyCost())) { - - can = false; - } - - return can; - } - - /** - * This function is called when the user is upgrading for the island It is - * called after the canUpgrade function - * - * You should call the super to update the balance of the user as well as the - * level is the island - * - * @param user This is the user that do the upgrade - * @param island This is the island that is concerned - * @return If upgrade was successful - */ - public boolean doUpgrade(User user, Island island) { - UpgradeValues upgradeValues = this.getUpgradeValues(user); - - if (this.upgradesAddon.isVaultProvided()) { - EconomyResponse response = this.upgradesAddon.getVaultHook().withdraw(user, upgradeValues.getMoneyCost()); - if (!response.transactionSuccess()) { - this.addon.logWarning( - "User Money withdrawing failed user: " + user.getName() + " reason: " + response.errorMessage); - user.sendMessage("upgrades.error.costwithdraw"); - return false; - } - } - - UpgradesData data = this.upgradesAddon.getUpgradesLevels(island.getUniqueId()); - data.setUpgradeLevel(this.name, data.getUpgradeLevel(this.name) + 1); - - return true; - } - - /** - * @return The name that is used for the DataBase - */ - public String getName() { - return this.name; - } - - /** - * @return The name that is displayed to the user - */ - public String getDisplayName() { - return this.displayName; - } - - /** - * @param displayName To update the name to display to the user - */ - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - /** - * @return The icon that is displayed to the user - */ - public Material getIcon() { - return this.icon; - } - - public int getUpgradeLevel(Island island) { - return this.upgradesAddon.getUpgradesLevels(island.getUniqueId()).getUpgradeLevel(this.name); - } - - /** - * @return The actual description for the user - */ - public String getOwnDescription(User user) { - return this.ownDescription.get(user.getUniqueId()); - } - - /** - * @param user User to set the description - * @param description Description to set - */ - public void setOwnDescription(User user, String description) { - this.ownDescription.put(user.getUniqueId(), description); - } - - /** - * @return The actual upgradeValues - */ - public UpgradeValues getUpgradeValues(User user) { - return this.playerCache.get(user.getUniqueId()); - } - - /** - * @param upgrade Values to upgrades - */ - public void setUpgradeValues(User user, UpgradeValues upgrade) { - this.playerCache.put(user.getUniqueId(), upgrade); - } - - /** - * Function that get the upgrades addon You should use it to use the upgrades - * addon methods - * - * @return UpgradesAddon - */ - public UpgradesAddon getUpgradesAddon() { - return this.upgradesAddon; - } - - /** - * You shouldn't override this function - */ - @Override - public int hashCode() { - return Objects.hash(name); - } - - /** - * You shouldn't override this function - */ - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (this == obj) { - return true; - } - if (!(obj instanceof Upgrade)) { - return false; - } - Upgrade other = (Upgrade) obj; - return Objects.equals(name, other.name); - } - - private final String name; - private String displayName; - private Material icon; - private Addon addon; - private UpgradesAddon upgradesAddon; - private Map playerCache; - private Map ownDescription; - - public class UpgradeValues { - - public UpgradeValues(Integer islandLevel, Integer moneyCost, Integer upgradeValue) { - this.islandLevel = islandLevel; - this.moneyCost = moneyCost; - this.upgradeValue = upgradeValue; - } - - public int getIslandLevel() { - return islandLevel; - } - - public void setIslandLevel(int islandLevel) { - this.islandLevel = islandLevel; - } - - public int getMoneyCost() { - return moneyCost; - } - - public void setMoneyCost(int moneyCost) { - this.moneyCost = moneyCost; - } - - public int getUpgradeValue() { - return upgradeValue; - } - - public void setUpgradeValue(int upgradeValue) { - this.upgradeValue = upgradeValue; - } - - private int islandLevel; - private int moneyCost; - private int upgradeValue; - } - - @Override - public String toString() { - return "Upgrade [name=" + name + ", displayName=" + displayName + ", icon=" + icon + "]"; - } + /** + * Initialize the upgrade object you should call it in your init methode + * + * @param addon This should be your addon + * @param name This is the name for the upgrade that will be used in the + * DataBase + * @param displayName This is the name that is shown to the user + * @param icon This is the icon shown to the user + */ + public Upgrade(UpgradesAddon addon, String name, String displayName, Material icon) { + this.name = name; + this.displayName = displayName; + this.icon = icon; + this.addon = addon; + + this.playerCache = new TreeMap<>(); + this.ownDescription = new TreeMap<>(); + + this.upgradesAddon = addon; + this.addon.log("Added upgrade -> " + name); + } + + /** + * This function is called every times a user open the interface You should make + * it update the upgradeValues + * + * @param user This is the user that ask for the interface + * @param island This is the island concerned by the interface + */ + public abstract void updateUpgradeValue(User user, Island island); + + /** + * This function is called every times a user open the interface If it return + * false, the upgrade won't be showed to the user + * + * @param user This is the user that ask for the interface + * @param island This is the island concerned by the interface + * @return If true, then upgrade is shown else, it is hided + */ + public boolean isShowed(User user, Island island) { + return true; + } + + /** + * This function return true if the user can upgrade for this island. You can + * override it and call the super. + * + * The super test for islandLevel and for money + * + * @param user This is the user that try to upgrade + * @param island This is the island that is concerned + * @return Can upgrade + */ + public boolean canUpgrade(User user, Island island) { + UpgradeValues upgradeValues = this.getUpgradeValues(user); + boolean can = true; + + if (this.upgradesAddon.isLevelProvided() + && this.upgradesAddon.getUpgradesManager().getIslandLevel(island) < upgradeValues.getIslandLevel()) { + + can = false; + } + + if (this.upgradesAddon.isVaultProvided() + && !this.upgradesAddon.getVaultHook().has(user, upgradeValues.getMoneyCost())) { + + can = false; + } + + return can; + } + + /** + * This function is called when the user is upgrading for the island It is + * called after the canUpgrade function + * + * You should call the super to update the balance of the user as well as the + * level is the island + * + * @param user This is the user that do the upgrade + * @param island This is the island that is concerned + * @return If upgrade was successful + */ + public boolean doUpgrade(User user, Island island) { + UpgradeValues upgradeValues = this.getUpgradeValues(user); + + if (this.upgradesAddon.isVaultProvided()) { + EconomyResponse response = this.upgradesAddon.getVaultHook().withdraw(user, upgradeValues.getMoneyCost()); + if (!response.transactionSuccess()) { + this.addon.logWarning( + "User Money withdrawing failed user: " + user.getName() + " reason: " + response.errorMessage); + user.sendMessage("upgrades.error.costwithdraw"); + return false; + } + } + + UpgradesData data = this.upgradesAddon.getUpgradesLevels(island.getUniqueId()); + data.setUpgradeLevel(this.name, data.getUpgradeLevel(this.name) + 1); + + return true; + } + + /** + * @return The name that is used for the DataBase + */ + public String getName() { + return this.name; + } + + /** + * @return The name that is displayed to the user + */ + public String getDisplayName() { + return this.displayName; + } + + /** + * @param displayName To update the name to display to the user + */ + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + /** + * @return The icon that is displayed to the user + */ + public Material getIcon() { + return this.icon; + } + + public int getUpgradeLevel(Island island) { + return this.upgradesAddon.getUpgradesLevels(island.getUniqueId()).getUpgradeLevel(this.name); + } + + /** + * @return The actual description for the user + */ + public String getOwnDescription(User user) { + return this.ownDescription.get(user.getUniqueId()); + } + + /** + * @param user User to set the description + * @param description Description to set + */ + public void setOwnDescription(User user, String description) { + this.ownDescription.put(user.getUniqueId(), description); + } + + /** + * @return The actual upgradeValues + */ + public UpgradeValues getUpgradeValues(User user) { + return this.playerCache.get(user.getUniqueId()); + } + + /** + * @param upgrade Values to upgrades + */ + public void setUpgradeValues(User user, UpgradeValues upgrade) { + this.playerCache.put(user.getUniqueId(), upgrade); + } + + /** + * Function that get the upgrades addon You should use it to use the upgrades + * addon methods + * + * @return UpgradesAddon + */ + public UpgradesAddon getUpgradesAddon() { + return this.upgradesAddon; + } + + /** + * You shouldn't override this function + */ + @Override + public int hashCode() { + return Objects.hash(name); + } + + /** + * You shouldn't override this function + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (!(obj instanceof Upgrade)) { + return false; + } + Upgrade other = (Upgrade) obj; + return Objects.equals(name, other.name); + } + + private final String name; + private String displayName; + private Material icon; + private Addon addon; + private UpgradesAddon upgradesAddon; + private Map playerCache; + private Map ownDescription; + + public class UpgradeValues { + + public UpgradeValues(Integer islandLevel, Integer moneyCost, Integer upgradeValue) { + this.islandLevel = islandLevel; + this.moneyCost = moneyCost; + this.upgradeValue = upgradeValue; + } + + public int getIslandLevel() { + return islandLevel; + } + + public void setIslandLevel(int islandLevel) { + this.islandLevel = islandLevel; + } + + public int getMoneyCost() { + return moneyCost; + } + + public void setMoneyCost(int moneyCost) { + this.moneyCost = moneyCost; + } + + public int getUpgradeValue() { + return upgradeValue; + } + + public void setUpgradeValue(int upgradeValue) { + this.upgradeValue = upgradeValue; + } + + private int islandLevel; + private int moneyCost; + private int upgradeValue; + } + + @Override + public String toString() { + return "Upgrade [name=" + name + ", displayName=" + displayName + ", icon=" + icon + "]"; + } } diff --git a/src/main/java/world/bentobox/upgrades/config/Settings.java b/src/main/java/world/bentobox/upgrades/config/Settings.java index 8fddfc2..f05beb7 100644 --- a/src/main/java/world/bentobox/upgrades/config/Settings.java +++ b/src/main/java/world/bentobox/upgrades/config/Settings.java @@ -16,903 +16,1224 @@ import org.bukkit.entity.EntityType; import org.eclipse.jdt.annotation.NonNull; -import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.upgrades.UpgradesAddon; +/** + * Represents the settings and configurations for the UpgradesAddon. + * Handles configuration data parsing and storage, enabling upgrades and + * managing limits for various game aspects like blocks, entities, and commands. + */ public class Settings { - public Settings(UpgradesAddon addon) { - this.addon = addon; - this.addon.saveDefaultConfig(); - - this.hasRangeUpgrade = false; - - this.disabledGameModes = new HashSet<>(this.addon.getConfig().getStringList("disabled-gamemodes")); - - if (this.addon.getConfig().isSet("range-upgrade")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("range-upgrade"); - for (String key : Objects.requireNonNull(section).getKeys(false)) { - UpgradeTier newUpgrade = addUpgradeSection(section, key); - - if (this.maxRangeUpgrade < newUpgrade.getMaxLevel()) - this.maxRangeUpgrade = newUpgrade.getMaxLevel(); - - this.hasRangeUpgrade = true; - this.rangeUpgradeTierMap.put(key, newUpgrade); - } - } - - if (this.addon.getConfig().isSet("block-limits-upgrade")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("block-limits-upgrade"); - this.blockLimitsUpgradeTierMap = this.loadBlockLimits(section, null); - } - - if (this.addon.getConfig().isSet("entity-icon")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("entity-icon"); - for (String entity : Objects.requireNonNull(section).getKeys(false)) { - String material = section.getString(entity); - EntityType ent = this.getEntityType(entity); - Material mat = Material.getMaterial(material); - if (ent == null) - this.addon.logError("Config: EntityType " + entity + " is not valid in icon"); - else if (mat == null) - this.addon.logError("Config: Material " + material + " is not a valid material"); - else - this.entityIcon.put(ent, mat); - } - } - - if (this.addon.getConfig().isSet("entity-group-icon")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("entity-group-icon"); - for (String group : Objects.requireNonNull(section).getKeys(false)) { - String material = section.getString(group); - Material mat = Material.getMaterial(material); - if (mat == null) - this.addon.logError("Config: Material " + material + " is not a valid material"); - else - this.entityGroupIcon.put(group, mat); - } - } - - if (this.addon.getConfig().isSet("entity-limits-upgrade")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("entity-limits-upgrade"); - this.entityLimitsUpgradeTierMap = this.loadEntityLimits(section, null); - } - - if (this.addon.getConfig().isSet("entity-group-limits-upgrade")) { - ConfigurationSection section = this.addon.getConfig() - .getConfigurationSection("entity-group-limits-upgrade"); - this.entityGroupLimitsUpgradeTierMap = this.loadEntityGroupLimits(section, null); - } - - if (this.addon.getConfig().isSet("command-icon")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("command-icon"); - for (String commandId : Objects.requireNonNull(section).getKeys(false)) { - String material = section.getString(commandId); - Material mat = Material.getMaterial(material); - if (mat == null) - this.addon.logError("Config: Material " + material + " is not a valid material"); - else - this.commandIcon.put(commandId, mat); - } - } - - if (this.addon.getConfig().isSet("command-upgrade")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("command-upgrade"); - this.commandUpgradeTierMap = this.loadCommand(section, null); - } - - if (this.addon.getConfig().isSet("gamemodes")) { - ConfigurationSection section = this.addon.getConfig().getConfigurationSection("gamemodes"); - - for (String gameMode : Objects.requireNonNull(section).getKeys(false)) { - ConfigurationSection gameModeSection = section.getConfigurationSection(gameMode); - - if (gameModeSection.isSet("range-upgrade")) { - ConfigurationSection lowSection = gameModeSection.getConfigurationSection("range-upgrade"); - for (String key : Objects.requireNonNull(lowSection).getKeys(false)) { - UpgradeTier newUpgrade = addUpgradeSection(lowSection, key); - - if (this.customMaxRangeUpgrade.get(gameMode) == null - || this.customMaxRangeUpgrade.get(gameMode) < newUpgrade.getMaxLevel()) - this.customMaxRangeUpgrade.put(gameMode, newUpgrade.getMaxLevel()); - - this.hasRangeUpgrade = true; - - this.customRangeUpgradeTierMap.computeIfAbsent(gameMode, k -> new TreeMap<>()).put(key, - newUpgrade); - } - } - - if (gameModeSection.isSet("block-limits-upgrade")) { - ConfigurationSection lowSection = gameModeSection.getConfigurationSection("block-limits-upgrade"); - this.customBlockLimitsUpgradeTierMap.computeIfAbsent(gameMode, - k -> loadBlockLimits(lowSection, gameMode)); - } - - if (gameModeSection.isSet("entity-limits-upgrade")) { - ConfigurationSection lowSection = gameModeSection.getConfigurationSection("entity-limits-upgrade"); - this.customEntityLimitsUpgradeTierMap.computeIfAbsent(gameMode, - k -> loadEntityLimits(lowSection, gameMode)); - } - - if (gameModeSection.isSet("entity-group-limits-upgrade")) { - ConfigurationSection lowSection = gameModeSection - .getConfigurationSection("entity-group-limits-upgrade"); - this.customEntityGroupLimitsUpgradeTierMap.computeIfAbsent(gameMode, - k -> loadEntityGroupLimits(lowSection, gameMode)); - } - - if (gameModeSection.isSet("command-upgrade")) { - ConfigurationSection lowSection = gameModeSection.getConfigurationSection("command-upgrade"); - this.customCommandUpgradeTierMap.computeIfAbsent(gameMode, k -> loadCommand(lowSection, gameMode)); - } - } - } - - } - - private Map> loadBlockLimits(ConfigurationSection section, String gameMode) { - Map> mats = new EnumMap<>(Material.class); - for (String material : Objects.requireNonNull(section).getKeys(false)) { - Material mat = Material.getMaterial(material); - if (mat != null && mat.isBlock()) { - Map tier = new TreeMap<>(); - ConfigurationSection matSection = section.getConfigurationSection(material); - for (String key : Objects.requireNonNull(matSection).getKeys(false)) { - UpgradeTier newUpgrade = addUpgradeSection(matSection, key); - - if (gameMode == null) { - if (this.maxBlockLimitsUpgrade.get(mat) == null - || this.maxBlockLimitsUpgrade.get(mat) < newUpgrade.getMaxLevel()) - this.maxBlockLimitsUpgrade.put(mat, newUpgrade.getMaxLevel()); - } else { - if (this.customMaxBlockLimitsUpgrade.get(gameMode) == null) { - Map newMap = new EnumMap<>(Material.class); - newMap.put(mat, newUpgrade.getMaxLevel()); - this.customMaxBlockLimitsUpgrade.put(gameMode, newMap); - } else { - if (this.customMaxBlockLimitsUpgrade.get(gameMode).get(mat) == null - || this.customMaxBlockLimitsUpgrade.get(gameMode).get(mat) < newUpgrade - .getMaxLevel()) - this.customMaxBlockLimitsUpgrade.get(gameMode).put(mat, newUpgrade.getMaxLevel()); - } - } - - tier.put(key, newUpgrade); - } - mats.put(mat, tier); - } else { - this.addon.logError("Material " + material + " is not a valid material. Skipping..."); - } - } - return mats; - } - - private Map> loadEntityLimits(ConfigurationSection section, String gameMode) { - Map> ents = new EnumMap<>(EntityType.class); - for (String entity : Objects.requireNonNull(section).getKeys(false)) { - EntityType ent = this.getEntityType(entity); - if (ent != null && this.entityIcon.containsKey(ent)) { - Map tier = new TreeMap<>(); - ConfigurationSection entSection = section.getConfigurationSection(entity); - for (String key : Objects.requireNonNull(entSection).getKeys(false)) { - UpgradeTier newUpgrade = addUpgradeSection(entSection, key); - - if (gameMode == null) { - if (this.maxEntityLimitsUpgrade.get(ent) == null - || this.maxEntityLimitsUpgrade.get(ent) < newUpgrade.getMaxLevel()) - this.maxEntityLimitsUpgrade.put(ent, newUpgrade.getMaxLevel()); - } else { - if (this.customMaxEntityLimitsUpgrade.get(gameMode) == null) { - Map newMap = new EnumMap<>(EntityType.class); - newMap.put(ent, newUpgrade.getMaxLevel()); - this.customMaxEntityLimitsUpgrade.put(gameMode, newMap); - } else { - if (this.customMaxEntityLimitsUpgrade.get(gameMode).get(ent) == null - || this.customMaxEntityLimitsUpgrade.get(gameMode).get(ent) < newUpgrade - .getMaxLevel()) - this.customMaxEntityLimitsUpgrade.get(gameMode).put(ent, newUpgrade.getMaxLevel()); - } - } - - tier.put(key, newUpgrade); - } - tier.keySet().forEach(k -> BentoBox.getInstance().logDebug("Key - " + k)); - ents.put(ent, tier); - } else { - if (ent != null) - this.addon.logError("Entity " + entity + " is not a valid entity. Skipping..."); - else - this.addon.logError("Entity " + entity + " is missing a corresponding icon. Skipping..."); - } - } - return ents; - } - - private Map> loadEntityGroupLimits(ConfigurationSection section, String gameMode) { - Map> ents = new TreeMap<>(); - for (String entitygroup : Objects.requireNonNull(section).getKeys(false)) { - Map tier = new TreeMap<>(); - ConfigurationSection entSection = section.getConfigurationSection(entitygroup); - for (String key : Objects.requireNonNull(entSection).getKeys(false)) { - UpgradeTier newUpgrade = addUpgradeSection(entSection, key); - - if (gameMode == null) { - if (this.maxEntityGroupLimitsUpgrade.get(entitygroup) == null - || this.maxEntityGroupLimitsUpgrade.get(entitygroup) < newUpgrade.getMaxLevel()) - this.maxEntityGroupLimitsUpgrade.put(entitygroup, newUpgrade.getMaxLevel()); - } else { - if (this.customMaxEntityGroupLimitsUpgrade.get(gameMode) == null) { - Map newMap = new TreeMap<>(); - newMap.put(entitygroup, newUpgrade.getMaxLevel()); - this.customMaxEntityGroupLimitsUpgrade.put(gameMode, newMap); - } else { - if (this.customMaxEntityGroupLimitsUpgrade.get(gameMode).get(entitygroup) == null - || this.customMaxEntityGroupLimitsUpgrade.get(gameMode).get(entitygroup) < newUpgrade - .getMaxLevel()) - this.customMaxEntityGroupLimitsUpgrade.get(gameMode).put(entitygroup, - newUpgrade.getMaxLevel()); - } - } - - tier.put(key, newUpgrade); - } - ents.put(entitygroup, tier); - } - return ents; - } - - private Map> loadCommand(ConfigurationSection section, String gamemode) { - Map> commands = new TreeMap<>(); - - for (String commandId : Objects.requireNonNull(section).getKeys(false)) { - if (this.commandIcon.containsKey(commandId)) { - String name = commandId; - Map tier = new TreeMap<>(); - ConfigurationSection cmdSection = section.getConfigurationSection(commandId); - for (String key : Objects.requireNonNull(cmdSection).getKeys(false)) { - if (key.equals("name")) { - name = cmdSection.getString(key); - } else { - CommandUpgradeTier newUpgrade = addCommandUpgradeSection(cmdSection, key); - - if (gamemode == null) { - if (this.maxCommandUpgrade.get(commandId) == null - || this.maxCommandUpgrade.get(commandId) < newUpgrade.getMaxLevel()) { - this.maxCommandUpgrade.put(commandId, newUpgrade.getMaxLevel()); - } - } else { - if (this.customMaxCommandUpgrade.get(gamemode) == null) { - Map newMap = new TreeMap<>(); - newMap.put(commandId, newUpgrade.getMaxLevel()); - this.customMaxCommandUpgrade.put(gamemode, newMap); - } else { - if (this.customMaxCommandUpgrade.get(gamemode).get(commandId) == null - || this.customMaxCommandUpgrade.get(gamemode).get(commandId) < newUpgrade - .getMaxLevel()) - this.customMaxCommandUpgrade.get(gamemode).put(commandId, newUpgrade.getMaxLevel()); - } - } - - tier.put(key, newUpgrade); - } - } - if (!this.commandName.containsKey(commandId) || !name.equals(commandId)) - this.commandName.put(commandId, name); - commands.put(commandId, tier); - } else { - this.addon.logError("Command " + commandId + " is missing a corresponding icon. Skipping..."); - } - } - - return commands; - } - - @NonNull - private UpgradeTier addUpgradeSection(ConfigurationSection section, String key) { - ConfigurationSection tierSection = section.getConfigurationSection(key); - UpgradeTier upgradeTier = new UpgradeTier(key); - upgradeTier.setTierName(tierSection.getName()); - upgradeTier.setMaxLevel(tierSection.getInt("max-level")); - upgradeTier.setUpgrade(parse(tierSection.getString("upgrade"), upgradeTier.getExpressionVariable())); - - if (tierSection.isSet("island-min-level")) - upgradeTier.setIslandMinLevel( - parse(tierSection.getString("island-min-level"), upgradeTier.getExpressionVariable())); - else - upgradeTier.setIslandMinLevel(parse("0", upgradeTier.getExpressionVariable())); - - if (tierSection.isSet("vault-cost")) - upgradeTier.setVaultCost(parse(tierSection.getString("vault-cost"), upgradeTier.getExpressionVariable())); - else - upgradeTier.setVaultCost(parse("0", upgradeTier.getExpressionVariable())); - - if (tierSection.isSet("permission-level")) - upgradeTier.setPermissionLevel(tierSection.getInt("permission-level")); - else - upgradeTier.setPermissionLevel(0); - - return upgradeTier; - - } - - @NonNull - private CommandUpgradeTier addCommandUpgradeSection(ConfigurationSection section, String key) { - ConfigurationSection tierSection = section.getConfigurationSection(key); - CommandUpgradeTier upgradeTier = new CommandUpgradeTier(key); - upgradeTier.setTierName(tierSection.getName()); - upgradeTier.setMaxLevel(tierSection.getInt("max-level")); - upgradeTier.setUpgrade(parse("0", upgradeTier.getExpressionVariable())); - - if (tierSection.isSet("island-min-level")) - upgradeTier.setIslandMinLevel( - parse(tierSection.getString("island-min-level"), upgradeTier.getExpressionVariable())); - else - upgradeTier.setIslandMinLevel(parse("0", upgradeTier.getExpressionVariable())); - - if (tierSection.isSet("vault-cost")) - upgradeTier.setVaultCost(parse(tierSection.getString("vault-cost"), upgradeTier.getExpressionVariable())); - else - upgradeTier.setVaultCost(parse("0", upgradeTier.getExpressionVariable())); - - if (tierSection.isSet("permission-level")) - upgradeTier.setPermissionLevel(tierSection.getInt("permission-level")); - else - upgradeTier.setPermissionLevel(0); - - if (tierSection.isSet("console") && tierSection.isBoolean("console")) - upgradeTier.setConsole(tierSection.getBoolean("console")); - else - upgradeTier.setConsole(false); - - if (tierSection.isSet("command")) - upgradeTier.setCommandList(tierSection.getStringList("command")); - - return upgradeTier; - - } - - /** - * @return the disabledGameModes - */ - public Set getDisabledGameModes() { - return disabledGameModes; - } - - public boolean getHasRangeUpgrade() { - return this.hasRangeUpgrade; - } - - public int getMaxRangeUpgrade(String addon) { - return this.customMaxRangeUpgrade.getOrDefault(addon, this.maxRangeUpgrade); - } - - public Map getDefaultRangeUpgradeTierMap() { - return this.rangeUpgradeTierMap; - } - - /** - * @return the rangeUpgradeTierMap - */ - public Map getAddonRangeUpgradeTierMap(String addon) { - return this.customRangeUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); - } - - public int getMaxBlockLimitsUpgrade(Material mat, String addon) { - return this.customMaxBlockLimitsUpgrade.getOrDefault(addon, this.maxBlockLimitsUpgrade).getOrDefault(mat, 0); - } - - public Map> getDefaultBlockLimitsUpgradeTierMap() { - return this.blockLimitsUpgradeTierMap; - } - - /** - * @return the rangeUpgradeTierMap - */ - public Map> getAddonBlockLimitsUpgradeTierMap(String addon) { - return this.customBlockLimitsUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); - } - - public Set getMaterialsLimitsUpgrade() { - Set materials = new HashSet<>(); - - this.customBlockLimitsUpgradeTierMap.forEach((addon, addonUpgrade) -> { - materials.addAll(addonUpgrade.keySet()); - }); - materials.addAll(this.blockLimitsUpgradeTierMap.keySet()); - - return materials; - } - - public Material getEntityIcon(EntityType entity) { - return this.entityIcon.getOrDefault(entity, null); - } - - public Material getEntityGroupIcon(String group) { - return this.entityGroupIcon.getOrDefault(group, null); - } - - public int getMaxEntityLimitsUpgrade(EntityType entity, String addon) { - return this.customMaxEntityLimitsUpgrade.getOrDefault(addon, this.maxEntityLimitsUpgrade).getOrDefault(entity, - 0); - } - - public int getMaxEntityGroupLimitsUpgrade(String group, String addon) { - return this.customMaxEntityGroupLimitsUpgrade.getOrDefault(addon, this.maxEntityGroupLimitsUpgrade) - .getOrDefault(group, 0); - } + /** + * The UpgradesAddon instance associated with this settings object. + */ + private UpgradesAddon addon; + + /** + * Set of game modes where the upgrades are disabled. + */ + private Set disabledGameModes; + + /** + * Maximum range for range upgrades. + */ + private int maxRangeUpgrade = 0; + + /** + * Flag indicating if the range upgrade is enabled. + */ + private boolean hasRangeUpgrade; + + /** + * Custom maximum range upgrades per game mode. + */ + private Map customMaxRangeUpgrade = new TreeMap<>(); + + /** + * Default range upgrade tiers. + */ + private Map rangeUpgradeTierMap = new TreeMap<>(); + + /** + * Custom range upgrade tiers per game mode. + */ + private Map> customRangeUpgradeTierMap = new TreeMap<>(); + + /** + * Default block limits upgrades for each material. + */ + private Map maxBlockLimitsUpgrade = new EnumMap<>(Material.class); + + /** + * Custom block limits upgrades for each material and game mode. + */ + private Map> customMaxBlockLimitsUpgrade = new TreeMap<>(); + + /** + * Default block limits upgrade tiers for each material. + */ + private Map> blockLimitsUpgradeTierMap = new EnumMap<>(Material.class); + + /** + * Custom block limits upgrade tiers per game mode. + */ + private Map>> customBlockLimitsUpgradeTierMap = new TreeMap<>(); + + /** + * Entity type to material icon mapping. + */ + private Map entityIcon = new EnumMap<>(EntityType.class); + + /** + * Entity group to material icon mapping. + */ + private Map entityGroupIcon = new TreeMap<>(); + + /** + * Default entity limits upgrades for each entity type. + */ + private Map maxEntityLimitsUpgrade = new EnumMap<>(EntityType.class); + + /** + * Default entity group limits upgrades. + */ + private Map maxEntityGroupLimitsUpgrade = new TreeMap<>(); + + /** + * Custom entity limits upgrades per game mode. + */ + private Map> customMaxEntityLimitsUpgrade = new TreeMap<>(); + + /** + * Custom entity group limits upgrades per game mode. + */ + private Map> customMaxEntityGroupLimitsUpgrade = new TreeMap<>(); + + /** + * Default entity limits upgrade tiers for each entity type. + */ + private Map> entityLimitsUpgradeTierMap = new EnumMap<>(EntityType.class); + + /** + * Default entity group limits upgrade tiers. + */ + private Map> entityGroupLimitsUpgradeTierMap = new TreeMap<>(); + + /** + * Custom entity limits upgrade tiers per game mode. + */ + private Map>> customEntityLimitsUpgradeTierMap = new TreeMap<>(); + + /** + * Custom entity group limits upgrade tiers per game mode. + */ + private Map>> customEntityGroupLimitsUpgradeTierMap = new TreeMap<>(); + + /** + * Default command limits upgrades. + */ + private Map maxCommandUpgrade = new TreeMap<>(); + + /** + * Custom command limits upgrades per game mode. + */ + private Map> customMaxCommandUpgrade = new TreeMap<>(); + + /** + * Default command upgrade tiers. + */ + private Map> commandUpgradeTierMap = new TreeMap<>(); + + /** + * Custom command upgrade tiers per game mode. + */ + private Map>> customCommandUpgradeTierMap = new TreeMap<>(); + + /** + * Command to material icon mapping. + */ + private Map commandIcon = new TreeMap<>(); + + /** + * Command name mappings. + */ + private Map commandName = new TreeMap<>(); + + private EntityType getEntityType(String key) { + return Arrays.stream(EntityType.values()).filter(v -> v.name().equalsIgnoreCase(key)).findFirst().orElse(null); + } + + /** + * Constructs a new Settings object and initializes the configurations. + * + * @param addon The UpgradesAddon instance. + */ + public Settings(UpgradesAddon addon) { + this.addon = addon; + this.addon.saveDefaultConfig(); + + this.hasRangeUpgrade = false; + + this.disabledGameModes = new HashSet<>(this.addon.getConfig().getStringList("disabled-gamemodes")); + + if (this.addon.getConfig().isSet("range-upgrade")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("range-upgrade"); + for (String key : Objects.requireNonNull(section).getKeys(false)) { + UpgradeTier newUpgrade = addUpgradeSection(section, key); + + if (this.maxRangeUpgrade < newUpgrade.getMaxLevel()) + this.maxRangeUpgrade = newUpgrade.getMaxLevel(); + + this.hasRangeUpgrade = true; + this.rangeUpgradeTierMap.put(key, newUpgrade); + } + } + + if (this.addon.getConfig().isSet("block-limits-upgrade")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("block-limits-upgrade"); + this.blockLimitsUpgradeTierMap = this.loadBlockLimits(section, null); + } + + if (this.addon.getConfig().isSet("entity-icon")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("entity-icon"); + for (String entity : Objects.requireNonNull(section).getKeys(false)) { + String material = section.getString(entity); + EntityType ent = this.getEntityType(entity); + Material mat = Material.getMaterial(material); + if (ent == null) + this.addon.logError("Config: EntityType " + entity + " is not valid in icon"); + else if (mat == null) + this.addon.logError("Config: Material " + material + " is not a valid material"); + else + this.entityIcon.put(ent, mat); + } + } + + if (this.addon.getConfig().isSet("entity-group-icon")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("entity-group-icon"); + for (String group : Objects.requireNonNull(section).getKeys(false)) { + String material = section.getString(group); + Material mat = Material.getMaterial(material); + if (mat == null) + this.addon.logError("Config: Material " + material + " is not a valid material"); + else + this.entityGroupIcon.put(group, mat); + } + } + + if (this.addon.getConfig().isSet("entity-limits-upgrade")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("entity-limits-upgrade"); + this.entityLimitsUpgradeTierMap = this.loadEntityLimits(section, null); + } + + if (this.addon.getConfig().isSet("entity-group-limits-upgrade")) { + ConfigurationSection section = this.addon.getConfig() + .getConfigurationSection("entity-group-limits-upgrade"); + this.entityGroupLimitsUpgradeTierMap = this.loadEntityGroupLimits(section, null); + } + + if (this.addon.getConfig().isSet("command-icon")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("command-icon"); + for (String commandId : Objects.requireNonNull(section).getKeys(false)) { + String material = section.getString(commandId); + Material mat = Material.getMaterial(material); + if (mat == null) + this.addon.logError("Config: Material " + material + " is not a valid material"); + else + this.commandIcon.put(commandId, mat); + } + } + + if (this.addon.getConfig().isSet("command-upgrade")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("command-upgrade"); + this.commandUpgradeTierMap = this.loadCommand(section, null); + } + + if (this.addon.getConfig().isSet("gamemodes")) { + ConfigurationSection section = this.addon.getConfig().getConfigurationSection("gamemodes"); + + for (String gameMode : Objects.requireNonNull(section).getKeys(false)) { + ConfigurationSection gameModeSection = section.getConfigurationSection(gameMode); + + if (gameModeSection.isSet("range-upgrade")) { + ConfigurationSection lowSection = gameModeSection.getConfigurationSection("range-upgrade"); + for (String key : Objects.requireNonNull(lowSection).getKeys(false)) { + UpgradeTier newUpgrade = addUpgradeSection(lowSection, key); + + if (this.customMaxRangeUpgrade.get(gameMode) == null + || this.customMaxRangeUpgrade.get(gameMode) < newUpgrade.getMaxLevel()) + this.customMaxRangeUpgrade.put(gameMode, newUpgrade.getMaxLevel()); + + this.hasRangeUpgrade = true; + + this.customRangeUpgradeTierMap.computeIfAbsent(gameMode, k -> new TreeMap<>()).put(key, + newUpgrade); + } + } + + if (gameModeSection.isSet("block-limits-upgrade")) { + ConfigurationSection lowSection = gameModeSection.getConfigurationSection("block-limits-upgrade"); + this.customBlockLimitsUpgradeTierMap.computeIfAbsent(gameMode, + k -> loadBlockLimits(lowSection, gameMode)); + } + + if (gameModeSection.isSet("entity-limits-upgrade")) { + ConfigurationSection lowSection = gameModeSection.getConfigurationSection("entity-limits-upgrade"); + this.customEntityLimitsUpgradeTierMap.computeIfAbsent(gameMode, + k -> loadEntityLimits(lowSection, gameMode)); + } + + if (gameModeSection.isSet("entity-group-limits-upgrade")) { + ConfigurationSection lowSection = gameModeSection + .getConfigurationSection("entity-group-limits-upgrade"); + this.customEntityGroupLimitsUpgradeTierMap.computeIfAbsent(gameMode, + k -> loadEntityGroupLimits(lowSection, gameMode)); + } + + if (gameModeSection.isSet("command-upgrade")) { + ConfigurationSection lowSection = gameModeSection.getConfigurationSection("command-upgrade"); + this.customCommandUpgradeTierMap.computeIfAbsent(gameMode, k -> loadCommand(lowSection, gameMode)); + } + } + } + } + + private Map> loadBlockLimits(ConfigurationSection section, String gameMode) { + Map> mats = new EnumMap<>(Material.class); + for (String material : Objects.requireNonNull(section).getKeys(false)) { + Material mat = Material.getMaterial(material); + if (mat != null && mat.isBlock()) { + Map tier = new TreeMap<>(); + ConfigurationSection matSection = section.getConfigurationSection(material); + for (String key : Objects.requireNonNull(matSection).getKeys(false)) { + UpgradeTier newUpgrade = addUpgradeSection(matSection, key); + + if (gameMode == null) { + if (this.maxBlockLimitsUpgrade.get(mat) == null + || this.maxBlockLimitsUpgrade.get(mat) < newUpgrade.getMaxLevel()) + this.maxBlockLimitsUpgrade.put(mat, newUpgrade.getMaxLevel()); + } else { + if (this.customMaxBlockLimitsUpgrade.get(gameMode) == null) { + Map newMap = new EnumMap<>(Material.class); + newMap.put(mat, newUpgrade.getMaxLevel()); + this.customMaxBlockLimitsUpgrade.put(gameMode, newMap); + } else { + if (this.customMaxBlockLimitsUpgrade.get(gameMode).get(mat) == null + || this.customMaxBlockLimitsUpgrade.get(gameMode).get(mat) < newUpgrade + .getMaxLevel()) + this.customMaxBlockLimitsUpgrade.get(gameMode).put(mat, newUpgrade.getMaxLevel()); + } + } + + tier.put(key, newUpgrade); + } + mats.put(mat, tier); + } else { + this.addon.logError("Material " + material + " is not a valid material. Skipping..."); + } + } + return mats; + } + + private Map> loadEntityLimits(ConfigurationSection section, String gameMode) { + Map> ents = new EnumMap<>(EntityType.class); + for (String entity : Objects.requireNonNull(section).getKeys(false)) { + EntityType ent = this.getEntityType(entity); + if (ent != null && this.entityIcon.containsKey(ent)) { + Map tier = new TreeMap<>(); + ConfigurationSection entSection = section.getConfigurationSection(entity); + for (String key : Objects.requireNonNull(entSection).getKeys(false)) { + UpgradeTier newUpgrade = addUpgradeSection(entSection, key); + + if (gameMode == null) { + if (this.maxEntityLimitsUpgrade.get(ent) == null + || this.maxEntityLimitsUpgrade.get(ent) < newUpgrade.getMaxLevel()) + this.maxEntityLimitsUpgrade.put(ent, newUpgrade.getMaxLevel()); + } else { + if (this.customMaxEntityLimitsUpgrade.get(gameMode) == null) { + Map newMap = new EnumMap<>(EntityType.class); + newMap.put(ent, newUpgrade.getMaxLevel()); + this.customMaxEntityLimitsUpgrade.put(gameMode, newMap); + } else { + if (this.customMaxEntityLimitsUpgrade.get(gameMode).get(ent) == null + || this.customMaxEntityLimitsUpgrade.get(gameMode).get(ent) < newUpgrade + .getMaxLevel()) + this.customMaxEntityLimitsUpgrade.get(gameMode).put(ent, newUpgrade.getMaxLevel()); + } + } + + tier.put(key, newUpgrade); + } + ents.put(ent, tier); + } else { + if (ent != null) + this.addon.logError("Entity " + entity + " is not a valid entity. Skipping..."); + else + this.addon.logError("Entity " + entity + " is missing a corresponding icon. Skipping..."); + } + } + return ents; + } + + private Map> loadEntityGroupLimits(ConfigurationSection section, String gameMode) { + Map> ents = new TreeMap<>(); + for (String entitygroup : Objects.requireNonNull(section).getKeys(false)) { + Map tier = new TreeMap<>(); + ConfigurationSection entSection = section.getConfigurationSection(entitygroup); + for (String key : Objects.requireNonNull(entSection).getKeys(false)) { + UpgradeTier newUpgrade = addUpgradeSection(entSection, key); + + if (gameMode == null) { + if (this.maxEntityGroupLimitsUpgrade.get(entitygroup) == null + || this.maxEntityGroupLimitsUpgrade.get(entitygroup) < newUpgrade.getMaxLevel()) + this.maxEntityGroupLimitsUpgrade.put(entitygroup, newUpgrade.getMaxLevel()); + } else { + if (this.customMaxEntityGroupLimitsUpgrade.get(gameMode) == null) { + Map newMap = new TreeMap<>(); + newMap.put(entitygroup, newUpgrade.getMaxLevel()); + this.customMaxEntityGroupLimitsUpgrade.put(gameMode, newMap); + } else { + if (this.customMaxEntityGroupLimitsUpgrade.get(gameMode).get(entitygroup) == null + || this.customMaxEntityGroupLimitsUpgrade.get(gameMode).get(entitygroup) < newUpgrade + .getMaxLevel()) + this.customMaxEntityGroupLimitsUpgrade.get(gameMode).put(entitygroup, + newUpgrade.getMaxLevel()); + } + } + + tier.put(key, newUpgrade); + } + ents.put(entitygroup, tier); + } + return ents; + } + + private Map> loadCommand(ConfigurationSection section, String gamemode) { + Map> commands = new TreeMap<>(); + + for (String commandId : Objects.requireNonNull(section).getKeys(false)) { + if (this.commandIcon.containsKey(commandId)) { + String name = commandId; + Map tier = new TreeMap<>(); + ConfigurationSection cmdSection = section.getConfigurationSection(commandId); + for (String key : Objects.requireNonNull(cmdSection).getKeys(false)) { + if (key.equals("name")) { + name = cmdSection.getString(key); + } else { + CommandUpgradeTier newUpgrade = addCommandUpgradeSection(cmdSection, key); + + if (gamemode == null) { + if (this.maxCommandUpgrade.get(commandId) == null + || this.maxCommandUpgrade.get(commandId) < newUpgrade.getMaxLevel()) { + this.maxCommandUpgrade.put(commandId, newUpgrade.getMaxLevel()); + } + } else { + if (this.customMaxCommandUpgrade.get(gamemode) == null) { + Map newMap = new TreeMap<>(); + newMap.put(commandId, newUpgrade.getMaxLevel()); + this.customMaxCommandUpgrade.put(gamemode, newMap); + } else { + if (this.customMaxCommandUpgrade.get(gamemode).get(commandId) == null + || this.customMaxCommandUpgrade.get(gamemode).get(commandId) < newUpgrade + .getMaxLevel()) + this.customMaxCommandUpgrade.get(gamemode).put(commandId, newUpgrade.getMaxLevel()); + } + } + + tier.put(key, newUpgrade); + } + } + if (!this.commandName.containsKey(commandId) || !name.equals(commandId)) + this.commandName.put(commandId, name); + commands.put(commandId, tier); + } else { + this.addon.logError("Command " + commandId + " is missing a corresponding icon. Skipping..."); + } + } + + return commands; + } + + @NonNull + private UpgradeTier addUpgradeSection(ConfigurationSection section, String key) { + ConfigurationSection tierSection = section.getConfigurationSection(key); + UpgradeTier upgradeTier = new UpgradeTier(key); + upgradeTier.setTierName(tierSection.getName()); + upgradeTier.setMaxLevel(tierSection.getInt("max-level")); + upgradeTier.setUpgrade(parse(tierSection.getString("upgrade"), upgradeTier.getExpressionVariable())); + + if (tierSection.isSet("island-min-level")) + upgradeTier.setIslandMinLevel( + parse(tierSection.getString("island-min-level"), upgradeTier.getExpressionVariable())); + else + upgradeTier.setIslandMinLevel(parse("0", upgradeTier.getExpressionVariable())); + + if (tierSection.isSet("vault-cost")) + upgradeTier.setVaultCost(parse(tierSection.getString("vault-cost"), upgradeTier.getExpressionVariable())); + else + upgradeTier.setVaultCost(parse("0", upgradeTier.getExpressionVariable())); + + if (tierSection.isSet("permission-level")) + upgradeTier.setPermissionLevel(tierSection.getInt("permission-level")); + else + upgradeTier.setPermissionLevel(0); + + return upgradeTier; + + } + + @NonNull + private CommandUpgradeTier addCommandUpgradeSection(ConfigurationSection section, String key) { + ConfigurationSection tierSection = section.getConfigurationSection(key); + CommandUpgradeTier upgradeTier = new CommandUpgradeTier(key); + upgradeTier.setTierName(tierSection.getName()); + upgradeTier.setMaxLevel(tierSection.getInt("max-level")); + upgradeTier.setUpgrade(parse("0", upgradeTier.getExpressionVariable())); + + if (tierSection.isSet("island-min-level")) + upgradeTier.setIslandMinLevel( + parse(tierSection.getString("island-min-level"), upgradeTier.getExpressionVariable())); + else + upgradeTier.setIslandMinLevel(parse("0", upgradeTier.getExpressionVariable())); + + if (tierSection.isSet("vault-cost")) + upgradeTier.setVaultCost(parse(tierSection.getString("vault-cost"), upgradeTier.getExpressionVariable())); + else + upgradeTier.setVaultCost(parse("0", upgradeTier.getExpressionVariable())); + + if (tierSection.isSet("permission-level")) + upgradeTier.setPermissionLevel(tierSection.getInt("permission-level")); + else + upgradeTier.setPermissionLevel(0); + + if (tierSection.isSet("console") && tierSection.isBoolean("console")) + upgradeTier.setConsole(tierSection.getBoolean("console")); + else + upgradeTier.setConsole(false); + + if (tierSection.isSet("command")) + upgradeTier.setCommandList(tierSection.getStringList("command")); + + return upgradeTier; + + } + + /** + * Retrieves the disabled game modes. + * + * @return A set of disabled game modes. + */ + public Set getDisabledGameModes() { + return disabledGameModes; + } + + /** + * Checks if the range upgrade is enabled. + * + * @return True if range upgrade is enabled, otherwise false. + */ + public boolean getHasRangeUpgrade() { + return hasRangeUpgrade; + } + + /** + * Gets the maximum range upgrade for a specific addon. + * + * @param addon The name of the addon. + * @return The maximum range upgrade value. + */ + public int getMaxRangeUpgrade(String addon) { + return customMaxRangeUpgrade.getOrDefault(addon, maxRangeUpgrade); + } + + /** + * Retrieves the default range upgrade tier map. + * + * @return A map of range upgrade tiers by their identifiers. + */ + public Map getDefaultRangeUpgradeTierMap() { + return rangeUpgradeTierMap; + } + + /** + * Retrieves the range upgrade tier map for a specific addon. + * + * @param addon The name of the addon. + * @return A map of range upgrade tiers specific to the addon. + */ + public Map getAddonRangeUpgradeTierMap(String addon) { + return customRangeUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); + } + + /** + * Gets the maximum block limits upgrade for a material and addon. + * + * @param mat The material type. + * @param addon The name of the addon. + * @return The maximum block limit for the material. + */ + public int getMaxBlockLimitsUpgrade(Material mat, String addon) { + return customMaxBlockLimitsUpgrade.getOrDefault(addon, maxBlockLimitsUpgrade).getOrDefault(mat, 0); + } + + /** + * Retrieves the default block limits upgrade tier map. + * + * @return A map of block limits upgrade tiers by material. + */ + public Map> getDefaultBlockLimitsUpgradeTierMap() { + return blockLimitsUpgradeTierMap; + } + + /** + * Retrieves the block limits upgrade tier map for a specific addon. + * + * @param addon The name of the addon. + * @return A map of block limits upgrade tiers specific to the addon. + */ + public Map> getAddonBlockLimitsUpgradeTierMap(String addon) { + return customBlockLimitsUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); + } + + /** + * Retrieves all materials with block limits upgrades. + * + * @return A set of materials with block limits upgrades. + */ + public Set getMaterialsLimitsUpgrade() { + Set materials = new HashSet<>(); + + customBlockLimitsUpgradeTierMap.forEach((addon, addonUpgrade) -> { + materials.addAll(addonUpgrade.keySet()); + }); + materials.addAll(blockLimitsUpgradeTierMap.keySet()); + + return materials; + } + + /** + * Retrieves the material icon for a specific entity type. + * + * @param entity The entity type. + * @return The material icon associated with the entity type, or null if not defined. + */ + public Material getEntityIcon(EntityType entity) { + return entityIcon.getOrDefault(entity, null); + } + + /** + * Retrieves the material icon for a specific entity group. + * + * @param group The entity group. + * @return The material icon associated with the entity group, or null if not defined. + */ + public Material getEntityGroupIcon(String group) { + return entityGroupIcon.getOrDefault(group, null); + } + + /** + * Gets the maximum entity limits upgrade for a specific entity type and addon. + * + * @param entity The entity type. + * @param addon The name of the addon. + * @return The maximum entity limit for the entity type. + */ + public int getMaxEntityLimitsUpgrade(EntityType entity, String addon) { + return customMaxEntityLimitsUpgrade.getOrDefault(addon, maxEntityLimitsUpgrade).getOrDefault(entity, 0); + } + + /** + * Gets the maximum entity group limits upgrade for a specific group and addon. + * + * @param group The entity group. + * @param addon The name of the addon. + * @return The maximum entity group limit for the group. + */ + public int getMaxEntityGroupLimitsUpgrade(String group, String addon) { + return customMaxEntityGroupLimitsUpgrade.getOrDefault(addon, maxEntityGroupLimitsUpgrade).getOrDefault(group, + 0); + } + + /** + * Retrieves the default entity limits upgrade tier map. + * + * @return A map of entity limits upgrade tiers by entity type. + */ + public Map> getDefaultEntityLimitsUpgradeTierMap() { + return entityLimitsUpgradeTierMap; + } + + /** + * Retrieves the default entity group limits upgrade tier map. + * + * @return A map of entity group limits upgrade tiers. + */ + public Map> getDefaultEntityGroupLimitsUpgradeTierMap() { + return entityGroupLimitsUpgradeTierMap; + } + + /** + * Retrieves the entity limits upgrade tier map for a specific addon. + * + * @param addon The name of the addon. + * @return A map of entity limits upgrade tiers specific to the addon. + */ + public Map> getAddonEntityLimitsUpgradeTierMap(String addon) { + return customEntityLimitsUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); + } + + /** + * Retrieves the entity group limits upgrade tier map for a specific addon. + * + * @param addon The name of the addon. + * @return A map of entity group limits upgrade tiers specific to the addon. + */ + public Map> getAddonEntityGroupLimitsUpgradeTierMap(String addon) { + return customEntityGroupLimitsUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); + } + + /** + * Retrieves all entity types with limits upgrades. + * + * @return A set of entity types with limits upgrades. + */ + public Set getEntityLimitsUpgrade() { + Set entity = new HashSet<>(); + + customEntityLimitsUpgradeTierMap.forEach((addon, addonUpgrade) -> { + entity.addAll(addonUpgrade.keySet()); + }); + entity.addAll(entityLimitsUpgradeTierMap.keySet()); + + return entity; + } + + /** + * Retrieves all entity groups with limits upgrades. + * + * @return A set of entity groups with limits upgrades. + */ + public Set getEntityGroupLimitsUpgrade() { + Set groups = new HashSet<>(); + + customEntityGroupLimitsUpgradeTierMap.forEach((addon, addonUpgrade) -> { + groups.addAll(addonUpgrade.keySet()); + }); + groups.addAll(entityGroupLimitsUpgradeTierMap.keySet()); + + return groups; + } + + /** + * Retrieves the maximum command upgrade limit for a specific command and addon. + * + * @param commandUpgrade The command identifier. + * @param addon The name of the addon. + * @return The maximum upgrade limit for the command. + */ + public int getMaxCommandUpgrade(String commandUpgrade, String addon) { + if (customMaxCommandUpgrade.containsKey(addon)) { + if (customMaxCommandUpgrade.get(addon).containsKey(commandUpgrade)) { + return customMaxCommandUpgrade.get(addon).get(commandUpgrade); + } + } + return maxCommandUpgrade.getOrDefault(commandUpgrade, 0); + } + + /** + * Retrieves the default command upgrade tier map. + * + * @return A map of command upgrade tiers by command identifier. + */ + public Map> getDefaultCommandUpgradeTierMap() { + return commandUpgradeTierMap; + } + + /** + * Retrieves the command upgrade tier map for a specific addon. + * + * @param addon The name of the addon. + * @return A map of command upgrade tiers specific to the addon. + */ + public Map> getAddonCommandUpgradeTierMap(String addon) { + return customCommandUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); + } + + /** + * Retrieves all commands with upgrades. + * + * @return A set of command identifiers with upgrades. + */ + public Set getCommandUpgrade() { + Set command = new HashSet<>(); + + customCommandUpgradeTierMap.forEach((addon, addonUpgrade) -> { + command.addAll(addonUpgrade.keySet()); + }); + command.addAll(commandUpgradeTierMap.keySet()); + + return command; + } + + /** + * Retrieves the material icon for a specific command. + * + * @param command The command identifier. + * @return The material icon associated with the command, or null if not defined. + */ + public Material getCommandIcon(String command) { + return commandIcon.getOrDefault(command, null); + } + + /** + * Retrieves the display name for a specific command. + * + * @param command The command identifier. + * @return The display name of the command. + */ + public String getCommandName(String command) { + return commandName.get(command); + } + + /** + * Represents an upgrade tier for a specific feature. + */ + public class UpgradeTier { + /** + * The unique identifier for the upgrade tier. + */ + private final String id; + + /** + * The maximum level of the upgrade tier. + */ + private int maxLevel = -1; + + /** + * The name of the upgrade tier. + */ + private String tierName; + + /** + * The permission level required for the upgrade. + */ + private Integer permissionLevel = 0; + + /** + * The expression defining the upgrade behavior. + */ + private Expression upgrade; + + /** + * Minimum island level required for the upgrade. + */ + private Expression islandMinLevel; + + /** + * Vault cost associated with the upgrade. + */ + private Expression vaultCost; + + /** + * Variables used in expressions for calculations. + */ + private Map expressionVariables; + + /** + * Creates a new UpgradeTier instance. + * + * @param id The unique identifier for the upgrade tier. + */ + public UpgradeTier(String id) { + this.id = id; + this.expressionVariables = new TreeMap<>(); + this.expressionVariables.put("[level]", 0.0); + this.expressionVariables.put("[islandLevel]", 0.0); + this.expressionVariables.put("[numberPlayer]", 0.0); + } + + /** + * Retrieves the ID of the upgrade tier. + * + * @return The ID of the tier. + */ + public String getId() { + return id; + } + + /** + * Retrieves the name of the upgrade tier. + * + * @return The name of the upgrade tier. + */ + public String getTierName() { + return tierName; + } + + /** + * Sets the name of the upgrade tier. + * + * @param tierName The name to set for + */ + public void setTierName(String tierName) { + this.tierName = tierName; + } + + /** + * Retrieves the maximum level of the upgrade tier. + * + * @return The maximum level. + */ + public int getMaxLevel() { + return maxLevel; + } + + /** + * Sets the maximum level of the upgrade tier. + * + * @param maxLevel The maximum level to set. + */ + public void setMaxLevel(int maxLevel) { + this.maxLevel = maxLevel; + } + + /** + * Retrieves the permission level required for the upgrade tier. + * + * @return The permission level. + */ + public Integer getPermissionLevel() { + return permissionLevel; + } + + /** + * Sets the permission level required for the upgrade tier. + * + * @param permissionLevel The permission level to set. + */ + public void setPermissionLevel(Integer permissionLevel) { + this.permissionLevel = permissionLevel; + } + + /** + * Retrieves the upgrade expression. + * + * @return The upgrade expression. + */ + public Expression getUpgrade() { + return upgrade; + } + + /** + * Sets the upgrade expression. + * + * @param upgrade The upgrade expression to set. + */ + public void setUpgrade(Expression upgrade) { + this.upgrade = upgrade; + } + + /** + * Retrieves the minimum island level required for the upgrade. + * + * @return The island minimum level expression. + */ + public Expression getIslandMinLevel() { + return islandMinLevel; + } + + /** + * Sets the minimum island level required for the upgrade. + * + * @param islandMinLevel The island minimum level expression to set. + */ + public void setIslandMinLevel(Expression islandMinLevel) { + this.islandMinLevel = islandMinLevel; + } + + /** + * Retrieves the vault cost associated with the upgrade. + * + * @return The vault cost expression. + */ + public Expression getVaultCost() { + return vaultCost; + } + + /** + * Sets the vault cost associated with the upgrade. + * + * @param vaultCost The vault cost expression to set. + */ + public void setVaultCost(Expression vaultCost) { + this.vaultCost = vaultCost; + } + + /** + * Updates a variable used in the upgrade expression calculations. + * + * @param key The variable name. + * @param value The value to set for the variable. + */ + public void updateExpressionVariable(String key, double value) { + this.expressionVariables.put(key, value); + } + + /** + * Retrieves all variables used in the upgrade expression calculations. + * + * @return A map of variable names to their values. + */ + public Map getExpressionVariable() { + return expressionVariables; + } + + /** + * Calculates the upgrade value based on the provided parameters. + * + * @param level The current level. + * @param islandLevel The island level. + * @param numberPeople The number of players. + * @return The calculated upgrade value. + */ + public double calculateUpgrade(double level, double islandLevel, double numberPeople) { + this.updateExpressionVariable("[level]", level); + this.updateExpressionVariable("[islandLevel]", islandLevel); + this.updateExpressionVariable("[numberPlayer]", numberPeople); + return this.getUpgrade().eval(); + } + + /** + * Calculates the minimum island level required based on the provided parameters. + * + * @param level The current level. + * @param islandLevel The island level. + * @param numberPeople The number of players. + * @return The calculated minimum island level. + */ + public double calculateIslandMinLevel(double level, double islandLevel, double numberPeople) { + this.updateExpressionVariable("[level]", level); + this.updateExpressionVariable("[islandLevel]", islandLevel); + this.updateExpressionVariable("[numberPlayer]", numberPeople); + return this.getIslandMinLevel().eval(); + } + + /** + * Calculates the vault cost based on the provided parameters. + * + * @param level The current level. + * @param islandLevel The island level. + * @param numberPeople The number of players. + * @return The calculated vault cost. + */ + public double calculateVaultCost(double level, double islandLevel, double numberPeople) { + this.updateExpressionVariable("[level]", level); + this.updateExpressionVariable("[islandLevel]", islandLevel); + this.updateExpressionVariable("[numberPlayer]", numberPeople); + return this.getVaultCost().eval(); + } + } + + /** + * Represents a command upgrade tier with additional properties. + */ + public class CommandUpgradeTier extends UpgradeTier { + /** + * List of commands associated with this upgrade tier. + */ + private List commandList; + + /** + * Indicates whether the commands should run on the console. + */ + private Boolean console; + + /** + * Creates a new CommandUpgradeTier instance. + * + * @param id The unique identifier for the upgrade tier. + */ + public CommandUpgradeTier(String id) { + super(id); + this.commandList = new ArrayList<>(); + } + + /** + * Sets whether the commands should run on the console. + * + * @param console True to run commands on the console, false otherwise. + */ + public void setConsole(Boolean console) { + this.console = console; + } + + /** + * Checks whether the commands should run on the console. + * + * @return True if commands run on the console, false otherwise. + */ + public Boolean getConsole() { + return console; + } + + /** + * Sets the list of commands for this upgrade tier. + * + * @param commandsList The list of commands to set. + */ + public void setCommandList(List commandsList) { + this.commandList = commandsList; + } + + /** + * Retrieves the formatted list of commands for execution. + * + * @param playerName The name of the player executing the commands. + * @param island The island associated with the commands. + * @param level The current level of the upgrade. + * @return A list of formatted commands ready for execution. + */ + public List getCommandList(String playerName, Island island, int level) { + List formattedList = new ArrayList<>(this.commandList.size()); + String owner = island.getPlugin().getPlayers().getName(island.getOwner()); + + this.commandList.forEach(cmd -> { + String formattedCmd = cmd.replace("[player]", playerName).replace("[level]", Integer.toString(level)) + .replace("[owner]", owner); + formattedList.add(formattedCmd); + }); + return formattedList; + } + } + + + + // ------------------------------------------------------------------------- + // Section: Arithmetic expressions Parser + // Thanks to Boann on StackOverflow + // Link: + // https://stackoverflow.com/questions/3422673/how-to-evaluate-a-math-expression-given-in-string-form + // ------------------------------------------------------------------------- + + @FunctionalInterface + interface Expression { + double eval(); + } + + private static final List funct = List.of("sqrt", "sin", "cos", "tan"); + + public static Expression parse(final String str, Map variables) { + return new Object() { + int pos = -1, ch; + + void nextChar() { + ch = (++pos < str.length()) ? str.charAt(pos) : -1; + } + + boolean eat(int charToEat) { + while (ch == ' ') + nextChar(); + if (ch == charToEat) { + nextChar(); + return true; + } + return false; + } + + Expression parse() { + nextChar(); + Expression x = parseExpression(); + if (pos < str.length()) + throw new RuntimeException("Unexpected: " + (char) ch); + return x; + } + + // Grammar: + // expression = term | expression `+` term | expression `-` term + // term = factor | term `*` factor | term `/` factor + // factor = `+` factor | `-` factor | `(` expression `)` + // | number | functionName factor | factor `^` factor + + Expression parseExpression() { + Expression x = parseTerm(); + for (;;) { + if (eat('+')) { + Expression a = x, b = parseTerm(); + x = (() -> a.eval() + b.eval()); + } else if (eat('-')) { + Expression a = x, b = parseTerm(); + x = (() -> a.eval() - b.eval()); + } else + return x; + } + } + + Expression parseTerm() { + Expression x = parseFactor(); + for (;;) { + if (eat('*')) { + Expression a = x, b = parseFactor(); + x = (() -> a.eval() * b.eval()); + } else if (eat('/')) { + Expression a = x, b = parseFactor(); + x = (() -> a.eval() / b.eval()); + } else + return x; + } + } + + Expression parseFactor() { + if (eat('+')) + return parseFactor(); // unary plus + if (eat('-')) { + Expression x = (() -> -parseFactor().eval()); + return x; // unary minus + } + + Expression 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(); + final Integer innerPos = Integer.valueOf(this.pos); + x = (() -> Double.parseDouble(str.substring(startPos, innerPos))); + } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '[' || ch == ']') { // functions + while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '[' || ch == ']') + nextChar(); + String func = str.substring(startPos, this.pos); + if (funct.contains(func)) { + Expression a = parseFactor(); + if (func.equals("sqrt")) + x = (() -> Math.sqrt(a.eval())); + else if (func.equals("sin")) + x = (() -> Math.sin(Math.toRadians(a.eval()))); + else if (func.equals("cos")) + x = (() -> Math.cos(Math.toRadians(a.eval()))); + else if (func.equals("tan")) + x = (() -> Math.tan(Math.toRadians(a.eval()))); + else + throw new RuntimeException("Unknown function: " + func); + } else { + x = (() -> variables.get(func)); + } + } else { + throw new RuntimeException("Unexpected: " + (char) ch); + } + + if (eat('^')) { + Expression a = x, b = parseFactor(); + x = (() -> Math.pow(a.eval(), b.eval())); // exponentiation + } + + return x; + } + }.parse(); + } - public Map> getDefaultEntityLimitsUpgradeTierMap() { - return this.entityLimitsUpgradeTierMap; - } - - public Map> getDefaultEntityGroupLimitsUpgradeTierMap() { - return this.entityGroupLimitsUpgradeTierMap; - } - - /** - * @return the rangeUpgradeTierMap - */ - public Map> getAddonEntityLimitsUpgradeTierMap(String addon) { - return this.customEntityLimitsUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); - } - - public Map> getAddonEntityGroupLimitsUpgradeTierMap(String addon) { - return this.customEntityGroupLimitsUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); - } - - public Set getEntityLimitsUpgrade() { - Set entity = new HashSet<>(); - - this.customEntityLimitsUpgradeTierMap.forEach((addon, addonUpgrade) -> { - entity.addAll(addonUpgrade.keySet()); - }); - entity.addAll(this.entityLimitsUpgradeTierMap.keySet()); - - return entity; - } - - public Set getEntityGroupLimitsUpgrade() { - Set groups = new HashSet<>(); - - this.customEntityGroupLimitsUpgradeTierMap.forEach((addon, addonUpgrade) -> { - groups.addAll(addonUpgrade.keySet()); - }); - groups.addAll(this.entityGroupLimitsUpgradeTierMap.keySet()); - - return groups; - } - - private EntityType getEntityType(String key) { - return Arrays.stream(EntityType.values()).filter(v -> v.name().equalsIgnoreCase(key)).findFirst().orElse(null); - } - - public int getMaxCommandUpgrade(String commandUpgrade, String addon) { - if (this.customMaxCommandUpgrade.containsKey(addon)) { - if (this.customMaxCommandUpgrade.get(addon).containsKey(commandUpgrade)) { - return this.customMaxCommandUpgrade.get(addon).get(commandUpgrade); - } - } - return this.maxCommandUpgrade.getOrDefault(commandUpgrade, 0); - } - - public Map> getDefaultCommandUpgradeTierMap() { - return this.commandUpgradeTierMap; - } - - /** - * @return the rangeUpgradeTierMap - */ - public Map> getAddonCommandUpgradeTierMap(String addon) { - return this.customCommandUpgradeTierMap.getOrDefault(addon, Collections.emptyMap()); - } - - public Set getCommandUpgrade() { - Set command = new HashSet<>(); - - this.customCommandUpgradeTierMap.forEach((addon, addonUpgrade) -> { - command.addAll(addonUpgrade.keySet()); - }); - command.addAll(this.commandUpgradeTierMap.keySet()); - - return command; - } - - public Material getCommandIcon(String command) { - return this.commandIcon.getOrDefault(command, null); - } - - public String getCommandName(String command) { - return this.commandName.get(command); - } - - private UpgradesAddon addon; - - private Set disabledGameModes; - - private int maxRangeUpgrade = 0; - - private boolean hasRangeUpgrade; - - private Map customMaxRangeUpgrade = new TreeMap<>(); - - private Map rangeUpgradeTierMap = new TreeMap<>(); - - private Map> customRangeUpgradeTierMap = new TreeMap<>(); - - private Map maxBlockLimitsUpgrade = new EnumMap<>(Material.class); - - private Map> customMaxBlockLimitsUpgrade = new TreeMap<>(); - - private Map> blockLimitsUpgradeTierMap = new EnumMap<>(Material.class); - - private Map>> customBlockLimitsUpgradeTierMap = new TreeMap<>(); - - private Map entityIcon = new EnumMap<>(EntityType.class); - - private Map entityGroupIcon = new TreeMap<>(); - - private Map maxEntityLimitsUpgrade = new EnumMap<>(EntityType.class); - - private Map maxEntityGroupLimitsUpgrade = new TreeMap<>(); - - private Map> customMaxEntityLimitsUpgrade = new TreeMap<>(); - - private Map> customMaxEntityGroupLimitsUpgrade = new TreeMap<>(); - - private Map> entityLimitsUpgradeTierMap = new EnumMap<>(EntityType.class); - - private Map> entityGroupLimitsUpgradeTierMap = new TreeMap<>(); - - private Map>> customEntityLimitsUpgradeTierMap = new TreeMap<>(); - - private Map>> customEntityGroupLimitsUpgradeTierMap = new TreeMap<>(); - - private Map maxCommandUpgrade = new TreeMap<>(); - - private Map> customMaxCommandUpgrade = new TreeMap<>(); - - private Map> commandUpgradeTierMap = new TreeMap<>(); - - private Map>> customCommandUpgradeTierMap = new TreeMap<>(); - - private Map commandIcon = new TreeMap<>(); - - private Map commandName = new TreeMap<>(); - - // ------------------------------------------------------------------ - // Section: Private object - // ------------------------------------------------------------------ - - public class UpgradeTier { - /** - * Constructor UpgradeTier create a new UpgradeTier instance and set - * expressionVariables to default value - * - * @param id - */ - public UpgradeTier(String id) { - this.id = id; - this.expressionVariables = new TreeMap<>(); - this.expressionVariables.put("[level]", 0.0); - this.expressionVariables.put("[islandLevel]", 0.0); - this.expressionVariables.put("[numberPlayer]", 0.0); - - } - - // -------------------------------------------------------------- - // Section: Methods - // -------------------------------------------------------------- - - /** - * @return the id - */ - public String getId() { - return id; - } - - public String getTierName() { - return this.tierName; - } - - public void setTierName(String tierName) { - this.tierName = tierName; - } - - /** - * @return the maxLevel - */ - public int getMaxLevel() { - return maxLevel; - } - - /** - * @param maxLevel the maxLevel to set - */ - public void setMaxLevel(int maxLevel) { - this.maxLevel = maxLevel; - } - - /** - * @return the level of permission - */ - public Integer getPermissionLevel() { - return this.permissionLevel; - } - - /** - * @param level of permission to set - */ - public void setPermissionLevel(Integer level) { - this.permissionLevel = level; - } - - /** - * @return the upgradeRange - */ - public Expression getUpgrade() { - return upgrade; - } - - /** - * @param upgrade the upgradeRange to set - */ - public void setUpgrade(Expression upgrade) { - this.upgrade = upgrade; - } - - /** - * @return the islandMinLevel - */ - public Expression getIslandMinLevel() { - return islandMinLevel; - } - - /** - * @param islandMinLevel the islandMinLevel to set - */ - public void setIslandMinLevel(Expression islandMinLevel) { - this.islandMinLevel = islandMinLevel; - } - - /** - * @return the vaultCost - */ - public Expression getVaultCost() { - return vaultCost; - } - - /** - * @param vaultCost the vaultCost to set - */ - public void setVaultCost(Expression vaultCost) { - this.vaultCost = vaultCost; - } - - /** - * Value to set for the math parser - * - * @param key - * @param value - */ - public void updateExpressionVariable(String key, double value) { - this.expressionVariables.put(key, value); - } - - public Map getExpressionVariable() { - return expressionVariables; - } - - public double calculateUpgrade(double level, double islandLevel, double numberPeople) { - this.updateExpressionVariable("[level]", level); - this.updateExpressionVariable("[islandLevel]", islandLevel); - this.updateExpressionVariable("[numberPlayer]", numberPeople); - return this.getUpgrade().eval(); - } - - public double calculateIslandMinLevel(double level, double islandLevel, double numberPeople) { - this.updateExpressionVariable("[level]", level); - this.updateExpressionVariable("[islandLevel]", islandLevel); - this.updateExpressionVariable("[numberPlayer]", numberPeople); - return this.getIslandMinLevel().eval(); - } - - public double calculateVaultCost(double level, double islandLevel, double numberPeople) { - this.updateExpressionVariable("[level]", level); - this.updateExpressionVariable("[islandLevel]", islandLevel); - this.updateExpressionVariable("[numberPlayer]", numberPeople); - return this.getVaultCost().eval(); - } - - // ---------------------------------------------------------------------- - // Section: Variables - // ---------------------------------------------------------------------- - - private final String id; - - private int maxLevel = -1; - - private String tierName; - - private Integer permissionLevel = 0; - - private Expression upgrade; - - private Expression islandMinLevel; - - private Expression vaultCost; - - private Map expressionVariables; - } - - public class CommandUpgradeTier extends UpgradeTier { - - public CommandUpgradeTier(String id) { - super(id); - this.commandList = new ArrayList(); - } - - public void setConsole(Boolean console) { - this.console = console; - } - - public Boolean getConsole() { - return this.console; - } - - public void setCommandList(List commandsList) { - this.commandList = commandsList; - } - - public List getCommandList(String playerName, Island island, int level) { - List formatedList = new ArrayList(this.commandList.size()); - String owner = island.getPlugin().getPlayers().getName(island.getOwner()); - - this.commandList.forEach(cmd -> { - String fcmd = cmd.replace("[player]", playerName).replace("[level]", Integer.toString(level)) - .replace("[owner]", owner); - formatedList.add(fcmd); - }); - return formatedList; - } - - private List commandList; - - private Boolean console; - - } - - // ------------------------------------------------------------------------- - // Section: Arithmetic expressions Parser - // Thanks to Boann on StackOverflow - // Link: - // https://stackoverflow.com/questions/3422673/how-to-evaluate-a-math-expression-given-in-string-form - // ------------------------------------------------------------------------- - - @FunctionalInterface - interface Expression { - double eval(); - } - - public static Expression parse(final String str, Map variables) { - return new Object() { - int pos = -1, ch; - - void nextChar() { - ch = (++pos < str.length()) ? str.charAt(pos) : -1; - } - - boolean eat(int charToEat) { - while (ch == ' ') - nextChar(); - if (ch == charToEat) { - nextChar(); - return true; - } - return false; - } - - Expression parse() { - nextChar(); - Expression x = parseExpression(); - if (pos < str.length()) - throw new RuntimeException("Unexpected: " + (char) ch); - return x; - } - - // Grammar: - // expression = term | expression `+` term | expression `-` term - // term = factor | term `*` factor | term `/` factor - // factor = `+` factor | `-` factor | `(` expression `)` - // | number | functionName factor | factor `^` factor - - Expression parseExpression() { - Expression x = parseTerm(); - for (;;) { - if (eat('+')) { - Expression a = x, b = parseTerm(); - x = (() -> a.eval() + b.eval()); - } else if (eat('-')) { - Expression a = x, b = parseTerm(); - x = (() -> a.eval() - b.eval()); - } else - return x; - } - } - - Expression parseTerm() { - Expression x = parseFactor(); - for (;;) { - if (eat('*')) { - Expression a = x, b = parseFactor(); - x = (() -> a.eval() * b.eval()); - } else if (eat('/')) { - Expression a = x, b = parseFactor(); - x = (() -> a.eval() / b.eval()); - } else - return x; - } - } - - Expression parseFactor() { - if (eat('+')) - return parseFactor(); // unary plus - if (eat('-')) { - Expression x = (() -> -parseFactor().eval()); - return x; // unary minus - } - - Expression 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(); - final Integer innerPos = new Integer(this.pos); - x = (() -> Double.parseDouble(str.substring(startPos, innerPos))); - } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '[' || ch == ']') { // functions - while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '[' || ch == ']') - nextChar(); - String func = str.substring(startPos, this.pos); - if (funct.contains(func)) { - Expression a = parseFactor(); - if (func.equals("sqrt")) - x = (() -> Math.sqrt(a.eval())); - else if (func.equals("sin")) - x = (() -> Math.sin(Math.toRadians(a.eval()))); - else if (func.equals("cos")) - x = (() -> Math.cos(Math.toRadians(a.eval()))); - else if (func.equals("tan")) - x = (() -> Math.tan(Math.toRadians(a.eval()))); - else - throw new RuntimeException("Unknown function: " + func); - } else { - x = (() -> variables.get(func)); - } - } else { - throw new RuntimeException("Unexpected: " + (char) ch); - } - - if (eat('^')) { - Expression a = x, b = parseFactor(); - x = (() -> Math.pow(a.eval(), b.eval())); // exponentiation - } - - return x; - } - }.parse(); - } - - private static final List funct = new ArrayList<>(); - static { - funct.add("sqrt"); - funct.add("sin"); - funct.add("cos"); - funct.add("tan"); - } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/upgrades/dataobjects/UpgradesData.java b/src/main/java/world/bentobox/upgrades/dataobjects/UpgradesData.java index 627adc4..531f6f9 100644 --- a/src/main/java/world/bentobox/upgrades/dataobjects/UpgradesData.java +++ b/src/main/java/world/bentobox/upgrades/dataobjects/UpgradesData.java @@ -8,6 +8,9 @@ import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.Table; +/** + * Database object for storing upgrades data + */ @Table(name = "UpgradesData") public class UpgradesData implements DataObject { diff --git a/src/main/java/world/bentobox/upgrades/listeners/IslandChangeListener.java b/src/main/java/world/bentobox/upgrades/listeners/IslandChangeListener.java index 7464257..993166c 100644 --- a/src/main/java/world/bentobox/upgrades/listeners/IslandChangeListener.java +++ b/src/main/java/world/bentobox/upgrades/listeners/IslandChangeListener.java @@ -21,6 +21,10 @@ public void onLimitsJoinPermCheckEvent(LimitsJoinPermCheckEvent e) { //e.setCancelled(true); }*/ + /** + * Delete data if the island is removed + * @param e IslandDeleteEvent + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false) public void onIslandDeleteEvent(IslandDeleteEvent e) { Island island = e.getIsland(); diff --git a/src/main/java/world/bentobox/upgrades/listeners/JoinPermCheckListener.java b/src/main/java/world/bentobox/upgrades/listeners/LimitsPermCheckListener.java similarity index 67% rename from src/main/java/world/bentobox/upgrades/listeners/JoinPermCheckListener.java rename to src/main/java/world/bentobox/upgrades/listeners/LimitsPermCheckListener.java index 1260f75..76883db 100644 --- a/src/main/java/world/bentobox/upgrades/listeners/JoinPermCheckListener.java +++ b/src/main/java/world/bentobox/upgrades/listeners/LimitsPermCheckListener.java @@ -11,12 +11,21 @@ import world.bentobox.limits.events.LimitsPermCheckEvent; import world.bentobox.upgrades.UpgradesAddon; -public class JoinPermCheckListener implements Listener { +/** + * Checks perms of players if Limits changes something + */ +public class LimitsPermCheckListener implements Listener { - public JoinPermCheckListener(UpgradesAddon addon) { + private UpgradesAddon addon; + + public LimitsPermCheckListener(UpgradesAddon addon) { this.addon = addon; } + /** + * Limits changed a permission - cancel it if it's being managed by Upgrades + * @param e LimitsPermCheckEvent + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false) public void onLimitsPermCheckEvent(LimitsPermCheckEvent e) { Material block = e.getMaterial(); @@ -24,18 +33,21 @@ public void onLimitsPermCheckEvent(LimitsPermCheckEvent e) { EntityGroup entgroup = e.getEntityGroup(); World world = e.getPlayer().getWorld(); + // Cancel the event if this block is handled by Upgrades if (block != null) { if (this.addon.getUpgradesManager().getAllBlockLimitsUpgradeTiers(world).containsKey(block)) { e.setCancelled(true); } } + // Cancel the event if this entity is being covered by Upgrades if (et != null) { if (this.addon.getUpgradesManager().getAllEntityLimitsUpgradeTiers(world).containsKey(et)) { e.setCancelled(true); } } + // Cancel if this Entity Group is handled by Upgrades if (entgroup != null) { if (this.addon.getUpgradesManager().getAllEntityGroupLimitsUpgradeTiers(world).containsKey(entgroup.getName())) { e.setCancelled(true); @@ -43,6 +55,4 @@ public void onLimitsPermCheckEvent(LimitsPermCheckEvent e) { } } - UpgradesAddon addon; - } diff --git a/src/main/java/world/bentobox/upgrades/ui/Panel.java b/src/main/java/world/bentobox/upgrades/ui/Panel.java index 3f875d5..6069f5a 100644 --- a/src/main/java/world/bentobox/upgrades/ui/Panel.java +++ b/src/main/java/world/bentobox/upgrades/ui/Panel.java @@ -10,18 +10,35 @@ import world.bentobox.upgrades.UpgradesAddon; import world.bentobox.upgrades.api.Upgrade; +/** + * User interface for Upgrades + */ public class Panel { + private UpgradesAddon addon; + private Island island; + + /** + * Start to create a panel for this island + * @param addon Upgrades + * @param island island + */ public Panel(UpgradesAddon addon, Island island) { super(); this.addon = addon; this.island = island; } + /** + * Show the GUI to the user + * @param user user + */ public void showPanel(User user) { - int islandLevel = this.addon.getUpgradesManager().getIslandLevel(this.island); + // Start the builder + PanelBuilder pb = new PanelBuilder().name(user.getTranslation("upgrades.ui.upgradepanel.title")); - PanelBuilder pb = new PanelBuilder().name(user.getTranslation("upgrades.ui.upgradepanel.title")); + // Get the island level + int islandLevel = this.addon.getUpgradesManager().getIslandLevel(this.island); this.addon.getAvailableUpgrades().forEach(upgrade -> { upgrade.updateUpgradeValue(user, this.island); @@ -70,7 +87,4 @@ private List getDescription(User user, Upgrade upgrade, int islandLevel) return descrip; } - private UpgradesAddon addon; - private Island island; - } diff --git a/src/main/java/world/bentobox/upgrades/upgrades/BlockLimitsUpgrade.java b/src/main/java/world/bentobox/upgrades/upgrades/BlockLimitsUpgrade.java index bd72d04..a4de399 100644 --- a/src/main/java/world/bentobox/upgrades/upgrades/BlockLimitsUpgrade.java +++ b/src/main/java/world/bentobox/upgrades/upgrades/BlockLimitsUpgrade.java @@ -16,13 +16,32 @@ import world.bentobox.upgrades.api.Upgrade; import world.bentobox.upgrades.dataobjects.UpgradesData; +/** + * An upgrade of limits for a specific block type + */ public class BlockLimitsUpgrade extends Upgrade { + private Material block; + + /** + * Initializes a BlockLimitsUpgrade for a specific block type. + * + * @param addon The instance of the UpgradesAddon. + * @param block The Material representing the block type for this upgrade. + */ public BlockLimitsUpgrade(UpgradesAddon addon, Material block) { super(addon, "LimitsUpgrade-" + block.toString(), block.toString() + " limits Upgrade", block); this.block = block; } + /** + * Updates the upgrade values for the specified user and island. + * This includes calculating the upgrade level, adjusting descriptions, + * and setting the display name for the upgrade based on current configurations. + * + * @param user The user for whom the upgrade values are being updated. + * @param island The island associated with the upgrade. + */ @Override public void updateUpgradeValue(User user, Island island) { UpgradesAddon upgradeAddon = this.getUpgradesAddon(); @@ -66,6 +85,14 @@ public void updateUpgradeValue(User user, Island island) { this.setDisplayName(newDisplayName); } + /** + * Determines whether this upgrade should be displayed to the user. + * Checks permissions and other configurations to ensure visibility. + * + * @param user The user requesting the visibility check. + * @param island The island associated with the upgrade. + * @return true if the upgrade should be displayed; false otherwise. + */ @Override public boolean isShowed(User user, Island island) { // Get the addon @@ -117,11 +144,26 @@ public boolean isShowed(User user, Island island) { return false; } + /** + * Logs an error message for issues related to permissions or configurations. + * + * @param name The name of the player associated with the error. + * @param perm The permission string causing the error. + * @param error The specific error message to log. + */ private void logError(String name, String perm, String error) { this.getUpgradesAddon() .logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring..."); } + /** + * Performs the upgrade for the specified user and island. + * This involves applying the upgrade's effects, such as modifying block limits. + * + * @param user The user performing the upgrade. + * @param island The island to which the upgrade is being applied. + * @return true if the upgrade is successfully applied; false otherwise. + */ @Override public boolean doUpgrade(User user, Island island) { UpgradesAddon islandAddon = this.getUpgradesAddon(); @@ -145,6 +187,4 @@ public boolean doUpgrade(User user, Island island) { return true; } - private Material block; - } diff --git a/src/main/java/world/bentobox/upgrades/upgrades/CommandUpgrade.java b/src/main/java/world/bentobox/upgrades/upgrades/CommandUpgrade.java index e4649fe..836ac5f 100644 --- a/src/main/java/world/bentobox/upgrades/upgrades/CommandUpgrade.java +++ b/src/main/java/world/bentobox/upgrades/upgrades/CommandUpgrade.java @@ -14,13 +14,39 @@ import world.bentobox.upgrades.api.Upgrade; import world.bentobox.upgrades.dataobjects.UpgradesData; +/** + * Represents an upgrade that executes a specific command when applied. + * This class extends the base functionality of {@link Upgrade} to support + * command-based upgrades for islands or players. + * + *

The {@code CommandUpgrade} class enables server administrators to + * configure upgrades that trigger predefined commands, allowing for + * customized upgrade behaviors.

+ */ public class CommandUpgrade extends Upgrade { - public CommandUpgrade(UpgradesAddon addon, String cmdId, Material icon) { + private String cmdId; + + /** + * Constructs a new {@code CommandUpgrade} instance. + * + * @param addon The instance of the {@code UpgradesAddon}. + * @param cmdId The command + * @param icon The material to represent the upgrade visually in the UI. + */ + public CommandUpgrade(UpgradesAddon addon, String cmdId, Material icon) { super(addon, "command-" + cmdId, addon.getSettings().getCommandName(cmdId), icon); this.cmdId = cmdId; } + /** + * Updates the upgrade values for the specified user and island. + * This method sets the upgrade's display name and other relevant + * attributes based on the current configuration and context. + * + * @param user The user for whom the upgrade values are being updated. + * @param island The island associated with the upgrade. + */ @Override public void updateUpgradeValue(User user, Island island) { UpgradesAddon upgradesAddon = this.getUpgradesAddon(); @@ -48,6 +74,15 @@ public void updateUpgradeValue(User user, Island island) { this.setUpgradeValues(user, upgrade); } + /** + * Determines whether this upgrade should be displayed to the user. + * Checks the visibility conditions for the upgrade, including + * permissions and other contextual requirements. + * + * @param user The user requesting the visibility check. + * @param island The island associated with the upgrade. + * @return {@code true} if the upgrade should be displayed; {@code false} otherwise. + */ @Override public boolean isShowed(User user, Island island) { UpgradesAddon upgradeAddon = this.getUpgradesAddon(); @@ -93,10 +128,26 @@ public boolean isShowed(User user, Island island) { return false; } + /** + * Logs an error message for issues related to the command upgrade configuration. + * + * @param user The user associated with the error. + * @param command The command causing the error. + * @param message The specific error message to log. + */ private void logError(String name, String perm, String error) { this.getUpgradesAddon().logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring..."); } + /** + * Executes the command associated with the upgrade. + * This method is triggered when the upgrade is applied and performs + * the configured command action. + * + * @param user The user triggering the upgrade. + * @param island The island on which the upgrade is applied. + * @return {@code true} if the command was executed successfully; {@code false} otherwise. + */ @Override public boolean doUpgrade(User user, Island island) { UpgradesAddon upgradeAddon = this.getUpgradesAddon(); @@ -119,6 +170,4 @@ public boolean doUpgrade(User user, Island island) { return true; } - private String cmdId; - } diff --git a/src/main/java/world/bentobox/upgrades/upgrades/EntityGroupLimitsUpgrade.java b/src/main/java/world/bentobox/upgrades/upgrades/EntityGroupLimitsUpgrade.java index 47ef471..ababff2 100644 --- a/src/main/java/world/bentobox/upgrades/upgrades/EntityGroupLimitsUpgrade.java +++ b/src/main/java/world/bentobox/upgrades/upgrades/EntityGroupLimitsUpgrade.java @@ -14,13 +14,37 @@ import world.bentobox.upgrades.api.Upgrade; import world.bentobox.upgrades.dataobjects.UpgradesData; +/** + * Represents an upgrade that modifies the limits of a specific entity group on an island. + * This class extends the {@link Upgrade} base class to support entity group-specific limits, + * allowing for customized upgrade behaviors in the BentoBox ecosystem. + * + *

Each instance of {@code EntityGroupLimitsUpgrade} is associated with a specific entity group, + * enabling server administrators to configure upgrades for entity group limits such as mobs or other entities.

+ */ public class EntityGroupLimitsUpgrade extends Upgrade { + private String group; + + /** + * Constructs a new {@code EntityGroupLimitsUpgrade} instance for a specified entity group. + * + * @param addon The instance of the {@code UpgradesAddon}. + * @param group The name of the entity group associated with this upgrade. + */ public EntityGroupLimitsUpgrade(UpgradesAddon addon, String group) { super(addon, "LimitsUpgrade-" + group, group + " limits Upgrade", addon.getSettings().getEntityGroupIcon(group)); this.group = group; } + /** + * Updates the upgrade values for the specified user and island. + * This method calculates and sets the upgrade's description, display name, and other + * relevant attributes based on the current upgrade level and island context. + * + * @param user The user for whom the upgrade values are being updated. + * @param island The island associated with the upgrade. + */ @Override public void updateUpgradeValue(User user, Island island) { UpgradesAddon upgradeAddon = this.getUpgradesAddon(); @@ -67,6 +91,15 @@ public void updateUpgradeValue(User user, Island island) { this.setDisplayName(newDisplayName); } + /** + * Determines whether this upgrade should be displayed to the user. + * Checks the visibility conditions for the upgrade, including permissions and other + * contextual requirements, ensuring that only valid upgrades are shown. + * + * @param user The user requesting the visibility check. + * @param island The island associated with the upgrade. + * @return {@code true} if the upgrade should be displayed; {@code false} otherwise. + */ @Override public boolean isShowed(User user, Island island) { // Get the addon @@ -117,10 +150,26 @@ public boolean isShowed(User user, Island island) { return false; } + /** + * Logs an error message for issues related to permissions or configurations. + * + * @param name The name of the player associated with the error. + * @param perm The permission string causing the error. + * @param error A description of the specific error to log. + */ private void logError(String name, String perm, String error) { this.getUpgradesAddon().logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring..."); } + /** + * Performs the upgrade for the specified user and island. + * This method applies the upgrade by increasing the limits for the specified entity group + * and updating the island's limit data accordingly. + * + * @param user The user performing the upgrade. + * @param island The island on which the upgrade is applied. + * @return {@code true} if the upgrade was successfully applied; {@code false} otherwise. + */ @Override public boolean doUpgrade(User user, Island island) { UpgradesAddon islandAddon = this.getUpgradesAddon(); @@ -144,5 +193,4 @@ public boolean doUpgrade(User user, Island island) { return true; } - private String group; } diff --git a/src/main/java/world/bentobox/upgrades/upgrades/EntityLimitsUpgrade.java b/src/main/java/world/bentobox/upgrades/upgrades/EntityLimitsUpgrade.java index e7331b7..f737ef3 100644 --- a/src/main/java/world/bentobox/upgrades/upgrades/EntityLimitsUpgrade.java +++ b/src/main/java/world/bentobox/upgrades/upgrades/EntityLimitsUpgrade.java @@ -15,14 +15,36 @@ import world.bentobox.upgrades.api.Upgrade; import world.bentobox.upgrades.dataobjects.UpgradesData; +/** + * Represents an upgrade that modifies the limits for a specific entity type on an island. + * This class extends the {@link Upgrade} base class, enabling administrators to configure + * entity-specific limit upgrades for the BentoBox ecosystem. + * + *

The {@code EntityLimitsUpgrade} class allows for the customization and management + * of entity limits based on upgrade levels, island configurations, and permissions.

+ */ public class EntityLimitsUpgrade extends Upgrade { + /** + * Constructs a new {@code EntityLimitsUpgrade} instance for a specific entity type. + * + * @param addon The instance of the {@code UpgradesAddon}. + * @param entity The {@link EntityType} associated with this upgrade. + */ public EntityLimitsUpgrade(UpgradesAddon addon, EntityType entity) { super(addon, "LimitsUpgrade-" + entity.toString(), entity.toString() + " limits Upgrade", addon.getSettings().getEntityIcon(entity)); this.entity = entity; } + /** + * Updates the upgrade values for the specified user and island. + * This method calculates and sets the upgrade's description, display name, and other + * relevant attributes based on the current upgrade level and island context. + * + * @param user The user for whom the upgrade values are being updated. + * @param island The island associated with the upgrade. + */ @Override public void updateUpgradeValue(User user, Island island) { UpgradesAddon upgradeAddon = this.getUpgradesAddon(); @@ -70,6 +92,15 @@ public void updateUpgradeValue(User user, Island island) { this.setDisplayName(newDisplayName); } + /** + * Determines whether this upgrade should be displayed to the user. + * Checks the visibility conditions for the upgrade, including permissions and other + * contextual requirements, ensuring that only valid upgrades are shown. + * + * @param user The user requesting the visibility check. + * @param island The island associated with the upgrade. + * @return {@code true} if the upgrade should be displayed; {@code false} otherwise. + */ @Override public boolean isShowed(User user, Island island) { // Get the addon @@ -121,11 +152,27 @@ public boolean isShowed(User user, Island island) { return false; } + /** + * Logs an error message for issues related to permissions or configurations. + * + * @param name The name of the player associated with the error. + * @param perm The permission string causing the error. + * @param error A description of the specific error to log. + */ private void logError(String name, String perm, String error) { this.getUpgradesAddon() .logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring..."); } + /** + * Performs the upgrade for the specified user and island. + * This method applies the upgrade by increasing the limits for the specified entity type + * and updating the island's limit data accordingly. + * + * @param user The user performing the upgrade. + * @param island The island on which the upgrade is applied. + * @return {@code true} if the upgrade was successfully applied; {@code false} otherwise. + */ @Override public boolean doUpgrade(User user, Island island) { UpgradesAddon islandAddon = this.getUpgradesAddon(); diff --git a/src/main/java/world/bentobox/upgrades/upgrades/RangeUpgrade.java b/src/main/java/world/bentobox/upgrades/upgrades/RangeUpgrade.java index 9250bd7..0611340 100644 --- a/src/main/java/world/bentobox/upgrades/upgrades/RangeUpgrade.java +++ b/src/main/java/world/bentobox/upgrades/upgrades/RangeUpgrade.java @@ -15,19 +15,37 @@ import world.bentobox.upgrades.api.Upgrade; /** - * Upgrade Object for range upgrade + * Represents an upgrade that increases the protection range of an island. + * This class extends the {@link Upgrade} base class and provides functionality + * for managing range upgrades within the BentoBox ecosystem. * + *

The {@code RangeUpgrade} dynamically calculates upgrade levels, updates + * protection ranges, and ensures that the new range stays within configured limits.

+ * + *

Administrators can configure this upgrade to allow players to extend their + * island's protected area based on various factors such as the island's level, + * number of members, and the current game mode.

+ * * @author Ikkino, tastybento - * */ public class RangeUpgrade extends Upgrade { + /** + * Constructs a new {@code RangeUpgrade} instance. + * + * @param addon The instance of the {@code UpgradesAddon}. + */ public RangeUpgrade(UpgradesAddon addon) { super(addon, "RangeUpgrade", "RangeUpgrade", Material.OAK_FENCE); } /** - * When user open the interface + * Updates the upgrade values when the user opens the upgrade interface. + * This method dynamically calculates and sets the upgrade's details such as + * the current level, maximum level, and associated costs. + * + * @param user The user viewing the upgrade interface. + * @param island The island for which the upgrade is being viewed. */ @Override public void updateUpgradeValue(User user, Island island) { @@ -80,6 +98,14 @@ public void updateUpgradeValue(User user, Island island) { this.setDisplayName(newDisplayName); } + /** + * Determines whether the upgrade should be displayed in the user's interface. + * This involves checking user permissions and other contextual requirements. + * + * @param user The user requesting the visibility check. + * @param island The island associated with the upgrade. + * @return {@code true} if the upgrade should be visible; {@code false} otherwise. + */ @Override public boolean isShowed(User user, Island island) { // Get the addon @@ -131,13 +157,26 @@ public boolean isShowed(User user, Island island) { return false; } + /** + * Logs an error related to permissions or configuration issues. + * + * @param name The name of the player associated with the error. + * @param perm The permission string causing the error. + * @param error A description of the specific error to log. + */ private void logError(String name, String perm, String error) { this.getUpgradesAddon() .logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring..."); } /** - * When user do upgrade + * Applies the range upgrade to the specified island. + * Increases the island's protection range and triggers relevant events to + * reflect the change. Ensures that the new range does not exceed the maximum limit. + * + * @param user The user performing the upgrade. + * @param island The island to which the upgrade is applied. + * @return {@code true} if the upgrade was successfully applied; {@code false} otherwise. */ @Override public boolean doUpgrade(User user, Island island) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 6e63e74..9cc33b6 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,10 @@ # Config file for Upgrades addon +# ====================== # Disabled Game Mode Addons -# IslandUpgrades will not work in these game modes +# ====================== +# List of game modes where the IslandUpgrades functionality will not work. +# Specify game modes by name in the array below. disabled-gamemodes: [] # Range Upgrade Default Tiers @@ -14,7 +17,7 @@ disabled-gamemodes: [] # vault-cost: Money cost of upgrade # permission-level: Is the level of permission needed to upgrade # permission: "[GAMEMODE].upgrades.[UPGRADE].[LEVEL]" -# Exemple for bskyblock with range-upgrade with a permission level of 2 +# Example for bskyblock with range-upgrade with a permission level of 2 # "bskyblock.upgrades.rangeupgrade.2" # # Note: for upgrade-range, island-min-level and vault-cost: @@ -27,41 +30,75 @@ disabled-gamemodes: [] # # Caution: You should always check that at max level, your players won't attain max range for island # Permission upgrade name: rangeupgrade + +# ====================== +# Range Upgrade Configuration +# ====================== +# Range Upgrade Default Tiers +# Each tier must define: +# max-level: The maximum level this tier applies to. +# upgrade: The number of blocks added to the protection range at each level. + +# Optional fields: +# island-min-level: Minimum island level required to unlock this tier (requires Level addon). +# vault-cost: The monetary cost (in Vault currency) for each upgrade. +# permission-level: The required permission level for this upgrade. + +# Permissions format: +# [GAMEMODE].upgrades.[UPGRADE].[LEVEL] +# Example: +# bskyblock.upgrades.rangeupgrade.2 + +# Note: +# - You can use mathematical expressions (+, -, *, /, ^) and functions (sqrt, sin, cos). +# - Special variables: +# [level]: Current upgrade level. +# [islandLevel]: Island level from Level addon (can be 0 if not set). +# [numberPlayer]: Number of players in the island team. +# Ensure the maximum level configuration does not allow islands to exceed their maximum range. range-upgrade: tier1: max-level: 5 - upgrade: "5" - island-min-level: "2" - vault-cost: "[level]*100" + upgrade: "5" # Adds 5 blocks to the protection range per level in this tier. + island-min-level: "2" # Minimum island level required is 2. + vault-cost: "[level]*100" # Cost increases by 100 Vault currency per level. tier2: max-level: 10 upgrade: "3" island-min-level: "4" - vault-cost: "[level]*[numberPlayer]*200" + vault-cost: "[level]*[numberPlayer]*200" # Cost scales with island members and level. -# Permission upgrade name: limitsupgrade-[BLOCK] -> In lower case +# ====================== +# Block Limits Upgrade Configuration +# ====================== +# Defines tiers for block-specific upgrades. +# Upgrade name format: limitsupgrade-[BLOCK] (lowercase block name). block-limits-upgrade: HOPPER: # Permission upgrade name: limitsupgrade-hopper tier1: max-level: 2 - upgrade: "1" + upgrade: "1" # Adds 1 hopper per upgrade level. island-min-level: "2" vault-cost: "[level]*100" tier2: max-level: 5 upgrade: "1" island-min-level: "4" - vault-cost: "([level]-2)*[numberPlayer]*700" - permission-level: 1 + vault-cost: "([level]-2)*[numberPlayer]*700" # Scales with team size for advanced upgrades. + permission-level: 1 # Requires permission level 1 to access. -# Permission upgrade name: limitsupgrade-[ENTITY] -> In lower case +# ====================== +# Entity Limits Upgrade Configuration +# ====================== +# Defines tiers for upgrades specific to entities (e.g., chickens). +# UPermission upgrade name format: limitsupgrade-[ENTITY] (lowercase entity name). entity-limits-upgrade: CHICKEN: # Permission upgrade name: limitsupgrade-chicken tier1: max-level: 2 - upgrade: "1" + upgrade: "1" # Adds 1 chicken limit per upgrade level. island-min-level: "2" vault-cost: "[level]*100" tier2: @@ -69,16 +106,20 @@ entity-limits-upgrade: upgrade: "1" island-min-level: "4" vault-cost: "([level]-2)*[numberPlayer]*700" - permission-level: 3 + permission-level: 3 # Requires permission level 3 for advanced tiers. -# Permission upgrade name: limitsupgrade-[GROUP] -> In lower case +# ====================== +# Entity Group Limits Upgrade Configuration +# ====================== +# Defines upgrades for groups of entities. Group names must match those in the Limits addon. +# Upgrade name format: limitsupgrade-[GROUP] (lowercase group name). entity-group-limits-upgrade: # this name has to match with your entity group names in the limits addon if installed group1: # Permission upgrade name: limitsupgrade-group1 tier1: max-level: 2 - upgrade: "1" + upgrade: "1" # Adds 1 group entity limit per upgrade level. island-min-level: "2" vault-cost: "[level]*100" tier2: @@ -88,10 +129,14 @@ entity-group-limits-upgrade: vault-cost: "([level]-2)*[numberPlayer]*700" permission-level: 3 -# For the command section: -# [player] : is the name of the player -# [level] : is the level of the upgrade -# [owner] : is the name of the island's owner +# ====================== +# Command Upgrade Configuration +# ====================== +# Defines upgrades that trigger commands when unlocked. +# Variables: +# [player]: Name of the player unlocking the upgrade. +# [level]: Upgrade level. +# [owner]: Name of the island owner. # Permission upgrade name: command-[NAME] -> In lower case + NAME != name: command-upgrade: lambda-upgrade: @@ -101,7 +146,7 @@ command-upgrade: max-level: 1 island-min-level: "2" vault-cost: "[level]*100" - console: true + console: true # Indicates the command is executed by the server console. command: - "say [player] has upgrade his lambda to level [level]" tier2: @@ -113,12 +158,14 @@ command-upgrade: - "say [player] has upgrade his lambda to level [level]" - "say [player] has reached the max level" -# GameMode differences +# ====================== +# GameMode-Specific Configurations +# ====================== +# Define custom tiers for specific game modes. # List any tiers that you want to add # You can overwrite a tier by using the same name gamemodes: BSkyBlock: - range-upgrade: tier3: max-level: 15 @@ -162,6 +209,10 @@ gamemodes: - "say [player] has upgrade his lambda to level [level]" - "say [player] has attained max on BSkyBlock" +# ====================== +# Icons for UI Representation +# ====================== +# Define icons used to represent entities, groups, and commands in the user interface. entity-icon: CHICKEN: CHICKEN_SPAWN_EGG diff --git a/src/test/java/world/bentobox/upgrades/UpgradesAddonTest.java b/src/test/java/world/bentobox/upgrades/UpgradesAddonTest.java new file mode 100644 index 0000000..75f73bc --- /dev/null +++ b/src/test/java/world/bentobox/upgrades/UpgradesAddonTest.java @@ -0,0 +1,436 @@ +package world.bentobox.upgrades; + +import static org.junit.Assert.assertFalse; +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.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.Addon.State; +import world.bentobox.bentobox.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.hooks.VaultHook; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.FlagsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.upgrades.api.Upgrade; +import world.bentobox.upgrades.mocks.ServerMocks; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class, BentoBox.class, User.class, IslandsManager.class }) +public class UpgradesAddonTest { + + 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 UpgradesAddon 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 VaultHook vh; + private @NonNull String targetIslandId = UUID.randomUUID().toString(); + + @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); + try (JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(jFile))) { + //Added the new files to the jar. + try (FileInputStream fis = new FileInputStream(path.toFile())) { + byte[] buffer = new byte[1024]; + int bytesRead = 0; + JarEntry entry = new JarEntry(path.toString()); + tempJarOutputStream.putNextEntry(entry); + while ((bytesRead = fis.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + } + } + } + /** + * @throws java.lang.Exception + */ + @SuppressWarnings("deprecation") + @Before + public void setUp() throws Exception { + ServerMocks.newServer(); + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // 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); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Player + Player p = mock(Player.class); + 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(any(), any(UUID.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + + // Locales + // Return the reference (USE THIS IN THE FUTURE) + when(user.getTranslation(anyString())) + .thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + + // Server + 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 UpgradesAddon(); + File dataFolder = new File("addons/Bank"); + addon.setDataFolder(dataFolder); + addon.setFile(jFile); + AddonDescription desc = new AddonDescription.Builder("bentobox", "Upgrades", "1.3").description("test") + .authors("tastybento").build(); + addon.setDescription(desc); + // Addons manager + when(am.getGameModeAddons()).thenReturn(List.of()); // No game modes + 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 + 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); + + // Vault + when(plugin.getVault()).thenReturn(Optional.of(vh)); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + ServerMocks.unsetBukkitServer(); + deleteAll(new File("database")); + } + + @AfterClass + public static void cleanUp() throws Exception { + new File("addon.jar").delete(); + new File("config.yml").delete(); + deleteAll(new File("addons")); + } + + private static void deleteAll(File file) throws IOException { + if (file.exists()) { + Files.walk(file.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#onEnable()}. + */ + @Test + public void testOnEnableDisabled() { + addon.onLoad(); + addon.setState(State.DISABLED); + addon.onEnable(); + verify(plugin).logWarning("[Upgrades] Upgrades Addon is not available or disabled!"); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#onEnable()}. + */ + @Test + public void testOnEnableNoAddons() { + addon.onLoad(); + addon.setState(State.ENABLED); + addon.onEnable(); + verify(plugin).logWarning("[Upgrades] Level addon not found so Upgrades won't look for Island Level"); + verify(plugin).logWarning("[Upgrades] Limits addon not found so Island Upgrade won't look for IslandLevel"); + verify(plugin).log("[Upgrades] Upgrades addon enabled"); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#onDisable()}. + */ + @Test + public void testOnDisable() { + addon.onDisable(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#onLoad()}. + */ + @Test + public void testOnLoad() { + addon.onLoad(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#onReload()}. + */ + @Test + public void testOnReload() { + addon.onReload(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getSettings()}. + */ + @Test + public void testGetSettings() { + addon.getSettings(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getUpgradesManager()}. + */ + @Test + public void testGetUpgradesManager() { + addon.getUpgradesManager(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getDatabase()}. + */ + @Test + public void testGetDatabase() { + addon.getDatabase(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getUpgradesLevels(java.lang.String)}. + */ + @Test + public void testGetUpgradesLevels() { + addon.onLoad(); + addon.onEnable(); + addon.getUpgradesLevels(targetIslandId); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#uncacheIsland(java.lang.String, boolean)}. + */ + @Test + public void testUncacheIsland() { + addon.uncacheIsland(targetIslandId, false); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getLevelAddon()}. + */ + @Test + public void testGetLevelAddon() { + addon.getLevelAddon(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getLimitsAddon()}. + */ + @Test + public void testGetLimitsAddon() { + addon.getLimitsAddon(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getVaultHook()}. + */ + @Test + public void testGetVaultHook() { + addon.getVaultHook(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#isLevelProvided()}. + */ + @Test + public void testIsLevelProvided() { + assertFalse(addon.isLevelProvided()); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#isLimitsProvided()}. + */ + @Test + public void testIsLimitsProvided() { + assertFalse(addon.isLimitsProvided()); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#isVaultProvided()}. + */ + @Test + public void testIsVaultProvided() { + assertFalse(addon.isVaultProvided()); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#getAvailableUpgrades()}. + */ + @Test + public void testGetAvailableUpgrades() { + addon.getAvailableUpgrades(); + } + + /** + * Test method for {@link world.bentobox.upgrades.UpgradesAddon#registerUpgrade(world.bentobox.upgrades.api.Upgrade)}. + */ + @Test + public void testRegisterUpgrade() { + Upgrade upgrade = new TestUpgrade(addon, "name", "Name", Material.ACACIA_BOAT); + addon.registerUpgrade(upgrade); + } + + private static class TestUpgrade extends Upgrade { + + public TestUpgrade(UpgradesAddon addon, String name, String displayName, Material icon) { + super(addon, name, displayName, icon); + } + + @Override + public void updateUpgradeValue(User user, Island island) { + // Test implementation + } + + @Override + public boolean isShowed(User user, Island island) { + return true; + } + } + +} diff --git a/src/test/java/world/bentobox/upgrades/api/UpgradeTest.java b/src/test/java/world/bentobox/upgrades/api/UpgradeTest.java new file mode 100644 index 0000000..aeada10 --- /dev/null +++ b/src/test/java/world/bentobox/upgrades/api/UpgradeTest.java @@ -0,0 +1,174 @@ +package world.bentobox.upgrades.api; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Material; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.powermock.modules.junit4.PowerMockRunner; + +import net.milkbowl.vault.economy.EconomyResponse; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.hooks.VaultHook; +import world.bentobox.upgrades.UpgradesAddon; +import world.bentobox.upgrades.UpgradesManager; +import world.bentobox.upgrades.dataobjects.UpgradesData; +import world.bentobox.upgrades.mocks.ServerMocks; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +public class UpgradeTest { + + @Mock + private UpgradesAddon addon; + @Mock + private User user; + @Mock + private Island island; + @Mock + private UpgradesData upgradesData; + @Mock + private UpgradesManager um; + @Mock + private VaultHook vh; + + private TestUpgrade testUpgrade; + private UUID userId; + private String islandId; + + @Before + public void setUp() { + ServerMocks.newServer(); + + MockitoAnnotations.openMocks(this); + + userId = UUID.randomUUID(); + islandId = UUID.randomUUID().toString(); + + when(user.getUniqueId()).thenReturn(userId); + when(island.getUniqueId()).thenReturn(islandId); + when(addon.getAddonByName("upgrades")).thenReturn(java.util.Optional.of(addon)); + when(addon.getUpgradesLevels(islandId)).thenReturn(upgradesData); + + when(um.getIslandLevel(island)).thenReturn(20); + when(addon.getUpgradesManager()).thenReturn(um); + + when(vh.has(any(), anyDouble())).thenReturn(true); // Player has money + when(addon.getVaultHook()).thenReturn(vh); + + testUpgrade = new TestUpgrade(addon, "test_upgrade", "Test Upgrade", Material.DIAMOND); + } + + @After + public void tearDown() { + ServerMocks.unsetBukkitServer(); + } + + @Test + public void testUpgradeInitialization() { + assertNotNull(testUpgrade.getUpgradesAddon()); + assertEquals("test_upgrade", testUpgrade.getName()); + assertEquals("Test Upgrade", testUpgrade.getDisplayName()); + assertEquals(Material.DIAMOND, testUpgrade.getIcon()); + } + + @Test + public void testCanUpgrade_WithSufficientResources() { + Upgrade.UpgradeValues upgradeValues = testUpgrade.new UpgradeValues(5, 100, 1); + testUpgrade.setUpgradeValues(user, upgradeValues); + + when(addon.isLevelProvided()).thenReturn(true); + when(addon.getUpgradesManager().getIslandLevel(island)).thenReturn(5); + when(addon.isVaultProvided()).thenReturn(true); + when(addon.getVaultHook().has(user, 100)).thenReturn(true); + + assertTrue(testUpgrade.canUpgrade(user, island)); + } + + @Test + public void testCanUpgrade_WithInsufficientResources() { + Upgrade.UpgradeValues upgradeValues = testUpgrade.new UpgradeValues(10, 200, 1); + testUpgrade.setUpgradeValues(user, upgradeValues); + + when(addon.isLevelProvided()).thenReturn(true); + when(addon.getUpgradesManager().getIslandLevel(island)).thenReturn(5); + when(addon.isVaultProvided()).thenReturn(true); + when(addon.getVaultHook().has(user, 200)).thenReturn(false); + + assertFalse(testUpgrade.canUpgrade(user, island)); + } + + @Test + public void testDoUpgrade_SuccessfulTransaction() { + Upgrade.UpgradeValues upgradeValues = testUpgrade.new UpgradeValues(5, 100, 1); + testUpgrade.setUpgradeValues(user, upgradeValues); + + when(addon.isVaultProvided()).thenReturn(true); + when(addon.getVaultHook().withdraw(user, 100)) + .thenReturn(new EconomyResponse(100, 0, EconomyResponse.ResponseType.SUCCESS, "")); + + testUpgrade.doUpgrade(user, island); + verify(upgradesData).setUpgradeLevel("test_upgrade", 1); + } + + @Test + public void testDoUpgrade_FailedTransaction() { + Upgrade.UpgradeValues upgradeValues = testUpgrade.new UpgradeValues(5, 100, 1); + testUpgrade.setUpgradeValues(user, upgradeValues); + + when(addon.isVaultProvided()).thenReturn(true); + when(addon.getVaultHook().withdraw(user, 100)) + .thenReturn(new EconomyResponse(100, 0, EconomyResponse.ResponseType.FAILURE, "Error")); + + assertFalse(testUpgrade.doUpgrade(user, island)); + } + + @Test + public void testGetAndSetDescription() { + String description = "Upgrade description"; + testUpgrade.setOwnDescription(user, description); + + assertEquals(description, testUpgrade.getOwnDescription(user)); + } + + @Test + public void testGetAndSetUpgradeValues() { + Upgrade.UpgradeValues upgradeValues = testUpgrade.new UpgradeValues(5, 100, 1); + testUpgrade.setUpgradeValues(user, upgradeValues); + + assertEquals(upgradeValues, testUpgrade.getUpgradeValues(user)); + } + + private static class TestUpgrade extends Upgrade { + + public TestUpgrade(UpgradesAddon addon, String name, String displayName, Material icon) { + super(addon, name, displayName, icon); + } + + @Override + public void updateUpgradeValue(User user, Island island) { + // Test implementation + } + + @Override + public boolean isShowed(User user, Island island) { + return true; + } + } +} diff --git a/src/test/java/world/bentobox/upgrades/command/PlayerUpgradeCommandTest.java b/src/test/java/world/bentobox/upgrades/command/PlayerUpgradeCommandTest.java new file mode 100644 index 0000000..af3184d --- /dev/null +++ b/src/test/java/world/bentobox/upgrades/command/PlayerUpgradeCommandTest.java @@ -0,0 +1,277 @@ +package world.bentobox.upgrades.command; + +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.anyInt; +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.io.File; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +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.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.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.RanksManager; +import world.bentobox.bentobox.util.Util; +import world.bentobox.upgrades.UpgradesAddon; +import world.bentobox.upgrades.UpgradesManager; +import world.bentobox.upgrades.config.Settings; +import world.bentobox.upgrades.mocks.ServerMocks; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class }) +public class PlayerUpgradeCommandTest { + + @Mock + private UpgradesAddon addon; + @Mock + private World world; + @Mock + private CompositeCommand ic; + @Mock + private User user; + + private UUID uuid; + private Settings settings; + + @Mock + private Location location; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private Player p; + private PlayerUpgradeCommand puc; + @Mock + private RanksManager rm; + @Mock + private IslandWorldManager iwm; + @Mock + private UpgradesManager um; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + ServerMocks.newServer(); + // Config + YamlConfiguration config = new YamlConfiguration(); + File configFile = new File("src/main/resources/config.yml"); + assertTrue(configFile.exists()); + config.load(configFile); + + when(addon.getConfig()).thenReturn(config); + + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + when(addon.getPlugin()).thenReturn(plugin); + + // RanksManager + when(rm.getRank(anyInt())).thenReturn(RanksManager.MEMBER_RANK_REF); + when(plugin.getRanksManager()).thenReturn(rm); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + // 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"); + + // World + when(world.toString()).thenReturn("world"); + // Player + // 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"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.isPlayer()).thenReturn(true); + when(user.getLocation()).thenReturn(location); + when(user.getTranslation(anyString())).thenReturn("translation"); + + // Util + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(any())).thenReturn(world); + + // Island Manager + when(plugin.getIslands()).thenReturn(im); + Optional opIsland = Optional.of(island); + when(im.getIslandAt(any())).thenReturn(opIsland); + + // Settings + settings = new Settings(addon); + when(addon.getSettings()).thenReturn(settings); + + // Island + when(location.toVector()).thenReturn(new Vector(0, 60, 0)); + + // IWM + when(iwm.getFriendlyName(world)).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + // Upgrades manager + when(um.getIslandLevel(island)).thenReturn(20); + when(addon.getUpgradesManager()).thenReturn(um); + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + + puc = new PlayerUpgradeCommand(addon, ic); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + ServerMocks.unsetBukkitServer(); + User.clearUsers(); + + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#PlayerUpgradeCommand(world.bentobox.upgrades.UpgradesAddon, world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testPlayerUpgradeCommand() { + assertEquals("upgrade", puc.getLabel()); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("", puc.getPermission()); + assertEquals("upgrades.commands.main.description", puc.getDescription()); + assertTrue(puc.isOnlyPlayer()); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoIsland() { + assertFalse(puc.canExecute(user, "", List.of())); + verify(user).sendMessage("general.errors.no-island"); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNotOnIsland() { + when(im.getIsland(world, user)).thenReturn(island); + assertFalse(puc.canExecute(user, "", List.of())); + verify(user).sendMessage("upgrades.error.notonisland"); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteInsufficientRank() { + when(im.getIsland(world, user)).thenReturn(island); + when(island.onIsland(location)).thenReturn(true); + assertFalse(puc.canExecute(user, "", List.of())); + verify(user).sendMessage("general.errors.insufficient-rank", TextVariables.RANK, "translation"); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteSuccess() { + when(im.getIsland(world, user)).thenReturn(island); + when(island.onIsland(location)).thenReturn(true); + when(island.isAllowed(eq(user), any())).thenReturn(true); + assertTrue(puc.canExecute(user, "", List.of())); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringNoIsland() { + assertFalse(puc.execute(user, "", List.of())); + verify(user).sendMessage("general.errors.no-island"); + + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringShowHelp() { + assertFalse(puc.execute(user, "", List.of("random"))); + verify(user).sendMessage("commands.help.header", "[label]", "BSkyBlock"); + + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteNotOnIsland() { + when(im.getIsland(world, user)).thenReturn(island); + assertFalse(puc.execute(user, "", List.of())); + verify(user).sendMessage("upgrades.error.notonisland"); + } + + /** + * Test method for {@link world.bentobox.upgrades.command.PlayerUpgradeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteSuccess() { + when(im.getIsland(world, user)).thenReturn(island); + when(island.onIsland(location)).thenReturn(true); + when(island.isAllowed(eq(user), any())).thenReturn(true); + assertTrue(puc.execute(user, "", List.of())); + } + +} diff --git a/src/test/java/world/bentobox/upgrades/config/SettingsTest.java b/src/test/java/world/bentobox/upgrades/config/SettingsTest.java new file mode 100644 index 0000000..860df4c --- /dev/null +++ b/src/test/java/world/bentobox/upgrades/config/SettingsTest.java @@ -0,0 +1,278 @@ +package world.bentobox.upgrades.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.util.Map; + +import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.upgrades.UpgradesAddon; +import world.bentobox.upgrades.config.Settings.Expression; +import world.bentobox.upgrades.mocks.ServerMocks; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +public class SettingsTest { + + @Mock + private UpgradesAddon addon; + private Settings settings; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + ServerMocks.newServer(); + // Config + YamlConfiguration config = new YamlConfiguration(); + File configFile = new File("src/main/resources/config.yml"); + assertTrue(configFile.exists()); + config.load(configFile); + + when(addon.getConfig()).thenReturn(config); + + settings = new Settings(addon); + } + + @After + public void tearDown() { + ServerMocks.unsetBukkitServer(); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#Settings(world.bentobox.upgrades.UpgradesAddon)}. + */ + @Test + public void testSettings() { + assertNotNull(settings); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getDisabledGameModes()}. + */ + @Test + public void testGetDisabledGameModes() { + assertTrue(settings.getDisabledGameModes().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getHasRangeUpgrade()}. + */ + @Test + public void testGetHasRangeUpgrade() { + assertTrue(settings.getHasRangeUpgrade()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getMaxRangeUpgrade(java.lang.String)}. + */ + @Test + public void testGetMaxRangeUpgrade() { + assertEquals(10, settings.getMaxRangeUpgrade("")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getDefaultRangeUpgradeTierMap()}. + */ + @Test + public void testGetDefaultRangeUpgradeTierMap() { + assertFalse(settings.getDefaultRangeUpgradeTierMap().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getAddonRangeUpgradeTierMap(java.lang.String)}. + */ + @Test + public void testGetAddonRangeUpgradeTierMap() { + assertTrue(settings.getAddonRangeUpgradeTierMap("").isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getMaxBlockLimitsUpgrade(org.bukkit.Material, java.lang.String)}. + */ + @Test + public void testGetMaxBlockLimitsUpgrade() { + assertEquals(0, settings.getMaxBlockLimitsUpgrade(Material.STONE, "")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getDefaultBlockLimitsUpgradeTierMap()}. + */ + @Test + public void testGetDefaultBlockLimitsUpgradeTierMap() { + assertFalse(settings.getDefaultBlockLimitsUpgradeTierMap().isEmpty()); + + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getAddonBlockLimitsUpgradeTierMap(java.lang.String)}. + */ + @Test + public void testGetAddonBlockLimitsUpgradeTierMap() { + assertTrue(settings.getAddonBlockLimitsUpgradeTierMap("").isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getMaterialsLimitsUpgrade()}. + */ + @Test + public void testGetMaterialsLimitsUpgrade() { + assertFalse(settings.getMaterialsLimitsUpgrade().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getEntityIcon(org.bukkit.entity.EntityType)}. + */ + @Test + public void testGetEntityIcon() { + assertEquals(null, settings.getEntityIcon(EntityType.CAT)); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getEntityGroupIcon(java.lang.String)}. + */ + @Test + public void testGetEntityGroupIcon() { + assertEquals(null, settings.getEntityGroupIcon("")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getMaxEntityLimitsUpgrade(org.bukkit.entity.EntityType, java.lang.String)}. + */ + @Test + public void testGetMaxEntityLimitsUpgrade() { + assertEquals(0, settings.getMaxEntityLimitsUpgrade(EntityType.HOPPER_MINECART, "")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getMaxEntityGroupLimitsUpgrade(java.lang.String, java.lang.String)}. + */ + @Test + public void testGetMaxEntityGroupLimitsUpgrade() { + assertEquals(0, settings.getMaxEntityGroupLimitsUpgrade("", "")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getDefaultEntityLimitsUpgradeTierMap()}. + */ + @Test + public void testGetDefaultEntityLimitsUpgradeTierMap() { + assertFalse(settings.getDefaultEntityLimitsUpgradeTierMap().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getDefaultEntityGroupLimitsUpgradeTierMap()}. + */ + @Test + public void testGetDefaultEntityGroupLimitsUpgradeTierMap() { + assertFalse(settings.getDefaultEntityGroupLimitsUpgradeTierMap().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getAddonEntityLimitsUpgradeTierMap(java.lang.String)}. + */ + @Test + public void testGetAddonEntityLimitsUpgradeTierMap() { + assertTrue(settings.getAddonEntityLimitsUpgradeTierMap("").isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getAddonEntityGroupLimitsUpgradeTierMap(java.lang.String)}. + */ + @Test + public void testGetAddonEntityGroupLimitsUpgradeTierMap() { + assertTrue(settings.getAddonEntityGroupLimitsUpgradeTierMap("").isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getEntityLimitsUpgrade()}. + */ + @Test + public void testGetEntityLimitsUpgrade() { + assertFalse(settings.getEntityLimitsUpgrade().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getEntityGroupLimitsUpgrade()}. + */ + @Test + public void testGetEntityGroupLimitsUpgrade() { + assertFalse(settings.getEntityGroupLimitsUpgrade().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getMaxCommandUpgrade(java.lang.String, java.lang.String)}. + */ + @Test + public void testGetMaxCommandUpgrade() { + assertEquals(0, settings.getMaxCommandUpgrade("", "")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getDefaultCommandUpgradeTierMap()}. + */ + @Test + public void testGetDefaultCommandUpgradeTierMap() { + assertFalse(settings.getDefaultCommandUpgradeTierMap().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getAddonCommandUpgradeTierMap(java.lang.String)}. + */ + @Test + public void testGetAddonCommandUpgradeTierMap() { + assertTrue(settings.getAddonCommandUpgradeTierMap("").isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getCommandUpgrade()}. + */ + @Test + public void testGetCommandUpgrade() { + assertFalse(settings.getCommandUpgrade().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getCommandIcon(java.lang.String)}. + */ + @Test + public void testGetCommandIcon() { + assertNull(settings.getCommandIcon("")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#getCommandName(java.lang.String)}. + */ + @Test + public void testGetCommandName() { + assertEquals(null, settings.getCommandName("")); + } + + /** + * Test method for {@link world.bentobox.upgrades.config.Settings#parse(java.lang.String, java.util.Map)}. + */ + @Test + public void testParse() { + Expression expression = Settings.parse("40*200", Map.of()); + assertEquals(8000D, expression.eval(), 0.1D); + } + +} diff --git a/src/test/java/world/bentobox/upgrades/mocks/ServerMocks.java b/src/test/java/world/bentobox/upgrades/mocks/ServerMocks.java new file mode 100644 index 0000000..7e9b9db --- /dev/null +++ b/src/test/java/world/bentobox/upgrades/mocks/ServerMocks.java @@ -0,0 +1,119 @@ +package world.bentobox.upgrades.mocks; + +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.Server; +import org.bukkit.Tag; +import org.bukkit.UnsafeValues; +import org.eclipse.jdt.annotation.NonNull; + +public final class ServerMocks { + + @SuppressWarnings({ "unchecked", "deprecation" }) + public static @NonNull Server newServer() { + Server mock = mock(Server.class); + + Logger noOp = mock(Logger.class); + when(mock.getLogger()).thenReturn(noOp); + when(mock.isPrimaryThread()).thenReturn(true); + + // Unsafe + UnsafeValues unsafe = mock(UnsafeValues.class); + when(mock.getUnsafe()).thenReturn(unsafe); + + // Server must be available before tags can be mocked. + Bukkit.setServer(mock); + + // Bukkit has a lot of static constants referencing registry values. To initialize those, the + // registries must be able to be fetched before the classes are touched. + Map, Object> registers = new HashMap<>(); + + doAnswer(invocationGetRegistry -> registers.computeIfAbsent(invocationGetRegistry.getArgument(0), clazz -> { + Registry registry = mock(Registry.class); + Map cache = new HashMap<>(); + doAnswer(invocationGetEntry -> { + NamespacedKey key = invocationGetEntry.getArgument(0); + // Some classes (like BlockType and ItemType) have extra generics that will be + // erased during runtime calls. To ensure accurate typing, grab the constant's field. + // This approach also allows us to return null for unsupported keys. + Class constantClazz; + try { + //noinspection unchecked + constantClazz = (Class) clazz + .getField(key.getKey().toUpperCase(Locale.ROOT).replace('.', '_')).getType(); + } catch (ClassCastException e) { + throw new RuntimeException(e); + } catch (NoSuchFieldException e) { + return null; + } + + return cache.computeIfAbsent(key, key1 -> { + Keyed keyed = mock(constantClazz); + doReturn(key).when(keyed).getKey(); + return keyed; + }); + }).when(registry).get(notNull()); + return registry; + })).when(mock).getRegistry(notNull()); + + // Tags are dependent on registries, but use a different method. + // This will set up blank tags for each constant; all that needs to be done to render them + // functional is to re-mock Tag#getValues. + doAnswer(invocationGetTag -> { + Tag tag = mock(Tag.class); + doReturn(invocationGetTag.getArgument(1)).when(tag).getKey(); + doReturn(Set.of()).when(tag).getValues(); + doAnswer(invocationIsTagged -> { + Keyed keyed = invocationIsTagged.getArgument(0); + Class type = invocationGetTag.getArgument(2); + if (!type.isAssignableFrom(keyed.getClass())) { + return null; + } + // Since these are mocks, the exact instance might not be equal. Consider equal keys equal. + return tag.getValues().contains(keyed) + || tag.getValues().stream().anyMatch(value -> value.getKey().equals(keyed.getKey())); + }).when(tag).isTagged(notNull()); + return tag; + }).when(mock).getTag(notNull(), notNull(), notNull()); + + // Once the server is all set up, touch BlockType and ItemType to initialize. + // This prevents issues when trying to access dependent methods from a Material constant. + try { + Class.forName("org.bukkit.inventory.ItemType"); + Class.forName("org.bukkit.block.BlockType"); + Class.forName("org.bukkit.block.Biome"); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + return mock; + } + + public static void unsetBukkitServer() { + try { + Field server = Bukkit.class.getDeclaredField("server"); + server.setAccessible(true); + server.set(null, null); + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private ServerMocks() { + } + +} \ No newline at end of file diff --git a/src/test/java/world/bentobox/upgrades/ui/PanelTest.java b/src/test/java/world/bentobox/upgrades/ui/PanelTest.java new file mode 100644 index 0000000..7abf726 --- /dev/null +++ b/src/test/java/world/bentobox/upgrades/ui/PanelTest.java @@ -0,0 +1,113 @@ +package world.bentobox.upgrades.ui; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.upgrades.UpgradesAddon; +import world.bentobox.upgrades.UpgradesManager; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class }) +public class PanelTest { + + @Mock + private UpgradesAddon addon; + @Mock + private Island island; + @Mock + private UpgradesManager um; + @Mock + private World world; + @Mock + private User user; + + private UUID uuid; + @Mock + private Location location; + @Mock + private IslandsManager im; + @Mock + private Player p; + private Panel panel; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // World + when(world.toString()).thenReturn("world"); + // Player + // 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"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.isPlayer()).thenReturn(true); + when(user.getLocation()).thenReturn(location); + when(user.getTranslation(anyString())).thenReturn("translation"); + + when(um.getIslandLevel(island)).thenReturn(20); + when(addon.getUpgradesManager()).thenReturn(um); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + panel = new Panel(addon, island); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + User.clearUsers(); + } + + /** + * Test method for {@link world.bentobox.upgrades.ui.Panel#Panel(world.bentobox.upgrades.UpgradesAddon, world.bentobox.bentobox.database.objects.Island)}. + */ + @Test + public void testPanel() { + assertNotNull(panel); + } + + /** + * Test method for {@link world.bentobox.upgrades.ui.Panel#showPanel(world.bentobox.bentobox.api.user.User)}. + */ + @Test + public void testShowPanel() { + panel.showPanel(user); + verify(p).openInventory(any(Inventory.class)); + } + +}