diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 77929dfeb..000000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: java -sudo: false -addons: - sonarcloud: - organization: "bentobox-world" - -jdk: - - openjdk8 - - openjdk11 - -matrix: - allow_failures: - - jdk: openjdk11 - -script: - - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install - - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh && . ./install-jdk.sh -F 11 -L GPL - - mvn sonar:sonar -Dsonar.projectKey=BentoBoxWorld_BentoBox - -cache: - directories: - - '$HOME/.m2/repository' - - '$HOME/.sonar/cache' diff --git a/README.md b/README.md index ea309b227..28648010e 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ There are also plenty of other official or community-made Addons you can try and ## Documentation * Start reading: [https://docs.bentobox.world](https://docs.bentobox.world) -* For developers: [Javadocs](https://bentoboxworld.github.io/BentoBox/) +* For developers: [Javadocs](https://ci.codemc.io/job/BentoBoxWorld/job/BentoBox/ws/target/apidocs/index.html) ## Downloads diff --git a/pom.xml b/pom.xml index db6efb907..cccabe1e3 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ -LOCAL - 1.17.2 + 1.17.3 @@ -353,7 +353,7 @@ maven-javadoc-plugin 3.3.0 - 8 + 16 private false -Xdoclint:none @@ -387,7 +387,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.3.0-SNAPSHOT + 3.3.1-SNAPSHOT true diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 627c052fb..abb2d79e4 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -263,7 +263,7 @@ private void completeSetup(long loadTime) { } private void fireCriticalError(String message, String error) { - logError("*****************CRITIAL ERROR!******************"); + logError("*****************CRITICAL ERROR!******************"); logError(message); logError(error + " Disabling BentoBox..."); logError("*************************************************"); diff --git a/src/main/java/world/bentobox/bentobox/api/addons/Addon.java b/src/main/java/world/bentobox/bentobox/api/addons/Addon.java index c05225ef6..54e525b3e 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/Addon.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/Addon.java @@ -40,7 +40,7 @@ public abstract class Addon { private FileConfiguration config; private File dataFolder; private File file; - private Map requestHandlers = new HashMap<>(); + private final Map requestHandlers = new HashMap<>(); protected Addon() { state = State.DISABLED; diff --git a/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java b/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java index 3c62a4153..6f5b6a512 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java @@ -30,8 +30,8 @@ public class AddonClassLoader extends URLClassLoader { private final Map> classes = new HashMap<>(); - private Addon addon; - private AddonsManager loader; + private final Addon addon; + private final AddonsManager loader; public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File jarFile, ClassLoader parent) throws InvalidAddonInheritException, diff --git a/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java b/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java index 1ad76c777..388aac2bc 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java @@ -163,9 +163,12 @@ public ConfigurationSection getPermissions() { } public static class Builder { - private @NonNull String main; - private @NonNull String name; - private @NonNull String version; + private @NonNull + final String main; + private @NonNull + final String name; + private @NonNull + final String version; private @NonNull String description = ""; private @NonNull List authors = new ArrayList<>(); private @NonNull List dependencies = new ArrayList<>(); diff --git a/src/main/java/world/bentobox/bentobox/api/addons/Pladdon.java b/src/main/java/world/bentobox/bentobox/api/addons/Pladdon.java index 0fdf15220..9f1197870 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/Pladdon.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/Pladdon.java @@ -54,4 +54,13 @@ protected void moveJar() { } } + + + /** + * This method enables marks pladdons as enabled. + * By default, enable status is not set because onEnable and onLoad is not triggered. + */ + public void setEnabled() { + this.setEnabled(true); + } } diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonException.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonException.java index 543e20bd7..36e7a1961 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonException.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonException.java @@ -1,10 +1,13 @@ package world.bentobox.bentobox.api.addons.exceptions; +import java.io.Serial; + public abstract class AddonException extends Exception { /** * */ + @Serial private static final long serialVersionUID = 4203162022348693854L; protected AddonException(String errorMessage){ diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonRequestException.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonRequestException.java index 29ec3f051..bba12bf74 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonRequestException.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/AddonRequestException.java @@ -1,8 +1,11 @@ package world.bentobox.bentobox.api.addons.exceptions; +import java.io.Serial; + public class AddonRequestException extends AddonException { - private static final long serialVersionUID = -5698456013070166174L; + @Serial + private static final long serialVersionUID = -5698456013070166174L; public AddonRequestException(String errorMessage) { super(errorMessage); diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java index b6f3fae62..3af6e932d 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java @@ -1,5 +1,7 @@ package world.bentobox.bentobox.api.addons.exceptions; +import java.io.Serial; + /** * @since 1.11.0 */ @@ -8,6 +10,7 @@ public class InvalidAddonDescriptionException extends AddonException { /** * */ + @Serial private static final long serialVersionUID = 7741502900847049986L; public InvalidAddonDescriptionException(String errorMessage) { diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonFormatException.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonFormatException.java index dc228a1a5..d5d61d965 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonFormatException.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonFormatException.java @@ -1,5 +1,6 @@ package world.bentobox.bentobox.api.addons.exceptions; +import java.io.Serial; import java.util.logging.Level; import org.bukkit.Bukkit; @@ -9,6 +10,7 @@ public class InvalidAddonFormatException extends AddonException { /** * */ + @Serial private static final long serialVersionUID = 7741502900847049986L; public InvalidAddonFormatException(String errorMessage) { diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonInheritException.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonInheritException.java index dec1db6db..7a367207a 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonInheritException.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonInheritException.java @@ -1,10 +1,13 @@ package world.bentobox.bentobox.api.addons.exceptions; +import java.io.Serial; + public class InvalidAddonInheritException extends AddonException { /** * */ + @Serial private static final long serialVersionUID = -5847358994397613244L; public InvalidAddonInheritException(String errorMessage) { diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/package-info.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/package-info.java new file mode 100644 index 000000000..28daf0a6a --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/package-info.java @@ -0,0 +1,10 @@ +/** + * This package covers Addon exceptions + *

+ * These exceptions can be thrown when the addon is loaded. + *

+ * + * @since 1.0 + * @author tastybento + */ +package world.bentobox.bentobox.api.addons.exceptions; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/addons/package-info.java b/src/main/java/world/bentobox/bentobox/api/addons/package-info.java new file mode 100644 index 000000000..4d239fd0d --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/addons/package-info.java @@ -0,0 +1,12 @@ +/** + * This package covers all addon-specific API + *

+ * The Addon class and the associated Pladdon are like Bukkit plugins + * but contain extra API specific for BentoBox games. + *

+ * + * @since 1.0 + * @author tastybento + * + */ +package world.bentobox.bentobox.api.addons; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/addons/request/AddonRequestBuilder.java b/src/main/java/world/bentobox/bentobox/api/addons/request/AddonRequestBuilder.java index e5fde808e..13db8ff10 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/request/AddonRequestBuilder.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/request/AddonRequestBuilder.java @@ -20,7 +20,7 @@ public class AddonRequestBuilder { private String addonName; private String requestLabel; - private Map metaData = new HashMap<>(); + private final Map metaData = new HashMap<>(); /** * Define the addon you wish to request. diff --git a/src/main/java/world/bentobox/bentobox/api/addons/request/package-info.java b/src/main/java/world/bentobox/bentobox/api/addons/request/package-info.java new file mode 100644 index 000000000..f643446bd --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/addons/request/package-info.java @@ -0,0 +1,13 @@ +/** + * API to enable plugins to request data from addons. + *

+ * Addons can expose data that they want to expose. To access it, call this class with the appropriate addon name, + * the label for the data that is requested and if required, a map of key-value pairs that will be given to the addon. + * + * Note Since BentoBox 1.17.0, Addons can be declared as Pladdons and be loaded by the Bukkit classloader. This + * enables Plugins to access Addon methods directly so this API is not required. + *

+ * + * @author tastybento + */ +package world.bentobox.bentobox.api.addons.request; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java index 2a88cd0b4..931dd5ebe 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -33,7 +33,7 @@ import world.bentobox.bentobox.util.Util; /** - * BSB composite command + * BentoBox composite command. Provides an abstract implementation of a command. * @author tastybento * @author Poslovitch */ @@ -78,12 +78,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi /** * Map of sub commands */ - private Map subCommands; + private final Map subCommands; /** * Map of aliases for subcommands */ - private Map subCommandAliases; + private final Map subCommandAliases; /** * The command chain from the very top, e.g., island team promote */ @@ -93,7 +93,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi * The prefix to be used in this command */ @Nullable - private String permissionPrefix; + private final String permissionPrefix; /** * The world that this command operates in. This is an overworld and will cover any associated nether or end @@ -104,17 +104,17 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi /** * The addon creating this command, if any */ - private Addon addon; + private final Addon addon; /** * The top level label */ - private String topLabel; + private final String topLabel; /** * Cool down tracker */ - private Map> cooldowns = new HashMap<>(); + private final Map> cooldowns = new HashMap<>(); /** * Top level command @@ -144,7 +144,7 @@ protected CompositeCommand(Addon addon, String label, String... aliases) { // Run setup setup(); - if (!getSubCommand("help").isPresent() && !label.equals("help")) { + if (getSubCommand("help").isEmpty() && !label.equals("help")) { new DefaultHelpCommand(this); } } @@ -204,11 +204,11 @@ protected CompositeCommand(Addon addon, CompositeCommand parent, String label, S p = p.getParent(); index++; } - setDescription(COMMANDS + reference.toString() + ".description"); - setParametersHelp(COMMANDS + reference.toString() + ".parameters"); + setDescription(COMMANDS + reference + ".description"); + setParametersHelp(COMMANDS + reference + ".parameters"); setup(); // If this command does not define its own help class, then use the default help command - if (!getSubCommand("help").isPresent() && !label.equals("help")) { + if (getSubCommand("help").isEmpty() && !label.equals("help")) { new DefaultHelpCommand(this); } } @@ -278,7 +278,7 @@ private CompositeCommand getCommandFromArgs(String[] args) { // get the subcommand corresponding to the arg if (subCommand.hasSubCommands()) { Optional sub = subCommand.getSubCommand(arg); - if (!sub.isPresent()) { + if (sub.isEmpty()) { return subCommand; } // Step down one @@ -602,7 +602,7 @@ public List tabComplete(final CommandSender sender, final String alias, return options; } // Add any tab completion from the subcommand - options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(() -> new ArrayList<>())); + options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(ArrayList::new)); if (command.hasSubCommands()) { options.addAll(getSubCommandLabels(sender, command)); } @@ -701,7 +701,7 @@ public String getTopLabel() { * @since 1.5.0 */ public void setCooldown(String uniqueId, String targetUUID, int timeInSeconds) { - cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID, System.currentTimeMillis() + timeInSeconds * 1000); + cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID, System.currentTimeMillis() + timeInSeconds * 1000L); } /** @@ -711,7 +711,7 @@ public void setCooldown(String uniqueId, String targetUUID, int timeInSeconds) { * @param timeInSeconds - time in seconds to cool down */ public void setCooldown(UUID uniqueId, UUID targetUUID, int timeInSeconds) { - cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000); + cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000L); } /** diff --git a/src/main/java/world/bentobox/bentobox/api/commands/ConfirmableCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/ConfirmableCommand.java index 5b735b1ab..ca1e326a1 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/ConfirmableCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/ConfirmableCommand.java @@ -20,7 +20,7 @@ public abstract class ConfirmableCommand extends CompositeCommand { /** * Confirmation tracker */ - private static Map toBeConfirmed = new HashMap<>(); + private static final Map toBeConfirmed = new HashMap<>(); /** * Top level command @@ -61,9 +61,9 @@ protected ConfirmableCommand(CompositeCommand parent, String label, String... al public void askConfirmation(User user, String message, Runnable confirmed) { // Check for pending confirmations if (toBeConfirmed.containsKey(user)) { - if (toBeConfirmed.get(user).getTopLabel().equals(getTopLabel()) && toBeConfirmed.get(user).getLabel().equalsIgnoreCase(getLabel())) { - toBeConfirmed.get(user).getTask().cancel(); - Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).getRunnable()); + if (toBeConfirmed.get(user).topLabel().equals(getTopLabel()) && toBeConfirmed.get(user).label().equalsIgnoreCase(getLabel())) { + toBeConfirmed.get(user).task().cancel(); + Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).runnable()); toBeConfirmed.remove(user); return; } else { @@ -97,51 +97,9 @@ public void askConfirmation(User user, Runnable confirmed) { } /** - * Holds the data to run once the confirmation is given - * @author tastybento + * Record to hold the data to run once the confirmation is given * */ - private class Confirmer { - private final String topLabel; - private final String label; - private final Runnable runnable; - private final BukkitTask task; - - /** - * @param label - command label - * @param runnable - runnable to run when confirmed - * @param task - task ID to cancel when confirmed - */ - Confirmer(String topLabel, String label, Runnable runnable, BukkitTask task) { - this.topLabel = topLabel; - this.label = label; - this.runnable = runnable; - this.task = task; - } - /** - * @return the topLabel - */ - public String getTopLabel() { - return topLabel; - } - /** - * @return the label - */ - public String getLabel() { - return label; - } - /** - * @return the runnable - */ - public Runnable getRunnable() { - return runnable; - } - /** - * @return the task - */ - public BukkitTask getTask() { - return task; - } - } + private record Confirmer (String topLabel, String label, Runnable runnable, BukkitTask task) { } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/DelayedTeleportCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/DelayedTeleportCommand.java index 490097099..283183d37 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/DelayedTeleportCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/DelayedTeleportCommand.java @@ -26,20 +26,20 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements /** * User monitor map */ - private static Map toBeMonitored = new HashMap<>(); + private static final Map toBeMonitored = new HashMap<>(); @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerMove(PlayerMoveEvent e) { UUID uuid = e.getPlayer().getUniqueId(); // Only check x,y,z - if (toBeMonitored.containsKey(uuid) && !e.getTo().toVector().equals(toBeMonitored.get(uuid).getLocation().toVector())) { + if (toBeMonitored.containsKey(uuid) && !e.getTo().toVector().equals(toBeMonitored.get(uuid).location().toVector())) { moved(uuid); } } private void moved(UUID uuid) { // Player moved - toBeMonitored.get(uuid).getTask().cancel(); + toBeMonitored.get(uuid).task().cancel(); toBeMonitored.remove(uuid); // Player has another outstanding confirmation request that will now be cancelled User.getInstance(uuid).notify("commands.delay.moved-so-command-cancelled"); @@ -103,7 +103,7 @@ public void delayCommand(User user, String message, Runnable confirmed) { UUID uuid = user.getUniqueId(); if (toBeMonitored.containsKey(uuid)) { // A double request - clear out the old one - toBeMonitored.get(uuid).getTask().cancel(); + toBeMonitored.get(uuid).task().cancel(); toBeMonitored.remove(uuid); // Player has another outstanding confirmation request that will now be cancelled user.sendMessage("commands.delay.previous-command-cancelled"); @@ -116,7 +116,7 @@ public void delayCommand(User user, String message, Runnable confirmed) { user.sendMessage("commands.delay.stand-still", "[seconds]", String.valueOf(getSettings().getDelayTime())); // Set up the run task BukkitTask task = Bukkit.getScheduler().runTaskLater(getPlugin(), () -> { - Bukkit.getScheduler().runTask(getPlugin(), toBeMonitored.get(uuid).getRunnable()); + Bukkit.getScheduler().runTask(getPlugin(), toBeMonitored.get(uuid).runnable()); toBeMonitored.remove(uuid); }, getPlugin().getSettings().getDelayTime() * 20L); @@ -135,42 +135,8 @@ public void delayCommand(User user, Runnable command) { /** * Holds the data to run once the confirmation is given - * @author tastybento * */ - private class DelayedCommand { - private final Runnable runnable; - private final BukkitTask task; - private final Location location; - - /** - * @param runnable - runnable to run when confirmed - * @param task - task ID to cancel when confirmed - * @param location - location - */ - DelayedCommand(Runnable runnable, BukkitTask task, Location location) { - this.runnable = runnable; - this.task = task; - this.location = location; - } - /** - * @return the runnable - */ - public Runnable getRunnable() { - return runnable; - } - /** - * @return the task - */ - public BukkitTask getTask() { - return task; - } - /** - * @return the location - */ - public Location getLocation() { - return location; - } - } + private record DelayedCommand(Runnable runnable, BukkitTask task, Location location) {} } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java index 79d9a0433..f335769cc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java @@ -9,6 +9,7 @@ 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.util.IslandInfo; import world.bentobox.bentobox.util.Util; public class AdminInfoCommand extends CompositeCommand { @@ -34,10 +35,8 @@ public boolean execute(User user, String label, List args) { } // If there are no args, then the player wants info on the island at this location if (args.isEmpty()) { - if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) { - user.sendMessage("commands.admin.info.no-island"); - return false; - } + getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user), () -> + user.sendMessage("commands.admin.info.no-island")); return true; } // Get target player @@ -49,7 +48,7 @@ public boolean execute(User user, String label, List args) { // Show info for this player Island island = getIslands().getIsland(getWorld(), targetUUID); if (island != null) { - island.showInfo(user); + new IslandInfo(island).showAdminInfo(user); if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) { user.sendMessage("commands.admin.info.islands-in-trash"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommand.java index fa0dbc1c2..29f0a500f 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommand.java @@ -20,7 +20,7 @@ */ public class AdminResetFlagsCommand extends ConfirmableCommand { - private List options; + private final List options; public AdminResetFlagsCommand(CompositeCommand parent) { super(parent, "resetflags"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetProtectionCenterCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetProtectionCenterCommand.java index 8f7766058..77de3e13e 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetProtectionCenterCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetProtectionCenterCommand.java @@ -59,7 +59,7 @@ public boolean canExecute(User user, String label, List args) { return false; } Optional optionalIsland = getIslands().getIslandAt(targetLoc); - if (!optionalIsland.isPresent()) { + if (optionalIsland.isEmpty()) { user.sendMessage("commands.admin.setspawnpoint.no-island-here"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java index f06126b25..f8c25d8c0 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java @@ -38,8 +38,8 @@ public class AdminSettingsCommand extends CompositeCommand { private final List PROTECTION_FLAG_NAMES; private Island island; private final List SETTING_FLAG_NAMES; - private List WORLD_SETTING_FLAG_NAMES; - private @NonNull Optional flag; + private final List WORLD_SETTING_FLAG_NAMES; + private @NonNull Optional flag = Optional.empty(); private boolean activeState; private int rank; @@ -258,20 +258,14 @@ public Optional> tabComplete(User user, String alias, List } } else if (args.size() == 4) { // Get flag in previous argument - options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> { - switch (f.getType()) { - case PROTECTION: - return getPlugin().getRanksManager() - .getRanks().entrySet().stream() - .filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK) - .map(Entry::getKey) - .map(user::getTranslation).collect(Collectors.toList()); - case SETTING: - return Arrays.asList(active, disabled); - default: - return Collections.emptyList(); - - } + options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> switch (f.getType()) { + case PROTECTION -> getPlugin().getRanksManager() + .getRanks().entrySet().stream() + .filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK) + .map(Entry::getKey) + .map(user::getTranslation).collect(Collectors.toList()); + case SETTING -> Arrays.asList(active, disabled); + default -> Collections.emptyList(); }).orElse(Collections.emptyList()); } return Optional.of(Util.tabLimit(options, lastArg)); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java index 5978050e0..3bb91c4c4 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java @@ -63,7 +63,7 @@ public boolean canExecute(User user, String label, List args) { public boolean execute(User user, String label, List args) { if (NumberUtils.isDigits(args.get(1))) { try { - Integer n = Integer.valueOf(args.get(1)); + int n = Integer.parseInt(args.get(1)); if (n < 1 || n > islands.size()) { user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel()); return false; diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommand.java index 42f695cf6..279531f66 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommand.java @@ -87,6 +87,10 @@ public boolean execute(User user, String label, List args) { world = getPlugin().getIWM().getEndWorld(getWorld()); } Location warpSpot = getSpot(world); + if (world == null || warpSpot == null) { + user.sendMessage("general.errors.no-safe-location-found"); + return false; + } // Otherwise, ask the admin to go to a safe spot String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " " diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java index 2c03b8dee..e17d10041 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java @@ -7,6 +7,7 @@ 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.util.IslandInfo; public class AdminTrashCommand extends CompositeCommand { @@ -63,7 +64,7 @@ private void showTrash(User user, List islands) { user.sendMessage("commands.admin.trash.title"); for (int i = 0; i < islands.size(); i++) { user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1)); - islands.get(i).showInfo(user); + new IslandInfo(islands.get(i)).showInfo(user); } user.sendMessage("commands.admin.trash.use-switch", TextVariables.LABEL, getTopLabel()); user.sendMessage("commands.admin.trash.use-emptytrash", TextVariables.LABEL, getTopLabel()); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminVersionCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminVersionCommand.java index 788ced898..06a0d73d2 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminVersionCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminVersionCommand.java @@ -22,7 +22,7 @@ public void setup() { @Override public boolean execute(User user, String label, List args) { user.sendMessage("commands.bentobox.version.addon-syntax", TextVariables.NAME, getAddon().getDescription().getName(), - TextVariables.VERSION, getAddon().getDescription().getVersion()); + TextVariables.VERSION, getAddon().getDescription().getVersion(), "[state]", getAddon().getState().name()); return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java index da4a26d20..b52060b56 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java @@ -61,7 +61,7 @@ public void setup() { new AdminTeamDisbandCommand(this); new AdminTeamSetownerCommand(this); new AdminTeamFixCommand(this); - // Schems + // Blueprints new AdminBlueprintCommand(this); // Register/unregister islands new AdminRegisterCommand(this); @@ -96,7 +96,7 @@ public void setup() { /** * Defines what will be executed when this command is run. - * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(User, String, List<String>) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(User, String, List) */ @Override public boolean execute(User user, String label, List args) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java index b7b916077..a4218ac9d 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java @@ -70,7 +70,7 @@ public boolean execute(User user, String label, List args) { islands.clear(); this.user = user; try { - Integer days = Integer.parseInt(args.get(0)); + int days = Integer.parseInt(args.get(0)); if (days < 1) { user.sendMessage("commands.admin.purge.days-one-or-more"); return false; diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java index 05392b038..0b8e726b2 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java @@ -24,7 +24,7 @@ public class AdminRangeDisplayCommand extends CompositeCommand { private static final String HIDE = "hide"; // Map of users to which ranges must be displayed - private Map displayRanges = new HashMap<>(); + private final Map displayRanges = new HashMap<>(); public AdminRangeDisplayCommand(CompositeCommand parent) { super(parent, DISPLAY, SHOW, HIDE); @@ -46,29 +46,15 @@ public boolean execute(User user, String label, List args) { if (!displayRanges.containsKey(user)) { switch (label) { - case DISPLAY: - case SHOW: - showZones(user); - break; - case HIDE: - user.sendMessage("commands.admin.range.display.already-off"); - break; - default: - showHelp(this, user); - break; + case DISPLAY, SHOW -> showZones(user); + case HIDE -> user.sendMessage("commands.admin.range.display.already-off"); + default -> showHelp(this, user); } } else { switch (label) { - case DISPLAY: - case HIDE: - hideZones(user); - break; - case SHOW: - user.sendMessage("commands.admin.range.display.already-on"); - break; - default: - showHelp(this, user); - break; + case DISPLAY, HIDE -> hideZones(user); + case SHOW -> user.sendMessage("commands.admin.range.display.already-on"); + default -> showHelp(this, user); } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java index 7153ab56f..b5feecab1 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java @@ -10,6 +10,7 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; +import world.bentobox.bentobox.util.IslandInfo; import world.bentobox.bentobox.util.Util; public class AdminTeamAddCommand extends CompositeCommand { @@ -49,7 +50,10 @@ public boolean execute(User user, String label, List args) { } if (getIslands().inTeam(getWorld(), ownerUUID) && !getIslands().getOwner(getWorld(), ownerUUID).equals(ownerUUID)) { user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0)); - getIslands().getIsland(getWorld(), ownerUUID).showMembers(user); + Island island = getIslands().getIsland(getWorld(), ownerUUID); + if (island != null) { + new IslandInfo(island).showMembers(user); + } return false; } if (getIslands().inTeam(getWorld(), targetUUID)) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java index c3cb032ca..5f44195ed 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java @@ -13,6 +13,7 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; +import world.bentobox.bentobox.util.IslandInfo; import world.bentobox.bentobox.util.Util; /** @@ -59,10 +60,12 @@ public boolean canExecute(User user, String label, List args) { @Override public boolean execute(User user, String label, @NonNull List args) { Island island = getIslands().getIsland(getWorld(), targetUUID); - + if (island == null) { + return false; + } if (targetUUID.equals(island.getOwner())) { user.sendMessage("commands.admin.team.kick.cannot-kick-owner"); - island.showMembers(user); + new IslandInfo(island).showMembers(user); return false; } User target = User.getInstance(targetUUID); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java index 736f85eab..055850486 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java @@ -32,6 +32,12 @@ public void setup() { @Override public boolean canExecute(User user, String label, List args) { + // Check if mid-teleport + if (getIslands().isGoingHome(user)) { + // Tell them again that it's in progress + user.sendMessage("commands.island.go.teleport"); + return false; + } // Check if the island is reserved Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); if (island == null) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommand.java index 1e4589aac..fca1faf73 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommand.java @@ -9,6 +9,7 @@ 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.util.IslandInfo; import world.bentobox.bentobox.util.Util; /** @@ -37,7 +38,7 @@ public boolean execute(User user, String label, List args) { } // If there are no args, then the player wants info on the island at this location if (args.isEmpty()) { - if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) { + if (!getIslands().getIslandAt(user.getLocation()).map(i -> new IslandInfo(i).showInfo(user)).orElse(false)) { user.sendMessage("commands.admin.info.no-island"); return false; } @@ -56,10 +57,10 @@ public boolean execute(User user, String label, List args) { return false; } // Show info for this player - island.showInfo(user); + new IslandInfo(island).showInfo(user); return true; } - + @Override public Optional> tabComplete(User user, String alias, List args) { String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java index 658d0be53..aec829c17 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java @@ -75,10 +75,9 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof Invite)) { + if (!(obj instanceof Invite other)) { return false; } - Invite other = (Invite) obj; return Objects.equals(invitee, other.invitee) && Objects.equals(inviter, other.inviter) && type == other.type; } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java index 6c93dc7e7..b1d2681e3 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java @@ -29,7 +29,7 @@ public class IslandTeamCommand extends CompositeCommand { * Invited list. Key is the invited party, value is the invite. * @since 1.8.0 */ - private Map inviteMap; + private final Map inviteMap; public IslandTeamCommand(CompositeCommand parent) { super(parent, "team"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java index 55c11be17..50c1d12de 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java @@ -22,7 +22,7 @@ */ public class IslandTeamCoopCommand extends CompositeCommand { - private IslandTeamCommand itc; + private final IslandTeamCommand itc; private @Nullable UUID targetUUID; public IslandTeamCoopCommand(IslandTeamCommand parentCommand) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index 2987453d4..ae8d6d9f4 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -20,7 +20,7 @@ */ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { - private IslandTeamCommand itc; + private final IslandTeamCommand itc; private UUID playerUUID; private UUID prospectiveOwnerUUID; @@ -50,16 +50,17 @@ public boolean canExecute(User user, String label, List args) { user.sendMessage("commands.island.team.invite.errors.invalid-invite"); return false; } - // Check rank to of inviter - Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID); - String inviteUsage = getParent().getSubCommand("invite").map(CompositeCommand::getUsage).orElse(""); - if (island == null || island.getRank(prospectiveOwnerUUID) < island.getRankCommand(inviteUsage)) { - user.sendMessage("commands.island.team.invite.errors.invalid-invite"); - itc.removeInvite(playerUUID); - return false; - } Invite invite = itc.getInvite(playerUUID); if (invite.getType().equals(Type.TEAM)) { + // Check rank to of inviter + Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID); + String inviteUsage = getParent().getSubCommand("invite").map(CompositeCommand::getUsage).orElse(""); + if (island == null || island.getRank(prospectiveOwnerUUID) < island.getRankCommand(inviteUsage)) { + user.sendMessage("commands.island.team.invite.errors.invalid-invite"); + itc.removeInvite(playerUUID); + return false; + } + // Check if player is already in a team if (getIslands().inTeam(getWorld(), playerUUID)) { user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team"); @@ -82,14 +83,10 @@ public boolean execute(User user, String label, List args) { // Get the invite Invite invite = itc.getInvite(playerUUID); switch (invite.getType()) { - case COOP: - askConfirmation(user, () -> acceptCoopInvite(user, invite)); - break; - case TRUST: - askConfirmation(user, () -> acceptTrustInvite(user, invite)); - break; - default: - askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"), () -> acceptTeamInvite(user, invite)); + case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite)); + case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite)); + default -> askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"), + () -> acceptTeamInvite(user, invite)); } return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java index 2de24d487..5fb312096 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java @@ -20,7 +20,7 @@ public class IslandTeamInviteCommand extends CompositeCommand { - private IslandTeamCommand itc; + private final IslandTeamCommand itc; private @Nullable User invitedPlayer; public IslandTeamInviteCommand(IslandTeamCommand parent) { @@ -51,15 +51,9 @@ public boolean canExecute(User user, String label, List args) { Invite invite = itc.getInvite(playerUUID); String name = getPlayers().getName(playerUUID); switch (invite.getType()) { - case COOP: - user.sendMessage("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name); - break; - case TRUST: - user.sendMessage("commands.island.team.invite.name-has-invited-you.trust", TextVariables.NAME, name); - break; - default: - user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name); - break; + case COOP -> user.sendMessage("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name); + case TRUST -> user.sendMessage("commands.island.team.invite.name-has-invited-you.trust", TextVariables.NAME, name); + default -> user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name); } return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteRejectCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteRejectCommand.java index b93224990..963f24065 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteRejectCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteRejectCommand.java @@ -11,7 +11,7 @@ public class IslandTeamInviteRejectCommand extends CompositeCommand { - private IslandTeamCommand itc; + private final IslandTeamCommand itc; public IslandTeamInviteRejectCommand(IslandTeamCommand islandTeamCommand) { super(islandTeamCommand, "reject"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/package-info.java b/src/main/java/world/bentobox/bentobox/api/commands/package-info.java new file mode 100644 index 000000000..7c4212842 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/package-info.java @@ -0,0 +1,18 @@ +/** + * API for BentoBox commands + */ +/** + *

+ * The workhorse class is the abstract class CompositeCommand. It provides all the functionality for + * a command including automatic help, sub-commands, convenience methods, etc. See examples of how to use + * it in the sub-folders admin and island. + *

+ *

+ * The package also includes abstract confirmable command and delayed teleport command classes. These can + * be extended for commands that need them. There is also a default help command. Commands can implement + * their own custom help if required, but most of the time it is not. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.api.commands; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/configuration/Config.java b/src/main/java/world/bentobox/bentobox/api/configuration/Config.java index d4d8ed688..420dd4174 100644 --- a/src/main/java/world/bentobox/bentobox/api/configuration/Config.java +++ b/src/main/java/world/bentobox/bentobox/api/configuration/Config.java @@ -22,8 +22,8 @@ */ public class Config { - private AbstractDatabaseHandler handler; - private Logger logger; + private final AbstractDatabaseHandler handler; + private final Logger logger; private Addon addon; public Config(BentoBox plugin, Class type) { diff --git a/src/main/java/world/bentobox/bentobox/api/events/addon/AddonEvent.java b/src/main/java/world/bentobox/bentobox/api/events/addon/AddonEvent.java index 76c8dfaec..78d1cd277 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/addon/AddonEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/addon/AddonEvent.java @@ -100,29 +100,21 @@ public AddonEventBuilder reason(Reason reason) { } private AddonBaseEvent getDeprecatedEvent() { - switch (reason) { - case ENABLE: - return new AddonEnableEvent(addon, keyValues); - case DISABLE: - return new AddonDisableEvent(addon, keyValues); - case LOAD: - return new AddonLoadEvent(addon, keyValues); - default: - return new AddonGeneralEvent(addon, keyValues); - } + return switch (reason) { + case ENABLE -> new AddonEnableEvent(addon, keyValues); + case DISABLE -> new AddonDisableEvent(addon, keyValues); + case LOAD -> new AddonLoadEvent(addon, keyValues); + default -> new AddonGeneralEvent(addon, keyValues); + }; } private AddonBaseEvent getEvent() { - switch (reason) { - case ENABLE: - return new world.bentobox.bentobox.api.events.addon.AddonEnableEvent(addon, keyValues); - case DISABLE: - return new world.bentobox.bentobox.api.events.addon.AddonDisableEvent(addon, keyValues); - case LOAD: - return new world.bentobox.bentobox.api.events.addon.AddonLoadEvent(addon, keyValues); - default: - return new world.bentobox.bentobox.api.events.addon.AddonGeneralEvent(addon, keyValues); - } + return switch (reason) { + case ENABLE -> new world.bentobox.bentobox.api.events.addon.AddonEnableEvent(addon, keyValues); + case DISABLE -> new world.bentobox.bentobox.api.events.addon.AddonDisableEvent(addon, keyValues); + case LOAD -> new world.bentobox.bentobox.api.events.addon.AddonLoadEvent(addon, keyValues); + default -> new world.bentobox.bentobox.api.events.addon.AddonGeneralEvent(addon, keyValues); + }; } /** diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java index b957b127f..14aad63ff 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java @@ -801,103 +801,58 @@ public IslandEventBuilder rankChange(int oldRank, int newRank){ * @return deprecated event */ private IslandBaseEvent getDeprecatedEvent() { - switch (reason) { - case EXPEL: - return new IslandExpelEvent(island, player, admin, location); - case BAN: - return new IslandBanEvent(island, player, admin, location); - case PRECREATE: - return new IslandPreCreateEvent(player); - case CREATE: - return new IslandCreateEvent(island, player, admin, location, blueprintBundle); - case CREATED: - return new IslandCreatedEvent(island, player, admin, location); - case DELETE: - return new IslandDeleteEvent(island, player, admin, location); - case DELETE_CHUNKS: - return new IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo); - case DELETED: - return new IslandDeletedEvent(island, player, admin, location, deletedIslandInfo); - case ENTER: - return new IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent); - case EXIT: - return new IslandExitEvent(island, player, admin, location, oldIsland, rawEvent); - case LOCK: - return new IslandLockEvent(island, player, admin, location); - case RESET: - return new IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland); - case RESETTED: - return new IslandResettedEvent(island, player, admin, location, oldIsland); - case UNBAN: - return new IslandUnbanEvent(island, player, admin, location); - case UNLOCK: - return new IslandUnlockEvent(island, player, admin, location); - case REGISTERED: - return new IslandRegisteredEvent(island, player, admin, location); - case UNREGISTERED: - return new IslandUnregisteredEvent(island, player, admin, location); - case RANGE_CHANGE: - return new IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange); - case PRECLEAR: - return new IslandPreclearEvent(island, player, admin, location, oldIsland); - case RESERVED: - return new IslandReservedEvent(island, player, admin, location); - case RANK_CHANGE: - return new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); - default: - return new IslandGeneralEvent(island, player, admin, location); - } + return switch (reason) { + case EXPEL -> new IslandExpelEvent(island, player, admin, location); + case BAN -> new IslandBanEvent(island, player, admin, location); + case PRECREATE -> new IslandPreCreateEvent(player); + case CREATE -> new IslandCreateEvent(island, player, admin, location, blueprintBundle); + case CREATED -> new IslandCreatedEvent(island, player, admin, location); + case DELETE -> new IslandDeleteEvent(island, player, admin, location); + case DELETE_CHUNKS -> new IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo); + case DELETED -> new IslandDeletedEvent(island, player, admin, location, deletedIslandInfo); + case ENTER -> new IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent); + case EXIT -> new IslandExitEvent(island, player, admin, location, oldIsland, rawEvent); + case LOCK -> new IslandLockEvent(island, player, admin, location); + case RESET -> new IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland); + case RESETTED -> new IslandResettedEvent(island, player, admin, location, oldIsland); + case UNBAN -> new IslandUnbanEvent(island, player, admin, location); + case UNLOCK -> new IslandUnlockEvent(island, player, admin, location); + case REGISTERED -> new IslandRegisteredEvent(island, player, admin, location); + case UNREGISTERED -> new IslandUnregisteredEvent(island, player, admin, location); + case RANGE_CHANGE -> new IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange); + case PRECLEAR -> new IslandPreclearEvent(island, player, admin, location, oldIsland); + case RESERVED -> new IslandReservedEvent(island, player, admin, location); + case RANK_CHANGE -> new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); + default -> new IslandGeneralEvent(island, player, admin, location); + }; } private IslandBaseEvent getEvent() { - switch (reason) { - case EXPEL: - return new world.bentobox.bentobox.api.events.island.IslandExpelEvent(island, player, admin, location); - case BAN: - return new world.bentobox.bentobox.api.events.island.IslandBanEvent(island, player, admin, location); - case PRECREATE: - return new world.bentobox.bentobox.api.events.island.IslandPreCreateEvent(player); - case CREATE: - return new world.bentobox.bentobox.api.events.island.IslandCreateEvent(island, player, admin, location, blueprintBundle); - case CREATED: - return new world.bentobox.bentobox.api.events.island.IslandCreatedEvent(island, player, admin, location); - case DELETE: - return new world.bentobox.bentobox.api.events.island.IslandDeleteEvent(island, player, admin, location); - case DELETE_CHUNKS: - return new world.bentobox.bentobox.api.events.island.IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo); - case DELETED: - return new world.bentobox.bentobox.api.events.island.IslandDeletedEvent(island, player, admin, location, deletedIslandInfo); - case ENTER: - return new world.bentobox.bentobox.api.events.island.IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent); - case EXIT: - return new world.bentobox.bentobox.api.events.island.IslandExitEvent(island, player, admin, location, oldIsland, rawEvent); - case LOCK: - return new world.bentobox.bentobox.api.events.island.IslandLockEvent(island, player, admin, location); - case RESET: - return new world.bentobox.bentobox.api.events.island.IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland); - case RESETTED: - return new world.bentobox.bentobox.api.events.island.IslandResettedEvent(island, player, admin, location, oldIsland); - case UNBAN: - return new world.bentobox.bentobox.api.events.island.IslandUnbanEvent(island, player, admin, location); - case UNLOCK: - return new world.bentobox.bentobox.api.events.island.IslandUnlockEvent(island, player, admin, location); - case REGISTERED: - return new world.bentobox.bentobox.api.events.island.IslandRegisteredEvent(island, player, admin, location); - case UNREGISTERED: - return new world.bentobox.bentobox.api.events.island.IslandUnregisteredEvent(island, player, admin, location); - case RANGE_CHANGE: - return new world.bentobox.bentobox.api.events.island.IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange); - case PRECLEAR: - return new world.bentobox.bentobox.api.events.island.IslandPreclearEvent(island, player, admin, location, oldIsland); - case RESERVED: - return new world.bentobox.bentobox.api.events.island.IslandReservedEvent(island, player, admin, location); - case RANK_CHANGE: - return new world.bentobox.bentobox.api.events.island.IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); - case NEW_ISLAND: - return new IslandNewIslandEvent(island, player, admin, location); - default: - return new world.bentobox.bentobox.api.events.island.IslandGeneralEvent(island, player, admin, location); - } + return switch (reason) { + case EXPEL -> new world.bentobox.bentobox.api.events.island.IslandExpelEvent(island, player, admin, location); + case BAN -> new world.bentobox.bentobox.api.events.island.IslandBanEvent(island, player, admin, location); + case PRECREATE -> new world.bentobox.bentobox.api.events.island.IslandPreCreateEvent(player); + case CREATE -> new world.bentobox.bentobox.api.events.island.IslandCreateEvent(island, player, admin, location, blueprintBundle); + case CREATED -> new world.bentobox.bentobox.api.events.island.IslandCreatedEvent(island, player, admin, location); + case DELETE -> new world.bentobox.bentobox.api.events.island.IslandDeleteEvent(island, player, admin, location); + case DELETE_CHUNKS -> new world.bentobox.bentobox.api.events.island.IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo); + case DELETED -> new world.bentobox.bentobox.api.events.island.IslandDeletedEvent(island, player, admin, location, deletedIslandInfo); + case ENTER -> new world.bentobox.bentobox.api.events.island.IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent); + case EXIT -> new world.bentobox.bentobox.api.events.island.IslandExitEvent(island, player, admin, location, oldIsland, rawEvent); + case LOCK -> new world.bentobox.bentobox.api.events.island.IslandLockEvent(island, player, admin, location); + case RESET -> new world.bentobox.bentobox.api.events.island.IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland); + case RESETTED -> new world.bentobox.bentobox.api.events.island.IslandResettedEvent(island, player, admin, location, oldIsland); + case UNBAN -> new world.bentobox.bentobox.api.events.island.IslandUnbanEvent(island, player, admin, location); + case UNLOCK -> new world.bentobox.bentobox.api.events.island.IslandUnlockEvent(island, player, admin, location); + case REGISTERED -> new world.bentobox.bentobox.api.events.island.IslandRegisteredEvent(island, player, admin, location); + case UNREGISTERED -> new world.bentobox.bentobox.api.events.island.IslandUnregisteredEvent(island, player, admin, location); + case RANGE_CHANGE -> new world.bentobox.bentobox.api.events.island.IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange); + case PRECLEAR -> new world.bentobox.bentobox.api.events.island.IslandPreclearEvent(island, player, admin, location, oldIsland); + case RESERVED -> new world.bentobox.bentobox.api.events.island.IslandReservedEvent(island, player, admin, location); + case RANK_CHANGE -> new world.bentobox.bentobox.api.events.island.IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); + case NEW_ISLAND -> new IslandNewIslandEvent(island, player, admin, location); + default -> new world.bentobox.bentobox.api.events.island.IslandGeneralEvent(island, player, admin, location); + }; } /** diff --git a/src/main/java/world/bentobox/bentobox/api/events/package-info.java b/src/main/java/world/bentobox/bentobox/api/events/package-info.java new file mode 100644 index 000000000..326a59c8c --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/events/package-info.java @@ -0,0 +1,8 @@ +/** + * API for all the events that BentoBox generates + */ +/** + * @author tastybento + * + */ +package world.bentobox.bentobox.api.events; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/events/team/TeamEvent.java b/src/main/java/world/bentobox/bentobox/api/events/team/TeamEvent.java index 19303c307..94791ef72 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/team/TeamEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/team/TeamEvent.java @@ -237,57 +237,35 @@ public TeamEventBuilder location(Location center) { } private IslandBaseEvent getDeprecatedEvent() { - switch (reason) { - case JOIN: - return new TeamJoinEvent(island, player, admin, location); - case JOINED: - return new TeamJoinedEvent(island, player, admin, location); - case INVITE: - return new TeamInviteEvent(island, player, admin, location); - case LEAVE: - return new TeamLeaveEvent(island, player, admin, location); - case REJECT: - return new TeamRejectEvent(island, player, admin, location); - case KICK: - return new TeamKickEvent(island, player, admin, location); - case SETOWNER: - return new TeamSetownerEvent(island, player, admin, location); - case INFO: - return new TeamInfoEvent(island, player, admin, location); - case DELETE: - return new TeamDeleteEvent(island, player, admin, location); - case UNINVITE: - return new TeamUninviteEvent(island, player, admin, location); - default: - return new TeamGeneralEvent(island, player, admin, location); - } + return switch (reason) { + case JOIN -> new TeamJoinEvent(island, player, admin, location); + case JOINED -> new TeamJoinedEvent(island, player, admin, location); + case INVITE -> new TeamInviteEvent(island, player, admin, location); + case LEAVE -> new TeamLeaveEvent(island, player, admin, location); + case REJECT -> new TeamRejectEvent(island, player, admin, location); + case KICK -> new TeamKickEvent(island, player, admin, location); + case SETOWNER -> new TeamSetownerEvent(island, player, admin, location); + case INFO -> new TeamInfoEvent(island, player, admin, location); + case DELETE -> new TeamDeleteEvent(island, player, admin, location); + case UNINVITE -> new TeamUninviteEvent(island, player, admin, location); + default -> new TeamGeneralEvent(island, player, admin, location); + }; } private IslandBaseEvent getEvent() { - switch (reason) { - case JOIN: - return new world.bentobox.bentobox.api.events.team.TeamJoinEvent(island, player, admin, location); - case JOINED: - return new world.bentobox.bentobox.api.events.team.TeamJoinedEvent(island, player, admin, location); - case INVITE: - return new world.bentobox.bentobox.api.events.team.TeamInviteEvent(island, player, admin, location); - case LEAVE: - return new world.bentobox.bentobox.api.events.team.TeamLeaveEvent(island, player, admin, location); - case REJECT: - return new world.bentobox.bentobox.api.events.team.TeamRejectEvent(island, player, admin, location); - case KICK: - return new world.bentobox.bentobox.api.events.team.TeamKickEvent(island, player, admin, location); - case SETOWNER: - return new world.bentobox.bentobox.api.events.team.TeamSetownerEvent(island, player, admin, location); - case INFO: - return new world.bentobox.bentobox.api.events.team.TeamInfoEvent(island, player, admin, location); - case DELETE: - return new world.bentobox.bentobox.api.events.team.TeamDeleteEvent(island, player, admin, location); - case UNINVITE: - return new world.bentobox.bentobox.api.events.team.TeamUninviteEvent(island, player, admin, location); - default: - return new world.bentobox.bentobox.api.events.team.TeamGeneralEvent(island, player, admin, location); - } + return switch (reason) { + case JOIN -> new world.bentobox.bentobox.api.events.team.TeamJoinEvent(island, player, admin, location); + case JOINED -> new world.bentobox.bentobox.api.events.team.TeamJoinedEvent(island, player, admin, location); + case INVITE -> new world.bentobox.bentobox.api.events.team.TeamInviteEvent(island, player, admin, location); + case LEAVE -> new world.bentobox.bentobox.api.events.team.TeamLeaveEvent(island, player, admin, location); + case REJECT -> new world.bentobox.bentobox.api.events.team.TeamRejectEvent(island, player, admin, location); + case KICK -> new world.bentobox.bentobox.api.events.team.TeamKickEvent(island, player, admin, location); + case SETOWNER -> new world.bentobox.bentobox.api.events.team.TeamSetownerEvent(island, player, admin, location); + case INFO -> new world.bentobox.bentobox.api.events.team.TeamInfoEvent(island, player, admin, location); + case DELETE -> new world.bentobox.bentobox.api.events.team.TeamDeleteEvent(island, player, admin, location); + case UNINVITE -> new world.bentobox.bentobox.api.events.team.TeamUninviteEvent(island, player, admin, location); + default -> new world.bentobox.bentobox.api.events.team.TeamGeneralEvent(island, player, admin, location); + }; } /** diff --git a/src/main/java/world/bentobox/bentobox/api/flags/Flag.java b/src/main/java/world/bentobox/bentobox/api/flags/Flag.java index 754cdf325..ed60c42ff 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/Flag.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/Flag.java @@ -25,6 +25,8 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; +import world.bentobox.bentobox.util.ItemParser; + public class Flag implements Comparable { @@ -51,7 +53,8 @@ public enum Type { */ WORLD_SETTING(Material.GRASS_BLOCK); - private @NonNull Material icon; + private @NonNull + final Material icon; Type(@NonNull Material icon) { this.icon = icon; @@ -91,14 +94,11 @@ public enum Mode { * @return next ranking mode */ public Mode getNext() { - switch(this) { - case ADVANCED: - return EXPERT; - case BASIC: - return ADVANCED; - default: - return BASIC; - } + return switch (this) { + case ADVANCED -> EXPERT; + case BASIC -> ADVANCED; + default -> BASIC; + }; } /** @@ -107,14 +107,11 @@ public Mode getNext() { * @return true if ranked greater */ public boolean isGreaterThan(Mode rank) { - switch(this) { - case EXPERT: - return rank.equals(BASIC) || rank.equals(ADVANCED); - case ADVANCED: - return rank.equals(BASIC); - default: - return false; - } + return switch (this) { + case EXPERT -> rank.equals(BASIC) || rank.equals(ADVANCED); + case ADVANCED -> rank.equals(BASIC); + default -> false; + }; } } @@ -207,12 +204,12 @@ public void setSetting(World world, boolean setting) { // Subflag support if (hasSubflags()) { subflags.stream() - .filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION)) - .forEach(subflag -> BentoBox.getInstance() - .getIWM() - .getWorldSettings(world) - .getWorldFlags() - .put(subflag.getID(), setting)); + .filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION)) + .forEach(subflag -> BentoBox.getInstance() + .getIWM() + .getWorldSettings(world) + .getWorldFlags() + .put(subflag.getID(), setting)); } // Save config file @@ -222,7 +219,7 @@ public void setSetting(World world, boolean setting) { /** * Set the original status of this flag for locations outside of island spaces. - * May be overriden by the the setting for this world. + * May be overridden by the setting for this world. * Does not affect subflags. * @param defaultSetting - true means it is allowed. false means it is not allowed */ @@ -300,10 +297,9 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof Flag)) { + if (!(obj instanceof Flag other)) { return false; } - Flag other = (Flag) obj; if (id == null) { if (other.id != null) { return false; @@ -322,6 +318,13 @@ public String getNameReference() { return PROTECTION_FLAGS + this.id + ".name"; } + /** + * @return a locale reference for the icon of this protection flag + */ + public String getIconReference() { + return PROTECTION_FLAGS + this.id + ".icon"; + } + /** * @return a locale reference for the description of this protection flag */ @@ -384,7 +387,7 @@ public PanelItem toPanelItem(BentoBox plugin, User user, @Nullable Island island } // Start the flag conversion PanelItemBuilder pib = new PanelItemBuilder() - .icon(new ItemStack(icon)) + .icon(ItemParser.parse(user.getTranslationOrNothing(this.getIconReference()), new ItemStack(icon))) .name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference()))) .clickHandler(clickHandler) .invisible(invisible); @@ -392,16 +395,11 @@ public PanelItem toPanelItem(BentoBox plugin, User user, @Nullable Island island pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference()))); return pib.build(); } - switch(getType()) { - case PROTECTION: - return createProtectionFlag(plugin, user, island, pib).build(); - case SETTING: - return createSettingFlag(user, island, pib).build(); - case WORLD_SETTING: - return createWorldSettingFlag(user, pib).build(); - default: - return pib.build(); - } + return switch (getType()) { + case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build(); + case SETTING -> createSettingFlag(user, island, pib).build(); + case WORLD_SETTING -> createWorldSettingFlag(user, pib).build(); + }; } private PanelItemBuilder createWorldSettingFlag(User user, PanelItemBuilder pib) { @@ -484,8 +482,8 @@ public int compareTo(Flag o) { */ public static class Builder { // Mandatory fields - private String id; - private Material icon; + private final String id; + private final Material icon; // Listener private Listener listener; @@ -514,7 +512,7 @@ public static class Builder { private Mode mode = Mode.EXPERT; // Subflags - private Set subflags; + private final Set subflags; /** * Builder for making flags @@ -638,7 +636,7 @@ public Builder mode(Mode mode) { * Take extra care to ensure that subflags have the same number of possible values as the parent flag. * @param flags all Flags that are subflags * @return Builder - flag builder - * @since 1.17.0 + * @since 1.17.1 */ public Builder subflags(Flag... flags) { this.subflags.addAll(Arrays.asList(flags)); @@ -653,17 +651,9 @@ public Flag build() { // If no clickHandler has been set, then apply default ones if (clickHandler == null) { switch (type) { - case SETTING: - clickHandler = new IslandToggleClick(id); - break; - case WORLD_SETTING: - clickHandler = new WorldToggleClick(id); - break; - case PROTECTION: - // Default option - default: - clickHandler = new CycleClick(id); - break; + case SETTING -> clickHandler = new IslandToggleClick(id); + case WORLD_SETTING -> clickHandler = new WorldToggleClick(id); + default -> clickHandler = new CycleClick(id); } } diff --git a/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java b/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java index 20b27b4fc..dc62e87b4 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java @@ -4,6 +4,7 @@ import java.util.UUID; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; @@ -18,6 +19,7 @@ import world.bentobox.bentobox.api.metadata.MetaDataValue; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.util.Util; @@ -253,4 +255,22 @@ protected IslandsManager getIslands() { protected IslandWorldManager getIWM() { return plugin.getIWM(); } + + /** + * Check if PVP is allowed here or not + * @param location location where action is taking + * @return true if PVP is allowed, false if not + */ + protected boolean PVPAllowed(Location location) { + return plugin.getIslands().getIslandAt(location).map(i -> i.isAllowed(this.getFlag(location.getWorld()))).orElse(false); + } + + protected Flag getFlag(World w) { + return switch (w.getEnvironment()) { + case NETHER -> Flags.PVP_NETHER; + case THE_END -> Flags.PVP_END; + default -> Flags.PVP_OVERWORLD; + }; + } + } diff --git a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java index 2afcd6b96..ddb5d2bd3 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java @@ -23,8 +23,8 @@ */ public class IslandToggleClick implements ClickHandler { - private BentoBox plugin = BentoBox.getInstance(); - private String id; + private final BentoBox plugin = BentoBox.getInstance(); + private final String id; /** * @param id - the flag ID that this click listener is associated with diff --git a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/package-info.java b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/package-info.java new file mode 100644 index 000000000..bf34c7ff9 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/package-info.java @@ -0,0 +1,12 @@ +/** + * This package contains click listener classes used when clicking on icons in settings + * + *

+ * CycleClick will cycle through different settings. IslandLock is specific to locking or unlocking an island. + * IslandToggleClick is a toggle-based selection. WorldToggleClick toggles world settings. + */ +/** + * @author tastybento + * + */ +package world.bentobox.bentobox.api.flags.clicklisteners; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/flags/package-info.java b/src/main/java/world/bentobox/bentobox/api/flags/package-info.java new file mode 100644 index 000000000..ca61498db --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/flags/package-info.java @@ -0,0 +1,16 @@ +/** + * This package contains the API for protection and settings flags + * + *

+ * New flags should use the Flag.Builder to create a flag. Listeners for flag related events should + * extend the abstract FlagListener class. + *

+ *

+ * Click listeners are different types of listeners specific to clicking on the settings UI in which the + * flags are shown. + *

+ * + * @author tastybento + * + */ +package world.bentobox.bentobox.api.flags; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/hooks/package-info.java b/src/main/java/world/bentobox/bentobox/api/hooks/package-info.java new file mode 100644 index 000000000..d23024e38 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/hooks/package-info.java @@ -0,0 +1,12 @@ +/** + * Provides API to enable BentoBox to hook into other plugins. + * + *

+ * This is used to hook into plugins like Placeholder API + *

+ */ +/** + * @author Poslovich + * + */ +package world.bentobox.bentobox.api.hooks; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/localization/BentoBoxLocale.java b/src/main/java/world/bentobox/bentobox/api/localization/BentoBoxLocale.java index c6c05e8d0..4d9a0674a 100644 --- a/src/main/java/world/bentobox/bentobox/api/localization/BentoBoxLocale.java +++ b/src/main/java/world/bentobox/bentobox/api/localization/BentoBoxLocale.java @@ -20,16 +20,16 @@ public class BentoBoxLocale { private static final String UNKNOWN = "unknown"; - private Locale locale; - private YamlConfiguration config; - private ItemStack banner; - private List authors; + private final Locale locale; + private final YamlConfiguration config; + private final ItemStack banner; + private final List authors; /** * List of available prefixes in this locale. * @since 1.12.0 */ - private Set prefixes; + private final Set prefixes; public BentoBoxLocale(Locale locale, YamlConfiguration config) { this.locale = locale; diff --git a/src/main/java/world/bentobox/bentobox/api/localization/package-info.java b/src/main/java/world/bentobox/bentobox/api/localization/package-info.java new file mode 100644 index 000000000..72ed5c7bd --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/localization/package-info.java @@ -0,0 +1,11 @@ +/** + * API for localization. + * + *

+ * BentoBoxLocale holds all the information required to specify a locale in BentoBox. + * TextVariables contains a static constants that are used as place holders in user messaging. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.api.localization; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java b/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java index 6112b8a57..583bd24a9 100644 --- a/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java +++ b/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java @@ -47,7 +47,7 @@ public Map getData() { public static class Builder { private long timestamp; - private String type; + private final String type; private Map data; public Builder(@NonNull String type) { diff --git a/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataAble.java b/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataAble.java index 6e706505b..5233d0d23 100644 --- a/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataAble.java +++ b/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataAble.java @@ -13,13 +13,13 @@ public interface MetaDataAble { /** * @return the metaData */ - public Optional> getMetaData(); + Optional> getMetaData(); /** * @param metaData the metaData to set * @since 1.15.4 */ - public void setMetaData(Map metaData); + void setMetaData(Map metaData); /** * Get meta data by key diff --git a/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataValue.java b/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataValue.java index 4ae5c6d14..ea6d7c0a9 100644 --- a/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataValue.java +++ b/src/main/java/world/bentobox/bentobox/api/metadata/MetaDataValue.java @@ -29,7 +29,7 @@ public class MetaDataValue { @Expose private Boolean booleanValue; @Expose - private @NonNull String stringValue; + private String stringValue; /** * Initialize this meta data value @@ -85,6 +85,6 @@ public boolean asBoolean() { @NonNull public String asString() { - return stringValue; + return stringValue == null ? "" : stringValue; } } diff --git a/src/main/java/world/bentobox/bentobox/api/metadata/package-info.java b/src/main/java/world/bentobox/bentobox/api/metadata/package-info.java new file mode 100644 index 000000000..15517d0d0 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/metadata/package-info.java @@ -0,0 +1,11 @@ +/** + * Metadata API for storing data in BentoBox database objects + *

+ * This API draws on the same concepts as Bukkit's meta data API. + * Classes capable of storing meta data will implement the MetaDataAble interface. + * MetaDataValue provides the actual object in which data is stored. +/*

+ * @author tastybento + * @since 1.15.4 + */ +package world.bentobox.bentobox.api.metadata; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/panels/Panel.java b/src/main/java/world/bentobox/bentobox/api/panels/Panel.java index 39d330465..9691371ad 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/Panel.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/Panel.java @@ -80,15 +80,9 @@ protected void makePanel(String name, Map items, int size, U // Create panel switch (type) { - case INVENTORY: - inventory = Bukkit.createInventory(null, fixSize(size), name); - break; - case HOPPER: - inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name); - break; - case DROPPER: - inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name); - break; + case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name); + case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name); + case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name); } // Fill the inventory and return diff --git a/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java b/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java index 1fe59e9ff..e347f200a 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java @@ -29,7 +29,7 @@ public static PanelItem empty() { private String name; private boolean glow; private ItemMeta meta; - private String playerHeadName; + private final String playerHeadName; private boolean invisible; public PanelItem(PanelItemBuilder builtItem) { diff --git a/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java b/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java index d9527a0f7..9ff12d57c 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java @@ -31,7 +31,8 @@ public class TabbedPanel extends Panel implements PanelListener { private static final String PROTECTION_PANEL = "protection.panel."; private static final long ITEMS_PER_PAGE = 36; private final TabbedPanelBuilder tpb; - private @NonNull BentoBox plugin = BentoBox.getInstance(); + private @NonNull + final BentoBox plugin = BentoBox.getInstance(); private int activeTab; private int activePage; private boolean closed; @@ -146,7 +147,7 @@ private void setupHeader(Tab tab, TreeMap items) { } } // Add any subsidiary icons - tab.getTabIcons().forEach(items::put); + items.putAll(tab.getTabIcons()); } private void setupFooter(TreeMap items) { diff --git a/src/main/java/world/bentobox/bentobox/api/panels/TemplatedPanel.java b/src/main/java/world/bentobox/bentobox/api/panels/TemplatedPanel.java new file mode 100644 index 000000000..279d51af3 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/panels/TemplatedPanel.java @@ -0,0 +1,527 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.bentobox.api.panels; + + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.util.*; +import java.util.function.BiFunction; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord; +import world.bentobox.bentobox.api.user.User; + + +/** + * This class creates a new Panel from the template record. + * @author BONNe + * @since 1.17.3 + */ +public class TemplatedPanel extends Panel +{ + /** + * TemplatedPanel constructor class which generates functional panel. + * @param builder Builder that contains all information about the panel that must be generated. + */ + public TemplatedPanel(@NonNull TemplatedPanelBuilder builder) + { + this.user = builder.getUser(); + this.setWorld(builder.getWorld()); + this.setListener(builder.getListener()); + + this.panelTemplate = builder.getPanelTemplate(); + // Init type creators + this.typeCreators = new HashMap<>(builder.getObjectCreatorMap()); + this.typeIndex = new HashMap<>(builder.getObjectCreatorMap().size()); + this.typeSlotMap = new HashMap<>(builder.getObjectCreatorMap().size()); + + if (this.panelTemplate == null) + { + BentoBox.getInstance().logError("Cannot generate panel because template is not loaded."); + } + else + { + this.generatePanel(); + } + } + + + /** + * This method generates the panel from the template. + */ + private void generatePanel() + { + Map items = switch (this.panelTemplate.type()) + { + case INVENTORY -> this.populateInventoryPanel(); + case HOPPER -> this.populateHopperPanel(); + case DROPPER -> this.populateDropperPanel(); + }; + + super.makePanel(this.user.getTranslation(this.panelTemplate.title()), + items, + items.keySet().stream().max(Comparator.naturalOrder()).orElse(9), + this.user, + this.getListener().orElse(null), + this.panelTemplate.type()); + } + + + /** + * This method creates map with item indexes and their icons that will be added into + * Inventory Panel. + * @return Map that contains indexes linked to the correct panel item. + */ + @NonNull + private Map populateInventoryPanel() + { + // Init item array with the max available size. + PanelItem[][] itemArray = new PanelItem[6][9]; + + // Analyze the GUI button layout a bit. + for (int i = 0; i < this.panelTemplate.content().length; i++) + { + for (int k = 0; k < this.panelTemplate.content()[i].length; k++) + { + ItemTemplateRecord record = this.panelTemplate.content()[i][k]; + + if (record != null && record.dataMap().containsKey("type")) + { + String type = String.valueOf(record.dataMap().get("type")); + + int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1); + this.typeSlotMap.put(type, counter + 1); + } + } + } + + // Make buttons for the GUI + for (int i = 0; i < this.panelTemplate.content().length; i++) + { + for (int k = 0; k < this.panelTemplate.content()[i].length; k++) + { + itemArray[i][k] = this.makeButton(this.panelTemplate.content()[i][k]); + } + } + + // After items are created, remove empty lines. + boolean[] showLine = this.panelTemplate.forcedRows(); + + for (int i = 0; i < this.panelTemplate.content().length; i++) + { + boolean emptyLine = true; + + for (int k = 0; emptyLine && k < this.panelTemplate.content()[i].length; k++) + { + emptyLine = itemArray[i][k] == null; + } + + // Do not generate fallback for "empty" lines. + showLine[i] = showLine[i] || !emptyLine; + } + + // Now fill the border. + if (this.panelTemplate.border() != null) + { + PanelItem template = this.makeTemplate(this.panelTemplate.border()); + + // Hard codded 6 + for (int i = 0; i < 6; i++) + { + if (i == 0 || i == 5) + { + // Fill first and last row completely with border. + for (int k = 0; k < 9; k++) + { + if (itemArray[i][k] == null) + { + itemArray[i][k] = template; + } + } + } + else + { + // Fill first and last element in row with border. + if (itemArray[i][0] == null) + { + itemArray[i][0] = template; + } + + if (itemArray[i][8] == null) + { + itemArray[i][8] = template; + } + } + } + + showLine[0] = true; + showLine[5] = true; + } + + // Now fill the background. + if (this.panelTemplate.background() != null) + { + PanelItem template = this.makeTemplate(this.panelTemplate.background()); + + for (int i = 0; i < 6; i++) + { + for (int k = 0; k < 9; k++) + { + if (itemArray[i][k] == null) + { + itemArray[i][k] = template; + } + } + } + } + + // Now place all panel items with their indexes into item map. + Map itemMap = new HashMap<>(6 * 9); + + int correctIndex = 0; + + for (int i = 0; i < itemArray.length; i++) + { + final boolean iterate = showLine[i]; + + for (int k = 0; iterate && k < itemArray[i].length; k++) + { + if (itemArray[i][k] != null) + { + itemMap.put(correctIndex, itemArray[i][k]); + } + + correctIndex++; + } + } + + return itemMap; + } + + + /** + * This method creates map with item indexes and their icons that will be added into + * hopper Panel. + * @return Map that contains indexes linked to the correct panel item. + */ + @NonNull + private Map populateHopperPanel() + { + // Init item array with the max available size. + PanelItem[] itemArray = new PanelItem[5]; + + // Analyze the template + for (int i = 0; i < 5; i++) + { + ItemTemplateRecord record = this.panelTemplate.content()[0][i]; + + if (record != null && record.dataMap().containsKey("type")) + { + String type = String.valueOf(record.dataMap().get("type")); + + int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1); + this.typeSlotMap.put(type, counter + 1); + } + } + + // Make buttons + for (int i = 0; i < 5; i++) + { + itemArray[i] = this.makeButton(this.panelTemplate.content()[0][i]); + } + + // Now fill the background. + if (this.panelTemplate.background() != null) + { + PanelItem template = this.makeTemplate(this.panelTemplate.background()); + + for (int i = 0; i < 5; i++) + { + if (itemArray[i] == null) + { + itemArray[i] = template; + } + } + } + + // Now place all panel items with their indexes into item map. + Map itemMap = new HashMap<>(5); + + int correctIndex = 0; + + for (PanelItem panelItem : itemArray) + { + if (panelItem != null) + { + itemMap.put(correctIndex, panelItem); + } + + correctIndex++; + } + + return itemMap; + } + + + /** + * This method creates map with item indexes and their icons that will be added into + * dropper Panel. + * @return Map that contains indexes linked to the correct panel item. + */ + @NonNull + private Map populateDropperPanel() + { + // Analyze the template + for (int i = 0; i < 3; i++) + { + for (int k = 0; k < 3; k++) + { + ItemTemplateRecord record = this.panelTemplate.content()[i][k]; + + if (record != null && record.dataMap().containsKey("type")) + { + String type = String.valueOf(record.dataMap().get("type")); + + int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1); + this.typeSlotMap.put(type, counter + 1); + } + } + } + + // Init item array with the max available size. + PanelItem[][] itemArray = new PanelItem[3][3]; + + // Make buttons + for (int i = 0; i < 3; i++) + { + for (int k = 0; k < 3; k++) + { + itemArray[i][k] = this.makeButton(this.panelTemplate.content()[i][k]); + } + } + + // Now fill the background. + if (this.panelTemplate.background() != null) + { + PanelItem template = this.makeTemplate(this.panelTemplate.background()); + + for (int i = 0; i < 3; i++) + { + for (int k = 0; k < 3; k++) + { + if (itemArray[i][k] == null) + { + itemArray[i][k] = template; + } + } + } + } + + // Init item map with the max available size. + Map itemMap = new HashMap<>(9); + + int correctIndex = 0; + + for (int i = 0; i < 3; i++) + { + for (int k = 0; k < 3; k++) + { + if (itemArray[i][k] != null) + { + itemMap.put(correctIndex, itemArray[i][k]); + } + + correctIndex++; + } + } + + return itemMap; + } + + + /** + * This method passes button creation from given record template. + * @param record Template of the button that must be created. + * @return PanelItem of the template, otherwise null. + */ + @Nullable + private PanelItem makeButton(@Nullable ItemTemplateRecord record) + { + if (record == null) + { + // Immediate exit if record is null. + return null; + } + + if (record.dataMap().containsKey("type")) + { + // If dataMap is not null, and it is not empty, then pass button to the object creator function. + + return this.makeAddonButton(record); + } + else + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(); + + if (record.icon() != null) + { + itemBuilder.icon(record.icon().clone()); + } + + if (record.title() != null) + { + itemBuilder.name(this.user.getTranslation(record.title())); + } + + if (record.description() != null) + { + itemBuilder.description(this.user.getTranslation(record.description())); + } + + // If there are generic click handlers that could be added, then this is a place + // where to process them. + + // Click Handlers are managed by custom addon buttons. + return itemBuilder.build(); + } + } + + + /** + * This method passes button to the type creator, if that exists. + * @param record Template of the button that must be created. + * @return PanelItem of the button created by typeCreator, otherwise null. + */ + @Nullable + private PanelItem makeAddonButton(@NonNull ItemTemplateRecord record) + { + // Get object type. + String type = String.valueOf(record.dataMap().getOrDefault("type", "")); + + if (!this.typeCreators.containsKey(type)) + { + // There are no object with a given type. + return this.makeFallBack(record.fallback()); + } + + BiFunction buttonBuilder = this.typeCreators.get(type); + + // Get next slot index. + ItemSlot itemSlot = this.typeIndex.containsKey(type) ? + this.typeIndex.get(type) : + new ItemSlot(0, this.typeSlotMap); + this.typeIndex.put(type, itemSlot.nextItemSlot()); + + // Try to get next object. + PanelItem item = buttonBuilder.apply(record, itemSlot); + return item == null ? this.makeFallBack(record.fallback()) : item; + } + + + /** + * This method creates a fall back button for given record. + * @param record Record which fallback must be created. + * @return PanelItem if fallback was creates successfully, otherwise null. + */ + @Nullable + private PanelItem makeFallBack(@Nullable ItemTemplateRecord record) + { + return record == null ? null : this.makeButton(record.fallback()); + } + + + /** + * This method translates template record into a panel item. + * @param record Record that must be translated. + * @return PanelItem that contains all information from the record. + */ + private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem record) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(); + + // Read icon only if it is not null. + if (record.icon() != null) + { + itemBuilder.icon(record.icon().clone()); + } + + // Read title only if it is not null. + if (record.title() != null) + { + itemBuilder.name(this.user.getTranslation(record.title())); + } + + // Read description only if it is not null. + if (record.description() != null) + { + itemBuilder.description(this.user.getTranslation(record.description())); + } + + // Click Handlers are managed by custom addon buttons. + return itemBuilder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Classes +// --------------------------------------------------------------------- + + + /** + * This record contains current slot object and map that links types with a number of slots in + * panel with it. + * Some buttons need information about all types, like previous/next. + * @param slot Index of object in current panel. + * @param amountMap Map that links types with number of objects in panel. + */ + public record ItemSlot(int slot, Map amountMap) + { + /** + * This method returns new record object with iterative slot index. + * @return New ItemSlot object that has increased slot index by 1. + */ + ItemSlot nextItemSlot() + { + return new ItemSlot(this.slot() + 1, this.amountMap()); + } + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * The GUI template record. + */ + private final PanelTemplateRecord panelTemplate; + + /** + * The user who opens the GUI. + */ + private final User user; + + /** + * This map links custom types with their info object. + */ + private final Map> typeCreators; + + /** + * Stores the item slot information for each type. + */ + private final Map typeIndex; + + /** + * Stores the number of items with given type in whole panel. + */ + private final Map typeSlotMap; +} diff --git a/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelBuilder.java b/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelBuilder.java index 0aef22784..608a185c4 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelBuilder.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelBuilder.java @@ -3,13 +3,14 @@ import java.util.SortedMap; import java.util.TreeMap; -import org.bukkit.ChatColor; import org.bukkit.World; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelListener; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; + /** * Builds panels @@ -26,7 +27,7 @@ public class PanelBuilder { private World world; public PanelBuilder name(String name) { - this.name = ChatColor.translateAlternateColorCodes('&', name); + this.name = Util.translateColorCodes(name); return this; } diff --git a/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelItemBuilder.java b/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelItemBuilder.java index cdcd7b65a..c3c10317b 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelItemBuilder.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/builders/PanelItemBuilder.java @@ -5,13 +5,14 @@ import java.util.Collections; import java.util.List; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler; +import world.bentobox.bentobox.util.Util; + public class PanelItemBuilder { private ItemStack icon = new ItemStack(Material.AIR); @@ -59,7 +60,7 @@ public PanelItemBuilder icon(String playerName) { } public PanelItemBuilder name(@Nullable String name) { - this.name = name != null ? ChatColor.translateAlternateColorCodes('&', name) : null; + this.name = name != null ? Util.translateColorCodes(name) : null; return this; } diff --git a/src/main/java/world/bentobox/bentobox/api/panels/builders/TemplatedPanelBuilder.java b/src/main/java/world/bentobox/bentobox/api/panels/builders/TemplatedPanelBuilder.java new file mode 100644 index 000000000..fa8ea51e3 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/panels/builders/TemplatedPanelBuilder.java @@ -0,0 +1,203 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.bentobox.api.panels.builders; + + +import org.bukkit.World; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.PanelListener; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord; +import world.bentobox.bentobox.api.panels.reader.TemplateReader; +import world.bentobox.bentobox.api.user.User; + + +/** + * Builds {@link TemplatedPanel}'s + * @author BONNe + * @since 1.17.3 + */ +public class TemplatedPanelBuilder +{ +// --------------------------------------------------------------------- +// Section: Builder +// --------------------------------------------------------------------- + + /** + * Adds the template that must be loaded for Template panel builder. + * + * @param guiName the gui name + * @param dataFolder the data folder + * @return the template panel builder + */ + public TemplatedPanelBuilder template(String guiName, File dataFolder) + { + this.panelTemplate = TemplateReader.readTemplatePanel(guiName, dataFolder); + return this; + } + + + /** + * Adds the user for template panel builder. + * + * @param user the user + * @return the template panel builder + */ + public TemplatedPanelBuilder user(User user) + { + this.user = user; + return this; + } + + + /** + * Adds the world for template panel builder. + * + * @param world the world + * @return the template panel builder + */ + public TemplatedPanelBuilder world(World world) + { + this.world = world; + return this; + } + + + /** + * Adds the panel listener for template panel builder. + * + * @param listener the listener + * @return the template panel builder + */ + public TemplatedPanelBuilder listener(PanelListener listener) + { + this.listener = listener; + return this; + } + + + /** + * Registers new button type builder for template panel builder. + * + * @param type the type + * @param buttonCreator the button creator + * @return the template panel builder + */ + public TemplatedPanelBuilder registerTypeBuilder(String type, BiFunction buttonCreator) + { + this.objectCreatorMap.put(type, buttonCreator); + return this; + } + + + /** + * Build templated panel. + * + * @return the templated panel + */ + public TemplatedPanel build() + { + return new TemplatedPanel(this); + } + + +// --------------------------------------------------------------------- +// Section: Getters +// --------------------------------------------------------------------- + + + /** + * Gets panel template. + * + * @return the panel template + */ + public PanelTemplateRecord getPanelTemplate() + { + return this.panelTemplate; + } + + + /** + * Gets user. + * + * @return the user + */ + public User getUser() + { + return this.user; + } + + + /** + * Gets world. + * + * @return the world + */ + public World getWorld() + { + return this.world; + } + + + /** + * Gets listener. + * + * @return the listener + */ + public PanelListener getListener() + { + return this.listener; + } + + + /** + * Gets object creator map. + * + * @return the object creator map + */ + public Map> getObjectCreatorMap() + { + return this.objectCreatorMap; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * The GUI template record. + */ + private PanelTemplateRecord panelTemplate; + + /** + * The user who opens the GUI. + */ + private User user; + + /** + * The world where GUI operates. + */ + private World world; + + /** + * Panel Listener + */ + private PanelListener listener; + + /** + * Map that links objects with their panel item creators. + */ + private final Map> objectCreatorMap = new HashMap<>(); +} diff --git a/src/main/java/world/bentobox/bentobox/api/panels/package-info.java b/src/main/java/world/bentobox/bentobox/api/panels/package-info.java new file mode 100644 index 000000000..3cc88a0c1 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/panels/package-info.java @@ -0,0 +1,21 @@ +/** + * API for GUI panel creation and usage. + * + *

+ * BentoBox provides an API that enables Addon developers to display a GUI. + * There is the basic Panel and the more advanced TabbedPanel. Both use Builders + * to make them. For examples, look at the Settings classes. + *

+ *

+ * Clicking on a panel item is handled by classes that implement the PanelListener interface. + * When a click is made, the listener is called with various parameters available. It is possible + * to have an overall listener for the whole panel and also individual listeners just for the + * icon that was clicked. + *

+ * The tabbed panel contains Tabs that are accessible via icons at the top of the panel. + *

+ * + * @author tastybento + * @since 1.7.0 + */ +package world.bentobox.bentobox.api.panels; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/panels/reader/ItemTemplateRecord.java b/src/main/java/world/bentobox/bentobox/api/panels/reader/ItemTemplateRecord.java new file mode 100644 index 000000000..fe8e39974 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/panels/reader/ItemTemplateRecord.java @@ -0,0 +1,89 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.bentobox.api.panels.reader; + + +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * This Record contains all necessary information about Item Template that can be used to craft panel item. + * + * @param icon ItemStack of the Item + * @param title Title of the item + * @param description Lore message of the item + * @param actions List of Actions for a button + * @param dataMap DataMap that links additional objects for a button. + * @param fallback FallBack item if current one is not possible to generate. + * + * @since 1.17.3 + */ +public record ItemTemplateRecord(@Nullable ItemStack icon, + @Nullable String title, + @Nullable String description, + @NonNull List actions, + @NonNull Map dataMap, + @Nullable ItemTemplateRecord fallback) +{ + /** + * Instantiates a new Item template record without actions and data map. + * + * @param icon the icon + * @param title the title + * @param description the description + * @param fallback the fallback + */ + public ItemTemplateRecord(ItemStack icon, String title, String description, ItemTemplateRecord fallback) + { + this(icon, title, description, new ArrayList<>(6), new HashMap<>(0), fallback); + } + + + /** + * This method adds given object associated with key into data map. + * @param key Key value of object. + * @param data Data that is associated with a key. + */ + public void addData(String key, Object data) + { + this.dataMap.put(key, data); + } + + + /** + * Add action to the actions list. + * + * @param actionData the action data + */ + public void addAction(ActionRecords actionData) + { + this.actions.add(actionData); + } + + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * The Action Records holds data about each action. + * + * @param clickType the click type + * @param actionType the string that represents action type + * @param content the content of the action + * @param tooltip the tooltip of action + */ + public record ActionRecords(ClickType clickType, String actionType, String content, String tooltip) {} +} diff --git a/src/main/java/world/bentobox/bentobox/api/panels/reader/PanelTemplateRecord.java b/src/main/java/world/bentobox/bentobox/api/panels/reader/PanelTemplateRecord.java new file mode 100644 index 000000000..f2c48dbf5 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/panels/reader/PanelTemplateRecord.java @@ -0,0 +1,79 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.bentobox.api.panels.reader; + + +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.api.panels.Panel; + + +/** + * This is template object for the panel reader. It contains data that can exist in the panel. + * PanelBuilder will use this to build panel. + * + * @param type the type of GUI + * @param title the title of GUI + * @param border the border block for GUI + * @param background the background block for GUI. + * @param forcedRows the array of boolean that indicate which rows must be force loaded. + * @param content The 2D array of ItemTemplateRecords + * + * @since 1.17.3 + */ +public record PanelTemplateRecord(Panel.Type type, + @Nullable String title, + @Nullable TemplateItem border, + @Nullable TemplateItem background, + boolean[] forcedRows, + @NonNull ItemTemplateRecord[][] content) +{ + /** + * Instantiates a new Panel template record with empty content. + * + * @param type the type + * @param title the title + * @param border the border + * @param background the background + * @param forcedRows the forced rows array + */ + public PanelTemplateRecord(Panel.Type type, String title, TemplateItem border, TemplateItem background, boolean[] forcedRows) + { + this(type, title, border, background, forcedRows, new ItemTemplateRecord[6][9]); + } + + + /** + * This method adds give item template record in given slot. + * @param rowIndex row index of content array + * @param columnIndex column index of content array. + * @param panelItemTemplate item template record that must be added. + */ + public void addButtonTemplate(int rowIndex, int columnIndex, ItemTemplateRecord panelItemTemplate) + { + this.content[rowIndex][columnIndex] = panelItemTemplate; + } + + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This record contains info about border and background item. + */ + public record TemplateItem(ItemStack icon, String title, String description) + { + public TemplateItem(ItemStack icon) + { + this(icon, null, null); + } + } +} diff --git a/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java b/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java new file mode 100644 index 000000000..2213cad90 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java @@ -0,0 +1,364 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.bentobox.api.panels.reader; + + +import com.google.common.base.Enums; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.inventory.ClickType; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import world.bentobox.bentobox.api.panels.Panel; +import world.bentobox.bentobox.util.ItemParser; + + +/** + * This class manages Template file reading, creating PanelTemplateRecord object and storing it internally. + * This class just reads and returns given panel template. It does not create a functional panel. + * + * @since 1.17.3 + */ +public class TemplateReader +{ + /** + * Read template panel panel template record. + * + * @param panelName the panel name + * @param panelLocation the panel location directory + * @return the panel template record + */ + public static PanelTemplateRecord readTemplatePanel(@NonNull String panelName, @NonNull File panelLocation) + { + if (!panelLocation.exists()) + { + // Return null because folder does not exist. + return null; + } + + File file = new File(panelLocation, panelName.endsWith(".yml") ? panelName : panelName + ".yml"); + + if (!file.exists()) + { + // Return as file does not exist. + return null; + } + + // Check if panel is already crafted. + if (TemplateReader.loadedPanels.containsKey(file.getAbsolutePath())) + { + return TemplateReader.loadedPanels.get(file.getAbsolutePath()); + } + + PanelTemplateRecord record; + + try + { + // Load config + YamlConfiguration config = new YamlConfiguration(); + config.load(file); + // Read panel + record = readPanelTemplate(config.getConfigurationSection(panelName)); + // Put panel into memory + TemplateReader.loadedPanels.put(file.getAbsolutePath(), record); + } + catch (IOException | InvalidConfigurationException e) + { + record = null; + } + + return record; + } + + + /** + * This method reads panel template from given configuration section. + * @param configurationSection Section that contains panel template data. + * @return Panel Template. + */ + private static PanelTemplateRecord readPanelTemplate(@Nullable ConfigurationSection configurationSection) + { + if (configurationSection == null) + { + // No data to return. + return null; + } + + String title = configurationSection.getString("title"); + Panel.Type type = + Enums.getIfPresent(Panel.Type.class, configurationSection.getString("type", "INVENTORY")). + or(Panel.Type.INVENTORY); + + PanelTemplateRecord.TemplateItem borderItem = null; + + // Read Border Icon. + if (configurationSection.isConfigurationSection("border")) + { + // Process border icon if it contains more options. + ConfigurationSection borderSection = configurationSection.getConfigurationSection("border"); + + if (borderSection != null) + { + borderItem = new PanelTemplateRecord.TemplateItem( + ItemParser.parse((borderSection.getString("icon", Material.AIR.name()))), + borderSection.getString("title", null), + borderSection.getString("description", null)); + } + } + else if (configurationSection.isString("border")) + { + // Process border icon if it contains only icon. + + borderItem = new PanelTemplateRecord.TemplateItem( + ItemParser.parse((configurationSection.getString("border", Material.AIR.name())))); + } + + PanelTemplateRecord.TemplateItem backgroundItem = null; + + // Read Background block + if (configurationSection.isConfigurationSection("background")) + { + // Process border icon if it contains more options. + ConfigurationSection backgroundSection = configurationSection.getConfigurationSection("background"); + + if (backgroundSection != null) + { + backgroundItem = new PanelTemplateRecord.TemplateItem( + ItemParser.parse((backgroundSection.getString("icon", Material.AIR.name()))), + backgroundSection.getString("title", null), + backgroundSection.getString("description", null)); + } + } + else if (configurationSection.isString("background")) + { + // Process background icon if it contains only icon. + + backgroundItem = new PanelTemplateRecord.TemplateItem( + ItemParser.parse((configurationSection.getString("background", Material.AIR.name())))); + } + + // Read reusable + Map panelItemDataMap = new HashMap<>(); + ConfigurationSection reusable = configurationSection.getConfigurationSection("reusable"); + + if (reusable != null) + { + // Add all reusables to the local storage. + reusable.getKeys(false).forEach(key -> + readPanelItemTemplate(reusable.getConfigurationSection(key), key, panelItemDataMap)); + } + + // Read content + boolean[] forcedRows = readForcedRows(configurationSection); + + // Create template record. + PanelTemplateRecord template = new PanelTemplateRecord(type, title, borderItem, backgroundItem, forcedRows); + + // Read content + ConfigurationSection content = configurationSection.getConfigurationSection("content"); + + if (content == null) + { + // Return empty template. + return template; + } + + for (int rowIndex = 0; rowIndex < 6; rowIndex++) + { + // Read each line. + if (content.isConfigurationSection(String.valueOf(rowIndex + 1))) + { + ConfigurationSection line = content.getConfigurationSection(String.valueOf(rowIndex + 1)); + + if (line != null) + { + // Populate existing lines with items. + for (int columnIndex = 0; columnIndex < 9; columnIndex++) + { + if (line.isConfigurationSection(String.valueOf(columnIndex + 1))) + { + // If it contains a section, then build a new button template from it. + template.addButtonTemplate(rowIndex, + columnIndex, + readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1)))); + } + else if (line.isString(String.valueOf(columnIndex + 1))) + { + // If it contains just a single word, assume it is a reusable. + template.addButtonTemplate(rowIndex, + columnIndex, + panelItemDataMap.get(line.getString(String.valueOf(columnIndex + 1)))); + } + } + } + } + } + + // Garbage collector. + panelItemDataMap.clear(); + + return template; + } + + + /** + * This method reads force shown rows that must be always displayed. + * @param section Configuration section that contains force-shown path. + * @return boolean array that contains which lines are force loaded. + */ + private static boolean[] readForcedRows(@Nullable ConfigurationSection section) + { + boolean[] forceShow = new boolean[6]; + + if (section != null && section.contains("force-shown")) + { + if (section.isInt("force-shown")) + { + int value = section.getInt("force-shown"); + + if (value > 0 && value < 7) + { + forceShow[value-1] = true; + } + } + else if (section.isList("force-shown")) + { + section.getIntegerList("force-shown").forEach(number -> { + if (number > 0 && number < 7) + { + forceShow[number-1] = true; + } + }); + } + } + + return forceShow; + } + + + /** + * This method creates PanelItemTemplate from a given configuration section. + * @param section Section that should contain all information about the panel item template. + * @return PanelItemTemplate that should represent button from a section. + */ + @Nullable + private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section) + { + return readPanelItemTemplate(section, null, null); + } + + + + /** + * This method creates PanelItemTemplate from a given configuration section. + * @param section Section that should contain all information about the panel item template. + * @return PanelItemTemplate that should represent button from a section. + */ + @Nullable + private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section, + String itemKey, + Map reusableItemMap) + { + if (section == null) + { + // No section, no item. + return null; + } + + ItemTemplateRecord fallback; + + if (section.isConfigurationSection("fallback")) + { + fallback = readPanelItemTemplate(section.getConfigurationSection("fallback")); + } + else if (section.isString("fallback") && reusableItemMap != null) + { + fallback = reusableItemMap.get(section.getString("fallback")); + } + else + { + fallback = null; + } + + // Create Item Record + ItemTemplateRecord itemRecord = new ItemTemplateRecord(ItemParser.parse(section.getString("icon")), + section.getString("title", null), + section.getString("description", null), + fallback); + + // Read data + if (section.isConfigurationSection("data")) + { + ConfigurationSection dataSection = section.getConfigurationSection("data"); + + if (dataSection != null) + { + dataSection.getKeys(false).forEach(key -> itemRecord.addData(key, dataSection.get(key))); + } + } + + // Read Click data + if (section.isConfigurationSection("actions")) + { + ConfigurationSection actionSection = section.getConfigurationSection("actions"); + + if (actionSection != null) + { + actionSection.getKeys(false).forEach(actionKey -> { + ClickType clickType = Enums.getIfPresent(ClickType.class, actionKey.toUpperCase()).orNull(); + + if (clickType != null) + { + ConfigurationSection actionDataSection = actionSection.getConfigurationSection(actionKey); + + if (actionDataSection != null) + { + ItemTemplateRecord.ActionRecords actionData = + new ItemTemplateRecord.ActionRecords(clickType, + actionDataSection.getString("type"), + actionDataSection.getString("content"), + actionDataSection.getString("tooltip")); + + itemRecord.addAction(actionData); + } + } + }); + } + } + + // Add item to the map + if (reusableItemMap != null && itemKey != null) + { + reusableItemMap.put(itemKey, itemRecord); + } + + return itemRecord; + } + + + /** + * This method clears loaded panels from the cache. + */ + public static void clearPanels() + { + loadedPanels.clear(); + } + + + /** + * This map contains already read panels and their location. + * This improves performance for GUI opening, with a some memory usage. + */ + private static final Map loadedPanels = new HashMap<>(); +} diff --git a/src/main/java/world/bentobox/bentobox/api/placeholders/package-info.java b/src/main/java/world/bentobox/bentobox/api/placeholders/package-info.java new file mode 100644 index 000000000..3f0426d18 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/placeholders/package-info.java @@ -0,0 +1,7 @@ +/** + * API for placeholder replacement. + * + * @since 1.5.0 + * @author Poslovitch + */ +package world.bentobox.bentobox.api.placeholders; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/AddonPlaceholderExpansion.java b/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/AddonPlaceholderExpansion.java index c066261c9..d428b5708 100644 --- a/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/AddonPlaceholderExpansion.java +++ b/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/AddonPlaceholderExpansion.java @@ -3,7 +3,7 @@ import world.bentobox.bentobox.api.addons.Addon; public class AddonPlaceholderExpansion extends BasicPlaceholderExpansion { - private Addon addon; + private final Addon addon; public AddonPlaceholderExpansion(Addon addon) { this.addon = addon; diff --git a/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BasicPlaceholderExpansion.java b/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BasicPlaceholderExpansion.java index 40b67a02a..e8a8a91f3 100644 --- a/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BasicPlaceholderExpansion.java +++ b/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BasicPlaceholderExpansion.java @@ -16,7 +16,7 @@ */ abstract class BasicPlaceholderExpansion extends PlaceholderExpansion { @NonNull - private Map<@NonNull String, @NonNull PlaceholderReplacer> placeholders; + private final Map<@NonNull String, @NonNull PlaceholderReplacer> placeholders; BasicPlaceholderExpansion() { super(); diff --git a/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BentoBoxPlaceholderExpansion.java b/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BentoBoxPlaceholderExpansion.java index e2acd7805..da290c84e 100644 --- a/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BentoBoxPlaceholderExpansion.java +++ b/src/main/java/world/bentobox/bentobox/api/placeholders/placeholderapi/BentoBoxPlaceholderExpansion.java @@ -3,7 +3,7 @@ import world.bentobox.bentobox.BentoBox; public class BentoBoxPlaceholderExpansion extends BasicPlaceholderExpansion { - private BentoBox plugin; + private final BentoBox plugin; public BentoBoxPlaceholderExpansion(BentoBox plugin) { super(); diff --git a/src/main/java/world/bentobox/bentobox/api/user/Notifier.java b/src/main/java/world/bentobox/bentobox/api/user/Notifier.java index c6027af03..e75adff03 100644 --- a/src/main/java/world/bentobox/bentobox/api/user/Notifier.java +++ b/src/main/java/world/bentobox/bentobox/api/user/Notifier.java @@ -9,7 +9,7 @@ /** * Utilities class that helps to avoid spamming the User with potential repeated messages - * @author Poslovitch + * @author Poslovitch, tastybento */ public class Notifier { @@ -18,17 +18,19 @@ public class Notifier { */ private static final int NOTIFICATION_DELAY = 4; + private record Notification(String message, long time) {} + private final LoadingCache notificationCache = CacheBuilder.newBuilder() .expireAfterAccess(NOTIFICATION_DELAY, TimeUnit.SECONDS) .maximumSize(500) .build( - new CacheLoader() { + new CacheLoader<>() { @Override public Notification load(User user) { return new Notification(null, 0); } } - ); + ); /** * Sends message to a user only if the message hasn't been sent recently @@ -41,7 +43,7 @@ public synchronized boolean notify(User user, String message) { Notification lastNotification = notificationCache.get(user); long now = System.currentTimeMillis(); - if (now >= lastNotification.getTime() + (NOTIFICATION_DELAY * 1000) || !message.equals(lastNotification.getMessage())) { + if (now >= lastNotification.time() + (NOTIFICATION_DELAY * 1000) || !message.equals(lastNotification.message())) { notificationCache.put(user, new Notification(message, now)); user.sendRawMessage(message); return true; @@ -52,21 +54,4 @@ public synchronized boolean notify(User user, String message) { } } - private class Notification { - private final String message; - private final long time; - - private Notification(String message, long time) { - this.message = message; - this.time = time; - } - - public String getMessage() { - return message; - } - - public long getTime() { - return time; - } - } } diff --git a/src/main/java/world/bentobox/bentobox/api/user/User.java b/src/main/java/world/bentobox/bentobox/api/user/User.java index 9fa33b437..b032a2988 100644 --- a/src/main/java/world/bentobox/bentobox/api/user/User.java +++ b/src/main/java/world/bentobox/bentobox/api/user/User.java @@ -47,7 +47,7 @@ */ public class User implements MetaDataAble { - private static Map users = new HashMap<>(); + private static final Map users = new HashMap<>(); /** * Clears all users from the user list @@ -136,7 +136,7 @@ public static void removePlayer(Player player) { private static BentoBox plugin = BentoBox.getInstance(); @Nullable - private Player player; + private final Player player; private OfflinePlayer offlinePlayer; private final UUID playerUUID; @Nullable @@ -410,7 +410,7 @@ private String translate(String addonPrefix, String reference, String[] variable translation = plugin.getPlaceholdersManager().replacePlaceholders(player, translation); } - return Util.stripSpaceAfterColorCodes(ChatColor.translateAlternateColorCodes('&', translation)); + return Util.translateColorCodes(translation); } } @@ -618,10 +618,9 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof User)) { + if (!(obj instanceof User other)) { return false; } - User other = (User) obj; if (playerUUID == null) { return other.playerUUID == null; } else return playerUUID.equals(other.playerUUID); diff --git a/src/main/java/world/bentobox/bentobox/api/user/package-info.java b/src/main/java/world/bentobox/bentobox/api/user/package-info.java new file mode 100644 index 000000000..f7218e606 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/user/package-info.java @@ -0,0 +1,18 @@ +/** + * API for BentoBox Users + * + *

+ * BentoBox has extended the Bukkit Player class to become a User with the primary reason + * to enable localized messaging. Apart from that a User can be an OfflinePlayer, a Player, + * or even the Console. Users are used throughout the BentoBox code base, e.g., for commands + * instead of Players. It is possible to get a Player from a User if the User is a player. + *

+ *

+ * Notifier is a special kind of messaging class that prevents spamming of messages to a + * user in chat. It is useful for cases when the same message may be generated many times, e.g., in + * a protection scenario. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.api.user; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index f02173dd5..034cdf919 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -63,10 +63,10 @@ public class BlueprintClipboard { private boolean copying; private int index; private int lastPercentage; - private Map> bpEntities = new LinkedHashMap<>(); - private Map bpAttachable = new LinkedHashMap<>(); - private Map bpBlocks = new LinkedHashMap<>(); - private BentoBox plugin = BentoBox.getInstance(); + private final Map> bpEntities = new LinkedHashMap<>(); + private final Map bpAttachable = new LinkedHashMap<>(); + private final Map bpBlocks = new LinkedHashMap<>(); + private final BentoBox plugin = BentoBox.getInstance(); /** * Create a clipboard for blueprint @@ -190,48 +190,7 @@ private boolean copyBlock(Location l, @Nullable Vector origin2, boolean copyAir, Vector pos = new Vector(x, y, z); // Set entities - List bpEnts = new ArrayList<>(); - for (LivingEntity entity: entities) { - BlueprintEntity bpe = new BlueprintEntity(); - bpe.setType(entity.getType()); - bpe.setCustomName(entity.getCustomName()); - if (entity instanceof Villager) { - setVillager(entity, bpe); - } - if (entity instanceof Colorable) { - Colorable c = (Colorable)entity; - if (c.getColor() != null) { - bpe.setColor(c.getColor()); - } - } - if (entity instanceof Tameable) { - bpe.setTamed(((Tameable)entity).isTamed()); - } - if (entity instanceof ChestedHorse) { - bpe.setChest(((ChestedHorse)entity).isCarryingChest()); - } - // Only set if child. Most animals are adults - if (entity instanceof Ageable && !((Ageable)entity).isAdult()) { - bpe.setAdult(false); - } - if (entity instanceof AbstractHorse) { - AbstractHorse horse = (AbstractHorse)entity; - bpe.setDomestication(horse.getDomestication()); - bpe.setInventory(new HashMap<>()); - for (int i = 0; i < horse.getInventory().getSize(); i++) { - ItemStack item = horse.getInventory().getItem(i); - if (item != null) { - bpe.getInventory().put(i, item); - } - } - } - - if (entity instanceof Horse) { - Horse horse = (Horse)entity; - bpe.setStyle(horse.getStyle()); - } - bpEnts.add(bpe); - } + List bpEnts = setEntities(entities); // Store if (!bpEnts.isEmpty()) { bpEntities.put(pos, bpEnts); @@ -242,22 +201,31 @@ private boolean copyBlock(Location l, @Nullable Vector origin2, boolean copyAir, return true; } + + BlueprintBlock b = bluePrintBlock(pos, block); + if (b != null) { + this.bpBlocks.put(pos, b); + } + return true; + } + + private BlueprintBlock bluePrintBlock(Vector pos, Block block) { // Block state BlockState blockState = block.getState(); BlueprintBlock b = new BlueprintBlock(block.getBlockData().getAsString()); // Biome b.setBiome(block.getBiome()); // Signs - if (blockState instanceof Sign) { - Sign sign = (Sign)blockState; + if (blockState instanceof Sign sign) { b.setSignLines(Arrays.asList(sign.getLines())); + b.setGlowingText(sign.isGlowingText()); } // Set block data if (blockState.getData() instanceof Attachable) { // Placeholder for attachment bpBlocks.put(pos, new BlueprintBlock("minecraft:air")); bpAttachable.put(pos, b); - return true; + return null; } if (block.getType().equals(Material.BEDROCK)) { @@ -272,9 +240,8 @@ private boolean copyBlock(Location l, @Nullable Vector origin2, boolean copyAir, } // Chests - if (blockState instanceof InventoryHolder) { + if (blockState instanceof InventoryHolder ih) { b.setInventory(new HashMap<>()); - InventoryHolder ih = (InventoryHolder)blockState; for (int i = 0; i < ih.getInventory().getSize(); i++) { ItemStack item = ih.getInventory().getItem(i); if (item != null) { @@ -283,17 +250,8 @@ private boolean copyBlock(Location l, @Nullable Vector origin2, boolean copyAir, } } - if (blockState instanceof CreatureSpawner) { - CreatureSpawner spawner = (CreatureSpawner)blockState; - BlueprintCreatureSpawner cs = new BlueprintCreatureSpawner(); - cs.setSpawnedType(spawner.getSpawnedType()); - cs.setDelay(spawner.getDelay()); - cs.setMaxNearbyEntities(spawner.getMaxNearbyEntities()); - cs.setMaxSpawnDelay(spawner.getMaxSpawnDelay()); - cs.setMinSpawnDelay(spawner.getMinSpawnDelay()); - cs.setRequiredPlayerRange(spawner.getRequiredPlayerRange()); - cs.setSpawnRange(spawner.getSpawnRange()); - b.setCreatureSpawner(cs); + if (blockState instanceof CreatureSpawner spawner) { + b.setCreatureSpawner(getSpawner(spawner)); } // Banners @@ -301,8 +259,62 @@ private boolean copyBlock(Location l, @Nullable Vector origin2, boolean copyAir, b.setBannerPatterns(((Banner) blockState).getPatterns()); } - this.bpBlocks.put(pos, b); - return true; + return b; + } + + private BlueprintCreatureSpawner getSpawner(CreatureSpawner spawner) { + BlueprintCreatureSpawner cs = new BlueprintCreatureSpawner(); + cs.setSpawnedType(spawner.getSpawnedType()); + cs.setDelay(spawner.getDelay()); + cs.setMaxNearbyEntities(spawner.getMaxNearbyEntities()); + cs.setMaxSpawnDelay(spawner.getMaxSpawnDelay()); + cs.setMinSpawnDelay(spawner.getMinSpawnDelay()); + cs.setRequiredPlayerRange(spawner.getRequiredPlayerRange()); + cs.setSpawnRange(spawner.getSpawnRange()); + return cs; + } + + private List setEntities(Collection entities) { + List bpEnts = new ArrayList<>(); + for (LivingEntity entity: entities) { + BlueprintEntity bpe = new BlueprintEntity(); + bpe.setType(entity.getType()); + bpe.setCustomName(entity.getCustomName()); + if (entity instanceof Villager) { + setVillager(entity, bpe); + } + if (entity instanceof Colorable c) { + if (c.getColor() != null) { + bpe.setColor(c.getColor()); + } + } + if (entity instanceof Tameable) { + bpe.setTamed(((Tameable)entity).isTamed()); + } + if (entity instanceof ChestedHorse) { + bpe.setChest(((ChestedHorse)entity).isCarryingChest()); + } + // Only set if child. Most animals are adults + if (entity instanceof Ageable && !((Ageable)entity).isAdult()) { + bpe.setAdult(false); + } + if (entity instanceof AbstractHorse horse) { + bpe.setDomestication(horse.getDomestication()); + bpe.setInventory(new HashMap<>()); + for (int i = 0; i < horse.getInventory().getSize(); i++) { + ItemStack item = horse.getInventory().getItem(i); + if (item != null) { + bpe.getInventory().put(i, item); + } + } + } + + if (entity instanceof Horse horse) { + bpe.setStyle(horse.getStyle()); + } + bpEnts.add(bpe); + } + return bpEnts; } /** diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java index d6f754863..cde794edd 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java @@ -13,7 +13,6 @@ import java.util.concurrent.CompletableFuture; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -73,7 +72,7 @@ enum PasteState { private static final Map BLOCK_CONVERSION = ImmutableMap.of("sign", "oak_sign", "wall_sign", "oak_wall_sign"); - private BentoBox plugin; + private final BentoBox plugin; // The minimum block position (x,y,z) private Location pos1; // The maximum block position (x,y,z) @@ -86,19 +85,19 @@ enum PasteState { * The Blueprint to paste. */ @NonNull - private Blueprint blueprint; + private final Blueprint blueprint; /** * The Location to paste to. */ @NonNull - private Location location; + private final Location location; /** * Island related to this paste, may be null. */ @Nullable - private Island island; + private final Island island; /** * Paste a clipboard to a location and run task @@ -291,37 +290,39 @@ private void setBlockState(Block block, BlueprintBlock bpBlock) { // Get the block state BlockState bs = block.getState(); // Signs - if (bs instanceof org.bukkit.block.Sign) { - writeSign(block, bpBlock.getSignLines()); + if (bs instanceof org.bukkit.block.Sign sign) { + writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText()); } // Chests, in general if (bs instanceof InventoryHolder) { Inventory ih = ((InventoryHolder)bs).getInventory(); - // Double chests are pasted as two blocks so inventory is filled twice. This code stops over filling for the first block. + // Double chests are pasted as two blocks so inventory is filled twice. + // This code stops over-filling for the first block. bpBlock.getInventory().forEach(ih::setItem); } // Mob spawners - if (bs instanceof CreatureSpawner) { - CreatureSpawner spawner = ((CreatureSpawner) bs); - BlueprintCreatureSpawner s = bpBlock.getCreatureSpawner(); - spawner.setSpawnedType(s.getSpawnedType()); - spawner.setMaxNearbyEntities(s.getMaxNearbyEntities()); - spawner.setMaxSpawnDelay(s.getMaxSpawnDelay()); - spawner.setMinSpawnDelay(s.getMinSpawnDelay()); - spawner.setDelay(s.getDelay()); - spawner.setRequiredPlayerRange(s.getRequiredPlayerRange()); - spawner.setSpawnRange(s.getSpawnRange()); - bs.update(true, false); + if (bs instanceof CreatureSpawner spawner) { + setSpawner(spawner, bpBlock.getCreatureSpawner()); } // Banners - if (bs instanceof Banner && bpBlock.getBannerPatterns() != null) { - Banner banner = (Banner) bs; + if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) { bpBlock.getBannerPatterns().removeIf(Objects::isNull); banner.setPatterns(bpBlock.getBannerPatterns()); banner.update(true, false); } } + private void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) { + spawner.setSpawnedType(s.getSpawnedType()); + spawner.setMaxNearbyEntities(s.getMaxNearbyEntities()); + spawner.setMaxSpawnDelay(s.getMaxSpawnDelay()); + spawner.setMinSpawnDelay(s.getMinSpawnDelay()); + spawner.setDelay(s.getDelay()); + spawner.setRequiredPlayerRange(s.getRequiredPlayerRange()); + spawner.setSpawnRange(s.getSpawnRange()); + spawner.update(true, false); + } + /** * Sets any entity that is in this location * @param location - location @@ -386,7 +387,7 @@ private void updatePos(Location l) { } } - private void writeSign(final Block block, final List lines) { + private void writeSign(final Block block, final List lines, boolean glow) { BlockFace bf; if (block.getType().name().contains("WALL_SIGN")) { WallSign wallSign = (WallSign)block.getBlockData(); @@ -416,7 +417,7 @@ private void writeSign(final Block block, final List lines) { // Get the addon that is operating in this world String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse(""); for (int i = 0; i < 4; i++) { - s.setLine(i, ChatColor.translateAlternateColorCodes('&', plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()), + s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()), addonName + ".sign.line" + i,"").replace(TextVariables.NAME, name))); } } else { @@ -425,6 +426,7 @@ private void writeSign(final Block block, final List lines) { s.setLine(i, lines.get(i)); } } + s.setGlowingText(glow); // Update the sign s.update(); } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionPrompt.java b/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionPrompt.java index c56d0a3d6..4493d9c47 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionPrompt.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionPrompt.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.List; -import org.bukkit.ChatColor; import org.bukkit.conversations.ConversationContext; import org.bukkit.conversations.Prompt; import org.bukkit.conversations.StringPrompt; @@ -13,6 +12,8 @@ import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; +import world.bentobox.bentobox.util.Util; + /** * Collects a description @@ -22,8 +23,8 @@ public class DescriptionPrompt extends StringPrompt { private static final String DESCRIPTION = "description"; - private GameModeAddon addon; - private BlueprintBundle bb; + private final GameModeAddon addon; + private final BlueprintBundle bb; public DescriptionPrompt(GameModeAddon addon, BlueprintBundle bb) { this.addon = addon; @@ -57,7 +58,7 @@ public Prompt acceptInput(ConversationContext context, String input) { if (context.getSessionData(DESCRIPTION) != null) { desc = ((List) context.getSessionData(DESCRIPTION)); } - desc.add(ChatColor.translateAlternateColorCodes('&', input)); + desc.add(Util.translateColorCodes(input)); context.setSessionData(DESCRIPTION, desc); return this; } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionSuccessPrompt.java b/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionSuccessPrompt.java index 1a69cd089..b28546a9c 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionSuccessPrompt.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/conversation/DescriptionSuccessPrompt.java @@ -15,8 +15,8 @@ public class DescriptionSuccessPrompt extends MessagePrompt { - private GameModeAddon addon; - private BlueprintBundle bb; + private final GameModeAddon addon; + private final BlueprintBundle bb; /** * @param addon game mode addon diff --git a/src/main/java/world/bentobox/bentobox/blueprints/conversation/NamePrompt.java b/src/main/java/world/bentobox/bentobox/blueprints/conversation/NamePrompt.java index db637fd13..5dd8a6294 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/conversation/NamePrompt.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/conversation/NamePrompt.java @@ -15,12 +15,14 @@ import world.bentobox.bentobox.blueprints.Blueprint; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; import world.bentobox.bentobox.managers.BlueprintsManager; +import world.bentobox.bentobox.util.Util; + public class NamePrompt extends StringPrompt { - private GameModeAddon addon; + private final GameModeAddon addon; @Nullable - private BlueprintBundle bb; + private final BlueprintBundle bb; @Nullable private Blueprint bp; @@ -45,7 +47,7 @@ public String getPromptText(ConversationContext context) { public Prompt acceptInput(ConversationContext context, String input) { User user = User.getInstance((Player)context.getForWhom()); // Convert color codes - input = ChatColor.translateAlternateColorCodes('&', input); + input = Util.translateColorCodes(input); if (ChatColor.stripColor(input).length() > 32) { context.getForWhom().sendRawMessage("Too long"); return this; diff --git a/src/main/java/world/bentobox/bentobox/blueprints/conversation/NameSuccessPrompt.java b/src/main/java/world/bentobox/bentobox/blueprints/conversation/NameSuccessPrompt.java index ad9422f32..8fe5d8c51 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/conversation/NameSuccessPrompt.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/conversation/NameSuccessPrompt.java @@ -17,9 +17,9 @@ public class NameSuccessPrompt extends MessagePrompt { - private GameModeAddon addon; + private final GameModeAddon addon; private BlueprintBundle bb; - private Blueprint bp; + private final Blueprint bp; /** * Handles the name processing @@ -42,7 +42,6 @@ public String getPromptText(ConversationContext context) { if (bp != null) { BentoBox.getInstance().getBlueprintsManager().renameBlueprint(addon, bp, name); new BlueprintManagementPanel(BentoBox.getInstance(), user, addon).openBB(bb); - return user.getTranslation("commands.admin.blueprint.management.description.success"); } else { // Blueprint Bundle if (bb == null) { @@ -61,8 +60,8 @@ public String getPromptText(ConversationContext context) { new BlueprintManagementPanel(BentoBox.getInstance(), user, addon).openPanel(); // Set the name // if successfully - return user.getTranslation("commands.admin.blueprint.management.description.success"); } + return user.getTranslation("commands.admin.blueprint.management.description.success"); } @Override diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java index 4aa327bdf..7b2584087 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java @@ -34,6 +34,8 @@ public class BlueprintBlock { */ @Expose private List bannerPatterns; + @Expose + private boolean glowingText; public BlueprintBlock(String blockData) { this.blockData = blockData; @@ -124,4 +126,20 @@ public Biome getBiome() { public void setBiome(Biome biome) { this.biome = biome; } + + /** + * @return the glowingText + */ + public boolean isGlowingText() { + return glowingText; + } + + /** + * @param glowingText the glowingText to set + */ + public void setGlowingText(boolean glowingText) { + this.glowingText = glowingText; + } + + } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index a99e972d9..e173f6b86 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -75,8 +75,7 @@ public void configureEntity(Entity e) { ((Ageable)e).setBaby(); } } - if (e instanceof AbstractHorse) { - AbstractHorse horse = (AbstractHorse)e; + if (e instanceof AbstractHorse horse) { if (domestication != null) horse.setDomestication(domestication); if (inventory != null) { inventory.forEach(horse.getInventory()::setItem); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/package-info.java b/src/main/java/world/bentobox/bentobox/blueprints/package-info.java new file mode 100644 index 000000000..36d90d13a --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/blueprints/package-info.java @@ -0,0 +1,10 @@ +/** + * This package contains non-API classes that handle Blueprints. + * + * Blueprints are BentoBox's version of schematics, but contain + * additional useful info. + * + * @author tastybento + * + */ +package world.bentobox.bentobox.blueprints; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardFormat.java b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardFormat.java index 2cc438013..602827df5 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardFormat.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardFormat.java @@ -49,12 +49,12 @@ public Set getAliases() { } @Override - public ClipboardReader getReader(InputStream inputStream) throws IOException { + public ClipboardReader getReader(InputStream inputStream) { return new BlueprintClipboardReader(inputStream); } @Override - public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { + public ClipboardWriter getWriter(OutputStream outputStream) { return new BlueprintClipboardWriter(outputStream); } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardReader.java b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardReader.java index c944294ee..c0701ba1f 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardReader.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardReader.java @@ -1,6 +1,5 @@ package world.bentobox.bentobox.blueprints.worldedit; -import java.io.IOException; import java.io.InputStream; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -12,19 +11,19 @@ */ public class BlueprintClipboardReader implements ClipboardReader { - private InputStream inputStream; + private final InputStream inputStream; public BlueprintClipboardReader(InputStream inputStream) { this.inputStream = inputStream; } @Override - public Clipboard read() throws IOException { + public Clipboard read() { throw new UnsupportedOperationException(); // TODO } @Override - public void close() throws IOException { + public void close() { throw new UnsupportedOperationException(); // TODO } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardWriter.java b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardWriter.java index 105b19b87..095ffbf3f 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardWriter.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardWriter.java @@ -1,6 +1,5 @@ package world.bentobox.bentobox.blueprints.worldedit; -import java.io.IOException; import java.io.OutputStream; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -12,18 +11,18 @@ */ public class BlueprintClipboardWriter implements ClipboardWriter { - private OutputStream outputStream; + private final OutputStream outputStream; public BlueprintClipboardWriter(OutputStream outputStream) { this.outputStream = outputStream; } @Override - public void write(Clipboard clipboard) throws IOException { + public void write(Clipboard clipboard) { throw new UnsupportedOperationException(); // TODO } @Override - public void close() throws IOException { + public void close() { throw new UnsupportedOperationException(); // TODO } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintSchematicConverter.java b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintSchematicConverter.java index 6a1708006..05c4621a9 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintSchematicConverter.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintSchematicConverter.java @@ -18,7 +18,7 @@ public class BlueprintSchematicConverter { private File blueprintFile; public BlueprintSchematicConverter(File blueprintFile) { - if(!BentoBox.getInstance().getHooks().getHook("WorldEdit").isPresent()) { + if(BentoBox.getInstance().getHooks().getHook("WorldEdit").isEmpty()) { BentoBox.getInstance().logError("WorldEdit must be installed to use that class !"); return; } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxReloadCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxReloadCommand.java index 85ca4c7af..4ee6838c6 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxReloadCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxReloadCommand.java @@ -7,6 +7,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand; import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; +import world.bentobox.bentobox.api.panels.reader.TemplateReader; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand; import world.bentobox.bentobox.listeners.PanelListenerManager; @@ -45,6 +46,8 @@ public boolean execute(User user, String label, List args) { // Close all open panels PanelListenerManager.closeAllPanels(); + // Clear all template panels. + TemplateReader.clearPanels(); // Reload settings getPlugin().loadSettings(); diff --git a/src/main/java/world/bentobox/bentobox/commands/package-info.java b/src/main/java/world/bentobox/bentobox/commands/package-info.java new file mode 100644 index 000000000..4893d86de --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/commands/package-info.java @@ -0,0 +1,7 @@ +/** + * The package contains non-API commands for BentoBox itself. + * + * @author tastybento + * + */ +package world.bentobox.bentobox.commands; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/database/Database.java b/src/main/java/world/bentobox/bentobox/database/Database.java index 7b193e117..5da65334f 100644 --- a/src/main/java/world/bentobox/bentobox/database/Database.java +++ b/src/main/java/world/bentobox/bentobox/database/Database.java @@ -21,8 +21,8 @@ */ public class Database { - private AbstractDatabaseHandler handler; - private Logger logger; + private final AbstractDatabaseHandler handler; + private final Logger logger; private static DatabaseSetup databaseSetup = DatabaseSetup.getDatabase(); /** diff --git a/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java index 20e312c68..8f4e7e105 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java @@ -17,7 +17,7 @@ */ public abstract class AbstractJSONDatabaseHandler extends AbstractDatabaseHandler { - private Gson gson; + private final Gson gson; /** * Constructor diff --git a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabase.java b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabase.java index b2b2ce108..0435d3acf 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabase.java +++ b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabase.java @@ -6,7 +6,7 @@ public class JSONDatabase implements DatabaseSetup { - private JSONDatabaseConnector connector = new JSONDatabaseConnector(BentoBox.getInstance()); + private final JSONDatabaseConnector connector = new JSONDatabaseConnector(BentoBox.getInstance()); /* (non-Javadoc) * @see world.bentobox.bentobox.database.DatabaseSetup#getHandler(java.lang.Class) diff --git a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseConnector.java b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseConnector.java index fab8aa33e..82a49c031 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseConnector.java +++ b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseConnector.java @@ -23,11 +23,11 @@ public class JSONDatabaseConnector implements DatabaseConnector { @NonNull public String getUniqueId(String tableName) { UUID uuid = UUID.randomUUID(); - File file = new File(dataFolder, tableName + File.separator + uuid.toString() + JSON); + File file = new File(dataFolder, tableName + File.separator + uuid + JSON); int limit = 0; while (file.exists() && limit++ < MAX_LOOPS) { uuid = UUID.randomUUID(); - file = new File(dataFolder, tableName + File.separator + uuid.toString() + JSON); + file = new File(dataFolder, tableName + File.separator + uuid + JSON); } return uuid.toString(); } diff --git a/src/main/java/world/bentobox/bentobox/database/json/adapters/FlagTypeAdapter.java b/src/main/java/world/bentobox/bentobox/database/json/adapters/FlagTypeAdapter.java index 2b283ae93..7d673182c 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/adapters/FlagTypeAdapter.java +++ b/src/main/java/world/bentobox/bentobox/database/json/adapters/FlagTypeAdapter.java @@ -15,7 +15,7 @@ public class FlagTypeAdapter extends TypeAdapter { - private BentoBox plugin; + private final BentoBox plugin; public FlagTypeAdapter(BentoBox plugin) { this.plugin = plugin; @@ -42,7 +42,7 @@ public Flag read(JsonReader reader) throws IOException { // Flags can end up null if an addon that created one is removed or if a flag name was changed if (f == null) { // Create a temporary flag with a unique key. It will be immediately deleted after loading - f = new Flag.Builder("NULL_FLAG_"+ UUID.randomUUID().toString(), Material.STONE).build(); + f = new Flag.Builder("NULL_FLAG_"+ UUID.randomUUID(), Material.STONE).build(); } return f; } diff --git a/src/main/java/world/bentobox/bentobox/database/json/adapters/package-info.java b/src/main/java/world/bentobox/bentobox/database/json/adapters/package-info.java new file mode 100644 index 000000000..e39cd5320 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/json/adapters/package-info.java @@ -0,0 +1,12 @@ +/** + * These are GSON adapters used to serialize and deserialize various data types. + *

+ * The {@link world.bentobox.bentobox.database.json.adapters.BukkitObjectTypeAdapter} + * is a catch-all adapter that uses the built-in Bukkit serialization capabilities. Before + * we knew about this, there were other ones built, like for Location, that have to remain + * for backwards compatibility reasons. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.database.json.adapters; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java index a347b94dd..02c0744a9 100644 --- a/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java @@ -40,7 +40,7 @@ public class MongoDBDatabaseHandler extends AbstractJSONDatabaseHandler { private static final String MONGO_ID = "_id"; private MongoCollection collection; - private DatabaseConnector dbConnecter; + private final DatabaseConnector dbConnecter; /** * Handles the connection to the database and creation of the initial database schema (tables) for @@ -142,12 +142,11 @@ public CompletableFuture saveObject(T instance) { completableFuture.complete(false); return completableFuture; } - if (!(instance instanceof DataObject)) { + if (!(instance instanceof DataObject dataObj)) { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); completableFuture.complete(false); return completableFuture; } - DataObject dataObj = (DataObject)instance; try { Gson gson = getGson(); String toStore = gson.toJson(instance); diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Island.java b/src/main/java/world/bentobox/bentobox/database/objects/Island.java index a2273e16f..8d7a2cd14 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -1,8 +1,6 @@ package world.bentobox.bentobox.database.objects; import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; @@ -23,7 +21,6 @@ import org.bukkit.World.Environment; import org.bukkit.entity.Player; import org.bukkit.util.BoundingBox; -import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -37,7 +34,6 @@ import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.flags.Flag; -import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.api.metadata.MetaDataAble; import world.bentobox.bentobox.api.metadata.MetaDataValue; @@ -49,6 +45,7 @@ import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.RanksManager; +import world.bentobox.bentobox.util.IslandInfo; import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Util; @@ -1080,71 +1077,21 @@ public void setWorld(World world) { * Shows info of this island to this user. * @param user the User who is requesting it * @return always true + * @deprecated Use {@link IslandInfo#showInfo(User) instead} */ + @Deprecated public boolean showInfo(User user) { - BentoBox plugin = BentoBox.getInstance(); - user.sendMessage("commands.admin.info.title"); - user.sendMessage("commands.admin.info.island-uuid", "[uuid]", this.getUniqueId()); - if (getOwner() == null) { - user.sendMessage("commands.admin.info.unowned"); - } else { - user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(getOwner()), "[uuid]", getOwner().toString()); - - // Fixes #getLastPlayed() returning 0 when it is the owner's first connection. - long lastPlayed = (Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() != 0) ? - Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() : Bukkit.getServer().getOfflinePlayer(getOwner()).getFirstPlayed(); - String formattedDate; - try { - String dateTimeFormat = plugin.getLocalesManager().get("commands.admin.info.last-login-date-time-format"); - formattedDate = new SimpleDateFormat(dateTimeFormat).format(new Date(lastPlayed)); - } catch (NullPointerException | IllegalArgumentException ignored) { - formattedDate = new Date(lastPlayed).toString(); - } - user.sendMessage("commands.admin.info.last-login","[date]", formattedDate); - - user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(getWorld(), getOwner()))); - String resets = String.valueOf(plugin.getPlayers().getResets(getWorld(), getOwner())); - String total = plugin.getIWM().getResetLimit(getWorld()) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(getWorld())); - user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); - // Show team members - showMembers(user); - } - Vector location = getProtectionCenter().toVector(); - user.sendMessage("commands.admin.info.island-protection-center", TextVariables.XYZ, Util.xyz(location)); - user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(getCenter().toVector())); - user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(new Vector(this.getMinX(), 0, getMinZ())), "[xz2]", Util.xyz(new Vector(this.getMaxX(), 0, getMaxZ()))); - user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(getProtectionRange())); - user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(getMaxEverProtectionRange())); - user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(this.getMinProtectedX(), 0, getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(this.getMaxProtectedX(), 0, getMaxProtectedZ()))); - if (spawn) { - user.sendMessage("commands.admin.info.is-spawn"); - } - if (!getBanned().isEmpty()) { - user.sendMessage("commands.admin.info.banned-players"); - getBanned().forEach(u -> user.sendMessage("commands.admin.info.banned-format", TextVariables.NAME, plugin.getPlayers().getName(u))); - } - if (getPurgeProtected()) { - user.sendMessage("commands.admin.info.purge-protected"); - } - return true; + return new IslandInfo(this).showInfo(user); } /** * Shows the members of this island to this user. * @param user the User who is requesting it + * @deprecated Use {@link IslandInfo#showMembers(User) instead} */ + @Deprecated public void showMembers(User user) { - BentoBox plugin = BentoBox.getInstance(); - user.sendMessage("commands.admin.info.team-members-title"); - members.forEach((u, i) -> { - if (owner.equals(u)) { - user.sendMessage("commands.admin.info.team-owner-format", TextVariables.NAME, plugin.getPlayers().getName(u) - , "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i))); - } else if (i > RanksManager.VISITOR_RANK){ - user.sendMessage("commands.admin.info.team-member-format", TextVariables.NAME, plugin.getPlayers().getName(u) - , "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i))); - } - }); + new IslandInfo(this).showMembers(user); } /** @@ -1346,7 +1293,7 @@ public boolean isCooldown(Flag flag) { * @param flag - Flag to cooldown */ public void setCooldown(Flag flag) { - cooldowns.put(flag, flag.getCooldown() * 1000 + System.currentTimeMillis()); + cooldowns.put(flag, flag.getCooldown() * 1000L + System.currentTimeMillis()); setChanged(); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java b/src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java index 30a5a9697..2b444a5e6 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java @@ -79,10 +79,9 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof IslandDeletion)) { + if (!(obj instanceof IslandDeletion other)) { return false; } - IslandDeletion other = (IslandDeletion) obj; if (uniqueId == null) { return other.uniqueId == null; } else return uniqueId.equals(other.uniqueId); diff --git a/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer.java b/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer.java index 118bd0932..128ede5a8 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer.java @@ -26,8 +26,7 @@ public Map deserialize(Object object) { return result; } // For YAML - if (object instanceof MemorySection) { - MemorySection section = (MemorySection) object; + if (object instanceof MemorySection section) { for (String key : section.getKeys(false)) { BentoBox.getInstance().getFlagsManager().getFlag(key).ifPresent(flag -> result.put(flag, section.getInt(key))); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer2.java b/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer2.java index 564eec52c..0c43f6de7 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer2.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer2.java @@ -23,8 +23,7 @@ public Map deserialize(Object object) { return result; } // For YAML - if (object instanceof MemorySection) { - MemorySection section = (MemorySection) object; + if (object instanceof MemorySection section) { for (String key : section.getKeys(false)) { BentoBox.getInstance().getFlagsManager().getFlag(key).ifPresent(flag -> result.put(flag, section.getBoolean(key) ? 0 : -1)); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer3.java b/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer3.java index 7ef181eeb..c6997f8fc 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer3.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/adapters/FlagSerializer3.java @@ -26,8 +26,7 @@ public Map deserialize(Object object) { return result; } // For YAML - if (object instanceof MemorySection) { - MemorySection section = (MemorySection) object; + if (object instanceof MemorySection section) { for (String key : section.getKeys(false)) { BentoBox.getInstance().getFlagsManager().getFlag(key).ifPresent(flag -> result.put(flag, section.getLong(key))); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/adapters/package-info.java b/src/main/java/world/bentobox/bentobox/database/objects/adapters/package-info.java new file mode 100644 index 000000000..e5a955634 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/objects/adapters/package-info.java @@ -0,0 +1,6 @@ +/** + * These adapters are used by the YAML database, which is now only used + * for configuration storage and management. For the GSON adapters used + * by the "real" databases, see {@link world.bentobox.bentobox.database.json.adapters} + */ +package world.bentobox.bentobox.database.objects.adapters; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/database/objects/package-info.java b/src/main/java/world/bentobox/bentobox/database/objects/package-info.java new file mode 100644 index 000000000..dee4be6e1 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/objects/package-info.java @@ -0,0 +1,11 @@ +/** + * These are the data objects and adapters that BentoBox uses to store its data. + * + *

+ * Key objects are Island and Players. BentoBox loads all Island objects on startup into RAM. Players are loaded + * as and when required. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.database.objects; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/database/package-info.java b/src/main/java/world/bentobox/bentobox/database/package-info.java new file mode 100644 index 000000000..d8873e206 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/package-info.java @@ -0,0 +1,21 @@ +/** + * Provides an abstract database for storing Java POJOs + * and also YAML config files. + * + *

+ * The database supports concrete implementations for JSON flat file, MongoDB, MySQL, PostgreSQL, SQLite + * and the ability to transition between them. + *

+ *

+ * Storage of POJOs is done via GSON, i.e, the object is serialized and then stored. Each data object must + * implement the DataObject interface, which requires a uniqueId field. This is what is used for indexing + * and finding. + *

+ *

+ * Performance with JSON is generally very good, and the other databases are really there for concurrent usage + * by other applications. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.database; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/database/sql/SQLConfiguration.java b/src/main/java/world/bentobox/bentobox/database/sql/SQLConfiguration.java index baafdad8b..373d4f04b 100644 --- a/src/main/java/world/bentobox/bentobox/database/sql/SQLConfiguration.java +++ b/src/main/java/world/bentobox/bentobox/database/sql/SQLConfiguration.java @@ -18,7 +18,7 @@ public class SQLConfiguration { private String loadObjectsSQL; private String renameTableSQL; private final String tableName; - private boolean renameRequired; + private final boolean renameRequired; private final String oldTableName; public SQLConfiguration(BentoBox plugin, Class type) { diff --git a/src/main/java/world/bentobox/bentobox/database/sql/SQLDatabaseConnector.java b/src/main/java/world/bentobox/bentobox/database/sql/SQLDatabaseConnector.java index bc4383fac..3770062fc 100644 --- a/src/main/java/world/bentobox/bentobox/database/sql/SQLDatabaseConnector.java +++ b/src/main/java/world/bentobox/bentobox/database/sql/SQLDatabaseConnector.java @@ -15,7 +15,7 @@ public abstract class SQLDatabaseConnector implements DatabaseConnector { protected String connectionUrl; - private DatabaseConnectionSettingsImpl dbSettings; + private final DatabaseConnectionSettingsImpl dbSettings; protected static Connection connection = null; protected static Set> types = new HashSet<>(); diff --git a/src/main/java/world/bentobox/bentobox/database/sql/sqlite/SQLiteDatabase.java b/src/main/java/world/bentobox/bentobox/database/sql/sqlite/SQLiteDatabase.java index ff599734b..327bdb657 100644 --- a/src/main/java/world/bentobox/bentobox/database/sql/sqlite/SQLiteDatabase.java +++ b/src/main/java/world/bentobox/bentobox/database/sql/sqlite/SQLiteDatabase.java @@ -10,7 +10,7 @@ */ public class SQLiteDatabase implements DatabaseSetup { - private SQLiteDatabaseConnector connector = new SQLiteDatabaseConnector(BentoBox.getInstance()); + private final SQLiteDatabaseConnector connector = new SQLiteDatabaseConnector(BentoBox.getInstance()); @Override public AbstractDatabaseHandler getHandler(Class dataObjectClass) { diff --git a/src/main/java/world/bentobox/bentobox/database/transition/TransitionDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/transition/TransitionDatabaseHandler.java index 325837f99..a3bb314bd 100644 --- a/src/main/java/world/bentobox/bentobox/database/transition/TransitionDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/transition/TransitionDatabaseHandler.java @@ -19,8 +19,8 @@ */ public class TransitionDatabaseHandler extends AbstractDatabaseHandler { - private AbstractDatabaseHandler fromHandler; - private AbstractDatabaseHandler toHandler; + private final AbstractDatabaseHandler fromHandler; + private final AbstractDatabaseHandler toHandler; /** * Constructor diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabase.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabase.java index 597a24384..4df6fb9cf 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabase.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabase.java @@ -6,7 +6,7 @@ public class YamlDatabase implements DatabaseSetup { - private YamlDatabaseConnector connector = new YamlDatabaseConnector(BentoBox.getInstance()); + private final YamlDatabaseConnector connector = new YamlDatabaseConnector(BentoBox.getInstance()); /** * Get the config diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java index c7a132965..aacc05b97 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java @@ -151,12 +151,9 @@ private void commentFile(File file, Map commentMap) { for (Entry e : commentMap.entrySet()) { if (nextLine.contains(e.getKey())) { // We want the comment to start at the same level as the entry - StringBuilder commentLine = new StringBuilder(); - for (int i = 0; i < nextLine.indexOf(e.getKey()); i++){ - commentLine.append(' '); - } - commentLine.append(e.getValue()); - nextLine = commentLine.toString(); + String commentLine = " ".repeat(Math.max(0, nextLine.indexOf(e.getKey()))) + + e.getValue(); + nextLine = commentLine; break; } } @@ -191,11 +188,11 @@ private void copyFileUsingStream(File source, File dest) throws IOException { @NonNull public String getUniqueId(String tableName) { UUID uuid = UUID.randomUUID(); - File file = new File(dataFolder, tableName + File.separator + uuid.toString() + YML); + File file = new File(dataFolder, tableName + File.separator + uuid + YML); int limit = 0; while (file.exists() && limit++ < MAX_LOOPS) { uuid = UUID.randomUUID(); - file = new File(dataFolder, tableName + File.separator + uuid.toString() + YML); + file = new File(dataFolder, tableName + File.separator + uuid + YML); } return uuid.toString(); } diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java index 6cad6bd8d..6c9d56dd5 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java @@ -534,7 +534,7 @@ private void handleConfigEntryComments(@NonNull ConfigEntry configEntry, @NonNul } private void setComment(@NonNull String comment, @NonNull YamlConfiguration config, @NonNull Map yamlComments, @NonNull String parent) { - String random = "comment-" + UUID.randomUUID().toString(); + String random = "comment-" + UUID.randomUUID(); // Store placeholder config.set(parent + random, " "); // Create comment @@ -554,20 +554,20 @@ private Object serialize(@Nullable Object object) { } // UUID has it's own serialization, that is not picked up automatically if (object instanceof UUID) { - return ((UUID)object).toString(); + return object.toString(); } // Only the world name is needed for worlds - if (object instanceof World) { - return ((World)object).getName(); + if (object instanceof World w) { + return w.getName(); } // Location - if (object instanceof Location) { - return Util.getStringLocation((Location)object); + if (object instanceof Location l) { + return Util.getStringLocation(l); } // Enums - if (object instanceof Enum) { + if (object instanceof Enum e) { //Custom enums are a child of the Enum class. Just get the names of each one. - return ((Enum)object).name(); + return e.name(); } return object; } diff --git a/src/main/java/world/bentobox/bentobox/hooks/DynmapHook.java b/src/main/java/world/bentobox/bentobox/hooks/DynmapHook.java index 3eb8c5347..a14cbaf93 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/DynmapHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/DynmapHook.java @@ -24,7 +24,7 @@ public class DynmapHook extends Hook { private MarkerAPI markerAPI; @NonNull - private Map<@NonNull GameModeAddon, @NonNull MarkerSet> markerSets; + private final Map<@NonNull GameModeAddon, @NonNull MarkerSet> markerSets; public DynmapHook() { super("dynmap", Material.FILLED_MAP); diff --git a/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java b/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java index 2d5aa10ae..d7e630f16 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java @@ -262,31 +262,29 @@ public static String getPotionTypeName(PotionType potionType, User user) { if (hooked) { return LanguageHelper.getPotionName(potionType, getUserLocale(user)); } - switch (potionType) { - case UNCRAFTABLE: return "Uncraftable Potion"; - case WATER: return "Water Bottle"; - case MUNDANE: return "Mundane Potion"; - case THICK: return "Thick Potion"; - case AWKWARD: return "Awkward Potion"; - case NIGHT_VISION: return "Potion of Night Vision"; - case INVISIBILITY: return "Potion of Invisibility"; - case JUMP: return "Potion of Leaping"; - case FIRE_RESISTANCE: return "Potion of Fire Resistance"; - case SPEED: return "Potion of Swiftness"; - case SLOWNESS: return "Potion of Slowness"; - case WATER_BREATHING: return "Potion of Water Breathing"; - case INSTANT_HEAL: return "Potion of Healing"; - case INSTANT_DAMAGE: return "Potion of Harming"; - case POISON: return "Potion of Poison"; - case REGEN: return "Potion of Regeneration"; - case STRENGTH: return "Potion of Strength"; - case WEAKNESS: return "Potion of Weakness"; - case LUCK: return "Potion of Luck"; - case TURTLE_MASTER: return "Potion of the Turtle Master"; - case SLOW_FALLING: return "Potion of Slow Falling"; - default: - return Util.prettifyText(potionType.name()); - } + return switch (potionType) { + case UNCRAFTABLE -> "Uncraftable Potion"; + case WATER -> "Water Bottle"; + case MUNDANE -> "Mundane Potion"; + case THICK -> "Thick Potion"; + case AWKWARD -> "Awkward Potion"; + case NIGHT_VISION -> "Potion of Night Vision"; + case INVISIBILITY -> "Potion of Invisibility"; + case JUMP -> "Potion of Leaping"; + case FIRE_RESISTANCE -> "Potion of Fire Resistance"; + case SPEED -> "Potion of Swiftness"; + case SLOWNESS -> "Potion of Slowness"; + case WATER_BREATHING -> "Potion of Water Breathing"; + case INSTANT_HEAL -> "Potion of Healing"; + case INSTANT_DAMAGE -> "Potion of Harming"; + case POISON -> "Potion of Poison"; + case REGEN -> "Potion of Regeneration"; + case STRENGTH -> "Potion of Strength"; + case WEAKNESS -> "Potion of Weakness"; + case LUCK -> "Potion of Luck"; + case TURTLE_MASTER -> "Potion of the Turtle Master"; + case SLOW_FALLING -> "Potion of Slow Falling"; + }; } @@ -301,31 +299,29 @@ public static String getSplashPotionName(PotionType potionType, User user) { if (hooked) { return LanguageHelper.getSplashPotionName(potionType, getUserLocale(user)); } - switch (potionType) { - case UNCRAFTABLE: return "Splash Uncraftable Potion"; - case WATER: return "Splash Water Bottle"; - case MUNDANE: return "Mundane Splash Potion"; - case THICK: return "Thick Splash Potion"; - case AWKWARD: return "Awkward Splash Potion"; - case NIGHT_VISION: return "Splash Potion of Night Vision"; - case INVISIBILITY: return "Splash Potion of Invisibility"; - case JUMP: return "Splash Potion of Leaping"; - case FIRE_RESISTANCE: return "Splash Potion of Fire Resistance"; - case SPEED: return "Splash Potion of Swiftness"; - case SLOWNESS: return "Splash Potion of Slowness"; - case WATER_BREATHING: return "Splash Potion of Water Breathing"; - case INSTANT_HEAL: return "Splash Potion of Healing"; - case INSTANT_DAMAGE: return "Splash Potion of Harming"; - case POISON: return "Splash Potion of Poison"; - case REGEN: return "Splash Potion of Regeneration"; - case STRENGTH: return "Splash Potion of Strength"; - case WEAKNESS: return "Splash Potion of Weakness"; - case LUCK: return "Splash Potion of Luck"; - case TURTLE_MASTER: return "Splash Potion of the Turtle Master"; - case SLOW_FALLING: return "Splash Potion of Slow Falling"; - default: - return Util.prettifyText(potionType.name()); - } + return switch (potionType) { + case UNCRAFTABLE -> "Splash Uncraftable Potion"; + case WATER -> "Splash Water Bottle"; + case MUNDANE -> "Mundane Splash Potion"; + case THICK -> "Thick Splash Potion"; + case AWKWARD -> "Awkward Splash Potion"; + case NIGHT_VISION -> "Splash Potion of Night Vision"; + case INVISIBILITY -> "Splash Potion of Invisibility"; + case JUMP -> "Splash Potion of Leaping"; + case FIRE_RESISTANCE -> "Splash Potion of Fire Resistance"; + case SPEED -> "Splash Potion of Swiftness"; + case SLOWNESS -> "Splash Potion of Slowness"; + case WATER_BREATHING -> "Splash Potion of Water Breathing"; + case INSTANT_HEAL -> "Splash Potion of Healing"; + case INSTANT_DAMAGE -> "Splash Potion of Harming"; + case POISON -> "Splash Potion of Poison"; + case REGEN -> "Splash Potion of Regeneration"; + case STRENGTH -> "Splash Potion of Strength"; + case WEAKNESS -> "Splash Potion of Weakness"; + case LUCK -> "Splash Potion of Luck"; + case TURTLE_MASTER -> "Splash Potion of the Turtle Master"; + case SLOW_FALLING -> "Splash Potion of Slow Falling"; + }; } /** @@ -339,31 +335,29 @@ public static String getLingeringPotionName(PotionType potionType, User user) { if (hooked) { return LanguageHelper.getLingeringPotionName(potionType, getUserLocale(user)); } - switch (potionType) { - case UNCRAFTABLE: return "Lingering Uncraftable Potion"; - case WATER: return "Lingering Water Bottle"; - case MUNDANE: return "Mundane Lingering Potion"; - case THICK: return "Thick Lingering Potion"; - case AWKWARD: return "Awkward Lingering Potion"; - case NIGHT_VISION: return "Lingering Potion of Night Vision"; - case INVISIBILITY: return "Lingering Potion of Invisibility"; - case JUMP: return "Lingering Potion of Leaping"; - case FIRE_RESISTANCE: return "Lingering Potion of Fire Resistance"; - case SPEED: return "Lingering Potion of Swiftness"; - case SLOWNESS: return "Lingering Potion of Slowness"; - case WATER_BREATHING: return "Lingering Potion of Water Breathing"; - case INSTANT_HEAL: return "Lingering Potion of Healing"; - case INSTANT_DAMAGE: return "Lingering Potion of Harming"; - case POISON: return "Lingering Potion of Poison"; - case REGEN: return "Lingering Potion of Regeneration"; - case STRENGTH: return "Lingering Potion of Strength"; - case WEAKNESS: return "Lingering Potion of Weakness"; - case LUCK: return "Lingering Potion of Luck"; - case TURTLE_MASTER: return "Lingering Potion of the Turtle Master"; - case SLOW_FALLING: return "Lingering Potion of Slow Falling"; - default: - return Util.prettifyText(potionType.name()); - } + return switch (potionType) { + case UNCRAFTABLE -> "Lingering Uncraftable Potion"; + case WATER -> "Lingering Water Bottle"; + case MUNDANE -> "Mundane Lingering Potion"; + case THICK -> "Thick Lingering Potion"; + case AWKWARD -> "Awkward Lingering Potion"; + case NIGHT_VISION -> "Lingering Potion of Night Vision"; + case INVISIBILITY -> "Lingering Potion of Invisibility"; + case JUMP -> "Lingering Potion of Leaping"; + case FIRE_RESISTANCE -> "Lingering Potion of Fire Resistance"; + case SPEED -> "Lingering Potion of Swiftness"; + case SLOWNESS -> "Lingering Potion of Slowness"; + case WATER_BREATHING -> "Lingering Potion of Water Breathing"; + case INSTANT_HEAL -> "Lingering Potion of Healing"; + case INSTANT_DAMAGE -> "Lingering Potion of Harming"; + case POISON -> "Lingering Potion of Poison"; + case REGEN -> "Lingering Potion of Regeneration"; + case STRENGTH -> "Lingering Potion of Strength"; + case WEAKNESS -> "Lingering Potion of Weakness"; + case LUCK -> "Lingering Potion of Luck"; + case TURTLE_MASTER -> "Lingering Potion of the Turtle Master"; + case SLOW_FALLING -> "Lingering Potion of Slow Falling"; + }; } /** @@ -377,31 +371,27 @@ public static String getTippedArrowName(PotionType potionType, User user) { if (hooked) { return LanguageHelper.getTippedArrowName(potionType, getUserLocale(user)); } - switch (potionType) { - case UNCRAFTABLE: return "Uncraftable Tipped Arrow"; - case WATER: return "Arrow of Splashing"; - case MUNDANE: - case THICK: - case AWKWARD: return "Tipped Arrow"; - case NIGHT_VISION: return "Arrow of Night Vision"; - case INVISIBILITY: return "Arrow of Invisibility"; - case JUMP: return "Arrow of Leaping"; - case FIRE_RESISTANCE: return "Arrow of Fire Resistance"; - case SPEED: return "Arrow of Swiftness"; - case SLOWNESS: return "Arrow of Slowness"; - case WATER_BREATHING: return "Arrow of Water Breathing"; - case INSTANT_HEAL: return "Arrow of Healing"; - case INSTANT_DAMAGE: return "Arrow of Harming"; - case POISON: return "Arrow of Poison"; - case REGEN: return "Arrow of Regeneration"; - case STRENGTH: return "Arrow of Strength"; - case WEAKNESS: return "Arrow of Weakness"; - case LUCK: return "Arrow of Luck"; - case TURTLE_MASTER: return "Arrow of the Turtle Master"; - case SLOW_FALLING: return "Arrow of Slow Falling"; - default: - return Util.prettifyText(potionType.name()); - } + return switch (potionType) { + case UNCRAFTABLE -> "Uncraftable Tipped Arrow"; + case WATER -> "Arrow of Splashing"; + case MUNDANE, THICK, AWKWARD -> "Tipped Arrow"; + case NIGHT_VISION -> "Arrow of Night Vision"; + case INVISIBILITY -> "Arrow of Invisibility"; + case JUMP -> "Arrow of Leaping"; + case FIRE_RESISTANCE -> "Arrow of Fire Resistance"; + case SPEED -> "Arrow of Swiftness"; + case SLOWNESS -> "Arrow of Slowness"; + case WATER_BREATHING -> "Arrow of Water Breathing"; + case INSTANT_HEAL -> "Arrow of Healing"; + case INSTANT_DAMAGE -> "Arrow of Harming"; + case POISON -> "Arrow of Poison"; + case REGEN -> "Arrow of Regeneration"; + case STRENGTH -> "Arrow of Strength"; + case WEAKNESS -> "Arrow of Weakness"; + case LUCK -> "Arrow of Luck"; + case TURTLE_MASTER -> "Arrow of the Turtle Master"; + case SLOW_FALLING -> "Arrow of Slow Falling"; + }; } /** @@ -620,22 +610,22 @@ public static String getMusicDiskDesc(Material material, User user) { // The description of the music record is the same in any language, // so directly output it here. - switch (material) { - case MUSIC_DISC_13 : return "C418 - 13"; - case MUSIC_DISC_CAT : return "C418 - cat"; - case MUSIC_DISC_BLOCKS : return "C418 - blocks"; - case MUSIC_DISC_CHIRP : return "C418 - chirp"; - case MUSIC_DISC_FAR : return "C418 - far"; - case MUSIC_DISC_MALL : return "C418 - mall"; - case MUSIC_DISC_MELLOHI : return "C418 - mellohi"; - case MUSIC_DISC_STAL : return "C418 - stal"; - case MUSIC_DISC_STRAD : return "C418 - strad"; - case MUSIC_DISC_WARD : return "C418 - ward"; - case MUSIC_DISC_11 : return "C418 - 11"; - case MUSIC_DISC_WAIT : return "C418 - wait"; - case MUSIC_DISC_PIGSTEP : return "Lena Raine - Pigstep"; - default : return null; - } + return switch (material) { + case MUSIC_DISC_13 -> "C418 - 13"; + case MUSIC_DISC_CAT -> "C418 - cat"; + case MUSIC_DISC_BLOCKS -> "C418 - blocks"; + case MUSIC_DISC_CHIRP -> "C418 - chirp"; + case MUSIC_DISC_FAR -> "C418 - far"; + case MUSIC_DISC_MALL -> "C418 - mall"; + case MUSIC_DISC_MELLOHI -> "C418 - mellohi"; + case MUSIC_DISC_STAL -> "C418 - stal"; + case MUSIC_DISC_STRAD -> "C418 - strad"; + case MUSIC_DISC_WARD -> "C418 - ward"; + case MUSIC_DISC_11 -> "C418 - 11"; + case MUSIC_DISC_WAIT -> "C418 - wait"; + case MUSIC_DISC_PIGSTEP -> "Lena Raine - Pigstep"; + default -> null; + }; } } diff --git a/src/main/java/world/bentobox/bentobox/hooks/package-info.java b/src/main/java/world/bentobox/bentobox/hooks/package-info.java new file mode 100644 index 000000000..86ce95aae --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/hooks/package-info.java @@ -0,0 +1,7 @@ +/** + * This is where hooks into other plugins go. + * + * @author Poslovitch + * + */ +package world.bentobox.bentobox.hooks; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/hooks/placeholders/PlaceholderAPIHook.java b/src/main/java/world/bentobox/bentobox/hooks/placeholders/PlaceholderAPIHook.java index 91177291e..7b3b174fd 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/placeholders/PlaceholderAPIHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/placeholders/PlaceholderAPIHook.java @@ -26,7 +26,7 @@ public class PlaceholderAPIHook extends PlaceholderHook { private BentoBoxPlaceholderExpansion bentoboxExpansion; - private Map addonsExpansions; + private final Map addonsExpansions; private final Set bentoBoxPlaceholders; private final Map> addonPlaceholders; diff --git a/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java b/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java index e1828f93e..c9dc27be1 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java +++ b/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java @@ -19,7 +19,7 @@ */ public class BannedCommands implements Listener { - private BentoBox plugin; + private final BentoBox plugin; /** * @param plugin - plugin diff --git a/src/main/java/world/bentobox/bentobox/listeners/BlockEndDragon.java b/src/main/java/world/bentobox/bentobox/listeners/BlockEndDragon.java index 380264da7..8c219a353 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/BlockEndDragon.java +++ b/src/main/java/world/bentobox/bentobox/listeners/BlockEndDragon.java @@ -18,7 +18,7 @@ public class BlockEndDragon implements Listener { - private BentoBox plugin; + private final BentoBox plugin; public BlockEndDragon(@NonNull BentoBox plugin) { this.plugin = plugin; diff --git a/src/main/java/world/bentobox/bentobox/listeners/DeathListener.java b/src/main/java/world/bentobox/bentobox/listeners/DeathListener.java index 22edcd57c..5cc4c73dc 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/DeathListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/DeathListener.java @@ -15,7 +15,7 @@ */ public class DeathListener implements Listener { - private BentoBox plugin; + private final BentoBox plugin; public DeathListener(@NonNull BentoBox plugin) { super(); diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index bac2aa6b1..e1f28b671 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -32,8 +32,8 @@ public class JoinLeaveListener implements Listener { - private BentoBox plugin; - private PlayersManager players; + private final BentoBox plugin; + private final PlayersManager players; /** * @param plugin - plugin object @@ -80,7 +80,7 @@ public void onPlayerJoin(final PlayerJoinEvent event) { players.setPlayerName(user); players.save(playerUUID); } else { - plugin.logWarning("Player that just logged in has no name! " + playerUUID.toString()); + plugin.logWarning("Player that just logged in has no name! " + playerUUID); } // If mobs have to be removed when a player joins, then wipe all the mobs on his island. @@ -126,9 +126,8 @@ private void firstTime(User user) { // - abort on logout is false // - abort on logout is true && user is online if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()){ - plugin.getIWM().getAddon(w).ifPresent(addon -> addon.getPlayerCommand() - .map(command -> command.getSubCommand("create").orElse(null)) - .ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME)))); + plugin.getIWM().getAddon(w).flatMap(addon -> addon.getPlayerCommand().flatMap(command -> command.getSubCommand("create"))) + .ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME))); } }; diff --git a/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java b/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java index 70d39b6d9..53d402c5e 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java +++ b/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java @@ -1,9 +1,9 @@ package world.bentobox.bentobox.listeners; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; import org.bukkit.ChatColor; import org.bukkit.entity.HumanEntity; @@ -24,7 +24,7 @@ public class PanelListenerManager implements Listener { - private static HashMap openPanels = new HashMap<>(); + private static final HashMap openPanels = new HashMap<>(); @EventHandler(priority = EventPriority.HIGHEST) public void onInventoryClick(InventoryClickEvent event) { @@ -97,8 +97,8 @@ public void onPluginDisable(PluginDisableEvent event) { */ public static void closeAllPanels() { // Use stream clones to avoid concurrent modification exceptions - openPanels.values().stream().collect(Collectors.toList()).forEach(p -> - p.getInventory().getViewers().stream().collect(Collectors.toList()).forEach(HumanEntity::closeInventory)); + new ArrayList<>(openPanels.values()).forEach(p -> + new ArrayList<>(p.getInventory().getViewers()).forEach(HumanEntity::closeInventory)); } /** diff --git a/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java b/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java index ed0cff519..3a8c04cd4 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java +++ b/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java @@ -47,7 +47,7 @@ public PlayerEntityPortalEvent(PlayerPortalEvent ppe) { * @return whether there should create be a destination portal created */ public boolean getCanCreatePortal() { - return epe == null ? ppe.getCanCreatePortal() : false; + return epe == null && ppe.getCanCreatePortal(); } /** diff --git a/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java b/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java index 495d4e1ab..d0e55998d 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java @@ -20,8 +20,8 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEnterEvent; import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPortalEvent; -import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; @@ -43,18 +43,13 @@ public class PortalTeleportationListener implements Listener { private final BentoBox plugin; - private Set inPortal; + private final Set inPortal; + private final Set inTeleport; public PortalTeleportationListener(@NonNull BentoBox plugin) { this.plugin = plugin; inPortal = new HashSet<>(); - } - - - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPlayerTeleport(PlayerTeleportEvent e) { - // Remove player from inPortal after a teleport - inPortal.remove(e.getPlayer().getUniqueId()); + inTeleport = new HashSet<>(); } /** @@ -72,19 +67,13 @@ public void onPlayerPortal(EntityPortalEnterEvent e) { if (inPortal.contains(uuid) || !plugin.getIWM().inWorld(Util.getWorld(e.getLocation().getWorld()))) { return; } + inPortal.add(uuid); if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL)) { - inPortal.add(uuid); // Schedule a time Bukkit.getScheduler().runTaskLater(plugin, () -> { // Check again if still in portal - if (entity.getLocation().getBlock().getType().equals(Material.NETHER_PORTAL)) { - PlayerPortalEvent en = new PlayerPortalEvent((Player)entity, e.getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0); - if (!this.onIslandPortal(en)) { - // Failed - inPortal.remove(uuid); - } - } else { - inPortal.remove(uuid); + if (inPortal.contains(uuid)) { + this.onIslandPortal(new PlayerPortalEvent((Player)entity, e.getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0)); } }, 40); return; @@ -104,7 +93,6 @@ public void onPlayerPortal(EntityPortalEnterEvent e) { /** * Handles non-player portal use. - * Currently disables portal use by entities to prevent dupe glitching. * * @param e - event */ @@ -117,7 +105,7 @@ public boolean onEntityPortal(EntityPortalEvent e) { || m.equals(Material.END_PORTAL) || m.equals(Material.END_GATEWAY)) .findFirst(); - if (!mat.isPresent()) { + if (mat.isEmpty()) { e.setCancelled(true); return false; } else if (mat.get().equals(Material.NETHER_PORTAL)){ @@ -129,21 +117,32 @@ public boolean onEntityPortal(EntityPortalEvent e) { return false; } + /** + * Remove inPortal flag only when player exits the portal + * @param e player move event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onExitPortal(PlayerMoveEvent e) { + if (!inPortal.contains(e.getPlayer().getUniqueId())) { + return; + } + if (e.getTo() != null && !e.getTo().getBlock().getType().equals(Material.NETHER_PORTAL)) { + inPortal.remove(e.getPlayer().getUniqueId()); + inTeleport.remove(e.getPlayer().getUniqueId()); + } + } + /** * Handles nether or end portals * @param e - event */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public boolean onIslandPortal(PlayerPortalEvent e) { - switch (e.getCause()) { - case END_GATEWAY: - case END_PORTAL: - return processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END); - case NETHER_PORTAL: - return processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER); - default: - return false; - } + return switch (e.getCause()) { + case END_GATEWAY, END_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END); + case NETHER_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER); + default -> false; + }; } @@ -154,18 +153,27 @@ public boolean onIslandPortal(PlayerPortalEvent e) { * @return true if portal happens, false if not */ private boolean processPortal(final PlayerEntityPortalEvent e, final Environment env) { - World fromWorld = e.getFrom().getWorld(); World overWorld = Util.getWorld(fromWorld); if (fromWorld == null || !plugin.getIWM().inWorld(overWorld)) { // Do nothing special return false; } - // 1.14.4 requires explicit cancellation to prevent teleporting to the normal nether + if (!isGenerate(overWorld, env)) { e.setCancelled(true); return false; } + + if (!Bukkit.getServer().getAllowNether()) { + e.setCancelled(true); + } + + if (inTeleport.contains(e.getEntity().getUniqueId())) { + return false; + } + inTeleport.add(e.getEntity().getUniqueId()); + // STANDARD NETHER OR END if (!isIslands(overWorld, env)) { handleStandardNetherOrEnd(e, fromWorld, overWorld, env); @@ -198,7 +206,6 @@ && getNetherEndWorld(overWorld, env) != null && e.getIsland().filter(i -> !hasPartnerIsland(i, env)).map(i -> { // No nether island present so paste the default one e.setCancelled(true); - inPortal.remove(e.getEntity().getUniqueId()); pasteNewIsland((Player)e.getEntity(), e.getTo(), i, env); return true; }).orElse(false)) { @@ -207,7 +214,6 @@ && getNetherEndWorld(overWorld, env) != null } if (e.getCanCreatePortal()) { // Let the server teleport - inPortal.remove(e.getEntity().getUniqueId()); return true; } if (env.equals(Environment.THE_END)) { @@ -216,10 +222,10 @@ && getNetherEndWorld(overWorld, env) != null e.getEntity().setFallDistance(0); } - // If we do not generate portals, teleporation should happen manually with safe spot builder. - // Otherwise, we could end up with situations when player is placed in mid air, if teleporation + // If we do not generate portals, teleportation should happen manually with safe spot builder. + // Otherwise, we could end up with situations when player is placed in mid air, if teleportation // is done instantly. - // Our safe spot task is triggered in next tick, however, end teleporation happens in the same tick. + // Our safe spot task is triggered in next tick, however, end teleportation happens in the same tick. // It is placed outside THE_END check, as technically it could happen with the nether portal too. e.setCancelled(true); @@ -232,7 +238,6 @@ && getNetherEndWorld(overWorld, env) != null .location(e.getTo()) .portal() .thenRun(() -> { - inPortal.remove(e.getEntity().getUniqueId()); e.getEntity().setVelocity(new Vector(0,0,0)); e.getEntity().setFallDistance(0); }) @@ -361,7 +366,6 @@ private void handleFromNetherOrEnd(PlayerEntityPortalEvent e, World overWorld, E e.setTo(e.getFrom().toVector().toLocation(overWorld)); // Find distance from edge of island's protection plugin.getIslands().getIslandAt(e.getFrom()).ifPresent(i -> setSeachRadius(e, i)); - inPortal.remove(e.getEntity().getUniqueId()); return; } // Custom portals @@ -374,7 +378,6 @@ private void handleFromNetherOrEnd(PlayerEntityPortalEvent e, World overWorld, E .entity(e.getEntity()) .location(to) .portal() - .thenRun(() -> inPortal.remove(e.getEntity().getUniqueId())) .build(); } @@ -412,7 +415,7 @@ private void handleStandardNetherOrEnd(PlayerEntityPortalEvent e, World fromWorl // From standard nether or end else if (e.getEntity() instanceof Player){ e.setCancelled(true); - plugin.getIslands().homeTeleportAsync(overWorld, (Player)e.getEntity()).thenAccept(b -> inPortal.remove(e.getEntity().getUniqueId())); + plugin.getIslands().homeTeleportAsync(overWorld, (Player)e.getEntity()); } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java b/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java index f07965dfa..737a219a2 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java @@ -111,8 +111,9 @@ public void onBucketEmpty(PlayerBucketEmptyEvent e) { private boolean atSpawn(@NonNull Location location) { Vector p = location.toVector().multiply(new Vector(1, 0, 1)); Vector spawn = location.getWorld().getSpawnLocation().toVector().multiply(new Vector(1, 0, 1)); - int radiusSquared = plugin.getIWM().getNetherSpawnRadius(location.getWorld()) * plugin.getIWM().getNetherSpawnRadius(location.getWorld()); - return (spawn.distanceSquared(p) < radiusSquared); + int radius = plugin.getIWM().getNetherSpawnRadius(location.getWorld()); + Vector diff = p.subtract(spawn); + return Math.abs(diff.getBlockX()) <= radius && Math.abs(diff.getBlockZ()) <= radius; } /** @@ -123,7 +124,7 @@ private boolean atSpawn(@NonNull Location location) { * @return true if nothing needs to be done */ private boolean noAction(@NonNull Player player) { - return (player.isOp() || player.getWorld().getEnvironment().equals(World.Environment.NORMAL) + return (player.isOp() || player.getWorld().getEnvironment().equals(World.Environment.NORMAL) || !plugin.getIWM().inWorld(Util.getWorld(player.getWorld())) || (player.getWorld().getEnvironment().equals(World.Environment.NETHER) && plugin.getIWM().isNetherIslands(player.getWorld())) || (player.getWorld().getEnvironment().equals(World.Environment.THE_END) && plugin.getIWM().isEndIslands(player.getWorld()))); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java b/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java index 4d730fe7d..8db7760e7 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java @@ -17,9 +17,9 @@ */ public class CommandCycleClick implements ClickHandler { - private BentoBox plugin = BentoBox.getInstance(); - private String command; - private CommandRankClickListener commandRankClickListener; + private final BentoBox plugin = BentoBox.getInstance(); + private final String command; + private final CommandRankClickListener commandRankClickListener; public CommandCycleClick(CommandRankClickListener commandRankClickListener, String c) { this.commandRankClickListener = commandRankClickListener; diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandRankClickListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandRankClickListener.java index e98abc574..d5ce0fecc 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandRankClickListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandRankClickListener.java @@ -29,7 +29,7 @@ */ public class CommandRankClickListener implements ClickHandler { - private BentoBox plugin = BentoBox.getInstance(); + private final BentoBox plugin = BentoBox.getInstance(); /* (non-Javadoc) * @see world.bentobox.bentobox.api.panels.PanelItem.ClickHandler#onClick(world.bentobox.bentobox.api.panels.Panel, world.bentobox.bentobox.api.user.User, org.bukkit.event.inventory.ClickType, int) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/package-info.java b/src/main/java/world/bentobox/bentobox/listeners/flags/package-info.java new file mode 100644 index 000000000..6527fd8ba --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/package-info.java @@ -0,0 +1,7 @@ +/** + * Contains listeners specific to settings flags. + * + * @author tastybento + * + */ +package world.bentobox.bentobox.listeners.flags; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index faf4408ad..98a382cec 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -1,7 +1,5 @@ package world.bentobox.bentobox.listeners.flags.protection; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -36,9 +34,7 @@ public class BlockInteractionListener extends FlagListener { */ private final static Map stringFlags; static { - Map f = new HashMap<>(); - f.put("RESPAWN_ANCHOR", "PLACE_BLOCKS"); - stringFlags = Collections.unmodifiableMap(f); + stringFlags = Map.of("RESPAWN_ANCHOR", "PLACE_BLOCKS"); } /** diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java index 35b4bc0ea..391f2605d 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java @@ -46,9 +46,8 @@ public void onBreakHanging(final HangingBreakByEntityEvent e) { checkIsland(e, (Player)e.getRemover(), e.getEntity().getLocation(), Flags.BREAK_BLOCKS); } // Check for projectiles - if (e.getRemover() instanceof Projectile) { + if (e.getRemover() instanceof Projectile p) { // Find out who fired it - Projectile p = (Projectile)e.getRemover(); if (p.getShooter() instanceof Player) { checkIsland(e, (Player)p.getShooter(), e.getEntity().getLocation(), Flags.BREAK_BLOCKS); } @@ -118,9 +117,8 @@ public void onEntityDamage(EntityDamageByEntityEvent e) { if (e.getDamager() instanceof Player) { // Check the break blocks flag notAllowed(e, (Player)e.getDamager(), e.getEntity().getLocation()); - } else if (e.getDamager() instanceof Projectile) { + } else if (e.getDamager() instanceof Projectile p) { // Find out who fired the arrow - Projectile p = (Projectile) e.getDamager(); if (p.getShooter() instanceof Player && notAllowed(e, (Player)p.getShooter(), e.getEntity().getLocation())) { e.getEntity().setFireTicks(0); p.setFireTicks(0); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java index d509d9627..1548535b2 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java @@ -73,8 +73,7 @@ public class BreedingListener extends FlagListener { @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true) public void onPlayerInteract(final PlayerInteractAtEntityEvent e) { Player p = e.getPlayer(); - if (e.getRightClicked() instanceof Animals && BREEDING_ITEMS.containsKey(e.getRightClicked().getType())) { - Animals animal = (Animals) e.getRightClicked(); + if (e.getRightClicked() instanceof Animals animal && BREEDING_ITEMS.containsKey(e.getRightClicked().getType())) { ItemStack inHand = p.getInventory().getItemInMainHand(); if (e.getHand().equals(EquipmentSlot.OFF_HAND)) { inHand = p.getInventory().getItemInOffHand(); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java index 3dd360185..02426ea85 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java @@ -40,18 +40,18 @@ public void onBucketEmpty(final PlayerBucketEmptyEvent e) { public void onBucketFill(final PlayerBucketFillEvent e) { // Check filling of various liquids switch (e.getItemStack().getType()) { - case LAVA_BUCKET: - checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.COLLECT_LAVA); - return; - case WATER_BUCKET: - checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.COLLECT_WATER); - return; - case MILK_BUCKET: - checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.MILKING); - return; - default: - // Check general bucket use - checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.BUCKET); + case LAVA_BUCKET -> { + checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.COLLECT_LAVA); + } + case WATER_BUCKET -> { + checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.COLLECT_WATER); + } + case MILK_BUCKET -> { + checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.MILKING); + } + default -> + // Check general bucket use + checkIsland(e, e.getPlayer(), e.getBlockClicked().getLocation(), Flags.BUCKET); } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/ElytraListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/ElytraListener.java index 9338e5a9c..f3d4b585c 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/ElytraListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/ElytraListener.java @@ -18,8 +18,7 @@ public class ElytraListener extends FlagListener { */ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onGlide(EntityToggleGlideEvent e) { - if (e.getEntity() instanceof Player) { - Player player = (Player) e.getEntity(); + if (e.getEntity() instanceof Player player) { if (!checkIsland(e, player, player.getLocation(), Flags.ELYTRA)) { player.setGliding(false); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java index d0e060912..887c278ef 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java @@ -41,8 +41,8 @@ */ public class HurtingListener extends FlagListener { - private Map thrownPotions = new HashMap<>(); - private Map firedFireworks = new WeakHashMap<>(); + private final Map thrownPotions = new HashMap<>(); + private final Map firedFireworks = new WeakHashMap<>(); /** * Handles mob and monster protection @@ -71,9 +71,8 @@ private void respond(EntityDamageByEntityEvent e, Entity damager, Flag flag) { // Get the attacker if (damager instanceof Player) { checkIsland(e, (Player)damager, damager.getLocation(), flag); - } else if (damager instanceof Projectile) { + } else if (damager instanceof Projectile p) { // Find out who fired the projectile - Projectile p = (Projectile) damager; if (p.getShooter() instanceof Player && !checkIsland(e, (Player)p.getShooter(), damager.getLocation(), flag)) { e.getEntity().setFireTicks(0); } @@ -123,8 +122,7 @@ public void onPlayerFeedParrots(PlayerInteractEntityEvent e) { public void onSplashPotionSplash(final PotionSplashEvent e) { // Try to get the shooter Projectile projectile = e.getEntity(); - if (projectile.getShooter() instanceof Player) { - Player attacker = (Player)projectile.getShooter(); + if (projectile.getShooter() instanceof Player attacker) { // Run through all the affected entities for (LivingEntity entity: e.getAffectedEntities()) { // Self damage diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java index f9e5275be..250969322 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java @@ -72,9 +72,8 @@ else if (inventoryHolder instanceof Barrel) { else if (inventoryHolder instanceof ShulkerBox) { checkIsland(e, player, e.getInventory().getLocation(), Flags.SHULKER_BOX); } - else if (inventoryHolder instanceof Chest) { + else if (inventoryHolder instanceof Chest chestInventoryHolder) { // To differentiate between a Chest and a Trapped Chest we need to get the Block corresponding to the inventory - Chest chestInventoryHolder = (Chest) inventoryHolder; try { if (chestInventoryHolder.getType() == Material.TRAPPED_CHEST) { checkIsland(e, player, e.getInventory().getLocation(), Flags.TRAPPED_CHEST); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java index d29e4330b..26ef9d7d8 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java @@ -53,10 +53,9 @@ public void onPlayerInteract(PlayerInteractEvent e) { */ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onProjectileHit(EntityInteractEvent e) { - if (!(e.getEntity() instanceof Projectile)) { + if (!(e.getEntity() instanceof Projectile p)) { return; } - Projectile p = (Projectile)e.getEntity(); if (p.getShooter() instanceof Player) { if (Tag.WOODEN_BUTTONS.isTagged(e.getBlock().getType())) { checkIsland(e, (Player)p.getShooter(), e.getBlock().getLocation(), Flags.BUTTON); @@ -71,23 +70,10 @@ public void onProjectileHit(EntityInteractEvent e) { } private boolean isPressurePlate(Material material) { - switch(material) { - case STONE_PRESSURE_PLATE: - case POLISHED_BLACKSTONE_PRESSURE_PLATE: - case ACACIA_PRESSURE_PLATE: - case BIRCH_PRESSURE_PLATE: - case CRIMSON_PRESSURE_PLATE: - case DARK_OAK_PRESSURE_PLATE: - case HEAVY_WEIGHTED_PRESSURE_PLATE: - case JUNGLE_PRESSURE_PLATE: - case LIGHT_WEIGHTED_PRESSURE_PLATE: - case OAK_PRESSURE_PLATE: - case SPRUCE_PRESSURE_PLATE: - case WARPED_PRESSURE_PLATE: - return true; - default: - return false; - } + return switch (material) { + case STONE_PRESSURE_PLATE, POLISHED_BLACKSTONE_PRESSURE_PLATE, ACACIA_PRESSURE_PLATE, BIRCH_PRESSURE_PLATE, CRIMSON_PRESSURE_PLATE, DARK_OAK_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE, JUNGLE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, OAK_PRESSURE_PLATE, SPRUCE_PRESSURE_PLATE, WARPED_PRESSURE_PLATE -> true; + default -> false; + }; } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java index 514d255bf..1d1b43a2e 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java @@ -1,7 +1,5 @@ package world.bentobox.bentobox.listeners.flags.protection; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.bukkit.Location; @@ -31,13 +29,13 @@ public class TNTListener extends FlagListener { * Contains {@link EntityType}s that generates an explosion. * @since 1.5.0 */ - private static final List TNT_TYPES = Collections.unmodifiableList(Arrays.asList(EntityType.PRIMED_TNT, EntityType.MINECART_TNT)); + private static final List TNT_TYPES = List.of(EntityType.PRIMED_TNT, EntityType.MINECART_TNT); /** * Contains {@link Material}s that can be used to prime a TNT. * @since 1.5.0 */ - private static final List PRIMING_ITEMS = Collections.unmodifiableList(Arrays.asList(Material.FLINT_AND_STEEL, Material.FIRE_CHARGE)); + private static final List PRIMING_ITEMS = List.of(Material.FLINT_AND_STEEL, Material.FIRE_CHARGE); /** * Protect TNT from being set light by a fire arrow @@ -50,8 +48,7 @@ public boolean onTNTDamage(EntityChangeBlockEvent e) { return false; } // Stop TNT from being damaged if it is being caused by a visitor with a flaming arrow - if (e.getEntity() instanceof Projectile) { - Projectile projectile = (Projectile) e.getEntity(); + if (e.getEntity() instanceof Projectile projectile) { // Find out who fired it if (projectile.getShooter() instanceof Player && projectile.getFireTicks() > 0 && !checkIsland(e, (Player)projectile.getShooter(), e.getBlock().getLocation(), Flags.TNT_PRIMING)) { diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java index 0a39e2ad7..2cc2fee54 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java @@ -2,11 +2,12 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.WeakHashMap; import org.bukkit.Bukkit; -import org.bukkit.World; +import org.bukkit.Sound; import org.bukkit.entity.Entity; import org.bukkit.entity.Firework; import org.bukkit.entity.LivingEntity; @@ -23,6 +24,7 @@ import org.bukkit.event.entity.LingeringPotionSplashEvent; import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerTeleportEvent; import world.bentobox.bentobox.api.events.flags.FlagSettingChangeEvent; import world.bentobox.bentobox.api.flags.Flag; @@ -38,8 +40,8 @@ */ public class PVPListener extends FlagListener { - private Map thrownPotions = new HashMap<>(); - private Map firedFireworks = new WeakHashMap<>(); + private final Map thrownPotions = new HashMap<>(); + private final Map firedFireworks = new WeakHashMap<>(); /** * This method protects players from PVP if it is not allowed and from @@ -54,6 +56,10 @@ public void onEntityDamage(EntityDamageByEntityEvent e) { if (e.getEntity().equals(e.getDamager()) || e.getEntity().hasMetadata("NPC")) { return; } + // Is PVP allowed here? + if (this.PVPAllowed(e.getEntity().getLocation())) { + return; + } // Protect visitors if (e.getCause().equals(DamageCause.ENTITY_ATTACK) && protectedVisitor((Player)e.getEntity())) { if (e.getDamager() instanceof Player) { @@ -83,9 +89,8 @@ private void respond(Cancellable e, Entity damager, Entity hurtEntity, Flag flag user.notify(getFlag(damager.getWorld()).getHintReference()); e.setCancelled(true); } - } else if (damager instanceof Projectile && ((Projectile)damager).getShooter() instanceof Player) { + } else if (damager instanceof Projectile p && ((Projectile)damager).getShooter() instanceof Player) { // Find out who fired the arrow - Projectile p = (Projectile) damager; Player shooter =(Player)p.getShooter(); processDamage(e, damager, shooter, hurtEntity, flag); } else if (damager instanceof Firework && firedFireworks.containsKey(damager)) { @@ -116,6 +121,10 @@ public void onFishing(PlayerFishEvent e) { if (e.getCaught().equals(e.getPlayer()) || e.getCaught().hasMetadata("NPC")) { return; } + // Is PVP allowed here? + if (this.PVPAllowed(e.getCaught().getLocation())) { + return; + } // Protect visitors if (protectedVisitor((Player)e.getCaught())) { User.getInstance(e.getPlayer()).notify(Flags.INVINCIBLE_VISITORS.getHintReference()); @@ -136,6 +145,10 @@ public void onFishing(PlayerFishEvent e) { public void onSplashPotionSplash(final PotionSplashEvent e) { if (e.getEntity().getShooter() instanceof Player && getPlugin().getIWM().inWorld(e.getEntity().getWorld())) { User user = User.getInstance((Player)e.getEntity().getShooter()); + // Is PVP allowed here? + if (this.PVPAllowed(e.getEntity().getLocation())) { + return; + } // Run through affected entities and cancel the splash for protected players for (LivingEntity le : e.getAffectedEntities()) { if (!le.getUniqueId().equals(user.getUniqueId()) && blockPVP(user, le, e, getFlag(e.getEntity().getWorld()))) { @@ -197,17 +210,6 @@ public void onLingeringPotionDamage(AreaEffectCloudApplyEvent e) { } } - private Flag getFlag(World w) { - switch (w.getEnvironment()) { - case NETHER: - return Flags.PVP_NETHER; - case THE_END: - return Flags.PVP_END; - default: - return Flags.PVP_OVERWORLD; - } - } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true) public void onPlayerShootFireworkEvent(final EntityShootBowEvent e) { // Only care about players shooting fireworks @@ -228,4 +230,35 @@ public void onPVPFlagToggle(final FlagSettingChangeEvent e) { e.getIsland().getMemberSet(RanksManager.COOP_RANK).forEach(member -> User.getInstance(member).sendMessage(message)); } } + + /** + * Warn visitors if the island they are teleporting to has PVP on + * @param e teleport event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true) + public void onPlayerTeleport(PlayerTeleportEvent e) { + if (e.getTo() == null) { + return; + } + getIslands().getIslandAt(e.getTo()).ifPresent(island -> { + if (island.getMemberSet(RanksManager.COOP_RANK).contains(e.getPlayer().getUniqueId())) { + return; + } + if (island.isAllowed(Flags.PVP_OVERWORLD)) { + alertUser(e.getPlayer(), Flags.PVP_OVERWORLD); + } + if (island.isAllowed(Flags.PVP_NETHER)) { + alertUser(e.getPlayer(), Flags.PVP_NETHER); + } + if (island.isAllowed(Flags.PVP_END)) { + alertUser(e.getPlayer(), Flags.PVP_END); + } + }); + } + + private void alertUser(Player player, Flag flag) { + String message = "protection.flags." + flag.getID() + ".enabled"; + Objects.requireNonNull(User.getInstance(player)).sendMessage(message); + player.playSound(player.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER,2F, 1F); + } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CleanSuperFlatListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CleanSuperFlatListener.java index 0a4390815..ed28ece90 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CleanSuperFlatListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CleanSuperFlatListener.java @@ -31,14 +31,14 @@ */ public class CleanSuperFlatListener extends FlagListener { - private BentoBox plugin = BentoBox.getInstance(); + private final BentoBox plugin = BentoBox.getInstance(); /** * Stores pairs of X,Z coordinates of chunks that need to be regenerated. * @since 1.1 */ @NonNull - private Queue<@NonNull Pair<@NonNull Integer, @NonNull Integer>> chunkQueue = new LinkedList<>(); + private final Queue<@NonNull Pair<@NonNull Integer, @NonNull Integer>> chunkQueue = new LinkedList<>(); /** * Task that runs each tick to regenerate chunks that are in the {@link #chunkQueue}. diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java index e70571b83..814c982ed 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java @@ -41,8 +41,7 @@ public void onExplosion(final EntityExplodeEvent e) { } // Check for griefing Creeper creeper = (Creeper)e.getEntity(); - if (!Flags.CREEPER_GRIEFING.isSetForWorld(e.getLocation().getWorld()) && creeper.getTarget() instanceof Player) { - Player target = (Player)creeper.getTarget(); + if (!Flags.CREEPER_GRIEFING.isSetForWorld(e.getLocation().getWorld()) && creeper.getTarget() instanceof Player target) { if (!getIslands().locationIsOnIsland(target, e.getLocation())) { User user = User.getInstance(target); user.notify("protection.protected", TextVariables.DESCRIPTION, user.getTranslation(Flags.CREEPER_GRIEFING.getHintReference())); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/GeoLimitMobsListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/GeoLimitMobsListener.java index bded3d444..ae536c2f7 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/GeoLimitMobsListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/GeoLimitMobsListener.java @@ -24,7 +24,7 @@ */ public class GeoLimitMobsListener extends FlagListener { - private Map mobSpawnTracker = new WeakHashMap<>(); + private final Map mobSpawnTracker = new WeakHashMap<>(); /** * Start the tracker when the plugin is loaded @@ -69,8 +69,7 @@ public void onMobDeath(final EntityDeathEvent e) { public void onProjectileExplode(final ExplosionPrimeEvent e) { if (e.getEntity() instanceof Projectile && getIWM().inWorld(e.getEntity().getLocation())) { ProjectileSource source = ((Projectile)e.getEntity()).getShooter(); - if (source instanceof Entity) { - Entity shooter = (Entity)source; + if (source instanceof Entity shooter) { if (mobSpawnTracker.containsKey(shooter) && !mobSpawnTracker.get(shooter).onIsland(e.getEntity().getLocation())) { e.getEntity().remove(); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListener.java index 87bbb4379..d8890723a 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListener.java @@ -124,16 +124,17 @@ private String getTranslation(User user, String name) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onVisitorGetDamage(EntityDamageEvent e) { World world = e.getEntity().getWorld(); - if (!(e.getEntity() instanceof Player) + if (!(e.getEntity() instanceof Player p) || !getIWM().inWorld(world) || e.getEntity().hasMetadata("NPC") || !getIWM().getIvSettings(world).contains(e.getCause().name()) - || getIslands().userIsOnIsland(world, User.getInstance(e.getEntity()))) { + || getIslands().userIsOnIsland(world, User.getInstance(e.getEntity())) + || PVPAllowed(p.getLocation()) + ) { return; } // Player is a visitor and should be protected from damage e.setCancelled(true); - Player p = (Player) e.getEntity(); // Handle the void - teleport player back to island in a safe spot if(e.getCause().equals(DamageCause.VOID)) { if (getIslands().getIslandAt(p.getLocation()).isPresent()) { diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/LiquidsFlowingOutListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/LiquidsFlowingOutListener.java index 9cb5b6e3a..3c533adbf 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/LiquidsFlowingOutListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/LiquidsFlowingOutListener.java @@ -38,7 +38,7 @@ public void onLiquidFlow(BlockFromToEvent e) { // Only prevent if it is flowing into the area between islands or into another island. Optional fromIsland = getIslands().getProtectedIslandAt(from.getLocation()); Optional toIsland = getIslands().getProtectedIslandAt(to.getLocation()); - if (!toIsland.isPresent() || (fromIsland.isPresent() && !fromIsland.equals(toIsland))) { + if (toIsland.isEmpty() || (fromIsland.isPresent() && !fromIsland.equals(toIsland))) { e.setCancelled(true); } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/NaturalSpawningOutsideRangeListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/NaturalSpawningOutsideRangeListener.java index 769078eae..ade03ed48 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/NaturalSpawningOutsideRangeListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/NaturalSpawningOutsideRangeListener.java @@ -23,7 +23,7 @@ public void onCreatureSpawn(CreatureSpawnEvent e) { } // If it is a natural spawn and there is no protected island at the location, block the spawn. - if (e.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NATURAL && !getIslands().getProtectedIslandAt(e.getLocation()).isPresent()) { + if (e.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NATURAL && getIslands().getProtectedIslandAt(e.getLocation()).isEmpty()) { e.setCancelled(true); } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java index bcff672a1..b312edab4 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java @@ -5,6 +5,7 @@ import java.util.List; import org.bukkit.Bukkit; +import org.bukkit.FluidCollisionMode; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Sound; @@ -51,7 +52,13 @@ public boolean onPlayerInteract(final PlayerInteractEvent e) { private boolean lookForLava(PlayerInteractEvent e) { Player player = e.getPlayer(); ItemStack bucket = e.getItem(); - Block b = e.getClickedBlock(); + + // Get block player is looking at + Block b = e.getPlayer().rayTraceBlocks(5, FluidCollisionMode.ALWAYS).getHitBlock(); + if (!b.getType().equals(Material.OBSIDIAN)) { + // This should not be needed but might catch some attempts + return false; + } User user = User.getInstance(player); if (getIslands().userIsOnIsland(user.getWorld(), user)) { // Look around to see if this is a lone obsidian block @@ -61,15 +68,14 @@ private boolean lookForLava(PlayerInteractEvent e) { } user.sendMessage("protection.flags.OBSIDIAN_SCOOPING.scooping"); player.getWorld().playSound(player.getLocation(), Sound.ITEM_BUCKET_FILL_LAVA, 1F, 1F); - b.setType(Material.AIR); e.setCancelled(true); - Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> givePlayerLava(player, bucket)); + Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> givePlayerLava(player, b, bucket)); return true; } return false; } - private void givePlayerLava(Player player, ItemStack bucket) { + private void givePlayerLava(Player player, Block b, ItemStack bucket) { if (bucket.getAmount() == 1) { // Needs some special handling when there is only 1 bucket in the stack bucket.setType(Material.LAVA_BUCKET); @@ -81,6 +87,8 @@ private void givePlayerLava(Player player, ItemStack bucket) { map.values().forEach(i -> player.getWorld().dropItem(player.getLocation(), i)); } } + // Set block to air only after giving bucket + b.setType(Material.AIR); } private List getBlocksAround(Block b) { diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java index 379c44de3..ced124ed8 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java @@ -25,9 +25,8 @@ public void onPetTeleport(final EntityTeleportEvent e) { if (e.getTo() == null || !getIWM().inWorld(e.getFrom()) || !Flags.PETS_STAY_AT_HOME.isSetForWorld(e.getFrom().getWorld()) - || !(e.getEntity() instanceof Tameable) + || !(e.getEntity() instanceof Tameable t) ) return; - Tameable t = (Tameable)e.getEntity(); if (t.isTamed() && t.getOwner() != null) { // Get where the pet is going e.setCancelled(getIslands().getProtectedIslandAt(e.getTo()) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/TreesGrowingOutsideRangeListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/TreesGrowingOutsideRangeListener.java index e927acd07..9f77ec728 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/TreesGrowingOutsideRangeListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/TreesGrowingOutsideRangeListener.java @@ -24,14 +24,14 @@ public void onTreeGrow(StructureGrowEvent e) { } // If there is no protected island at the location of the sapling, just cancel the event (prevents the sapling from growing). - if (!getIslands().getProtectedIslandAt(e.getLocation()).isPresent()) { + if (getIslands().getProtectedIslandAt(e.getLocation()).isEmpty()) { e.setCancelled(true); return; } // Now, run through all the blocks that will be generated and if there is no protected island at their location, turn them into AIR. e.getBlocks().stream() - .filter(blockState -> !getIslands().getProtectedIslandAt(blockState.getLocation()).isPresent()) + .filter(blockState -> getIslands().getProtectedIslandAt(blockState.getLocation()).isEmpty()) .forEach(blockState -> blockState.setType(Material.AIR)); } @@ -45,13 +45,13 @@ public void onChorusGrow(BlockSpreadEvent e) { } // If there is no protected island at the location of the chorus flower, just cancel the event (prevents the flower from growing). - if (!getIslands().getProtectedIslandAt(e.getSource().getLocation()).isPresent()) { + if (getIslands().getProtectedIslandAt(e.getSource().getLocation()).isEmpty()) { e.setCancelled(true); return; } // Now prevent the flower to grow if this is growing outside the island - if (!getIslands().getProtectedIslandAt(e.getBlock().getLocation()).isPresent()) { + if (getIslands().getProtectedIslandAt(e.getBlock().getLocation()).isEmpty()) { e.setCancelled(true); } } diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 057082afe..ec832e32c 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -422,7 +422,7 @@ private Flags() {} .listener(new PistonPushListener()) .build(); - private static InvincibleVisitorsListener ilv = new InvincibleVisitorsListener(); + private static final InvincibleVisitorsListener ilv = new InvincibleVisitorsListener(); public static final Flag INVINCIBLE_VISITORS = new Flag.Builder("INVINCIBLE_VISITORS", Material.DIAMOND_CHESTPLATE).type(Type.WORLD_SETTING) .listener(ilv).clickHandler(ilv).usePanel(true).build(); diff --git a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java index 6378569dd..13bf14790 100644 --- a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java +++ b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java @@ -319,11 +319,11 @@ public enum GameModePlaceholder { */ OWNS_ISLAND("owns_island", (addon, user, island) -> String.valueOf(island != null && user != null && user.getUniqueId().equals(island.getOwner()))); - private String placeholder; + private final String placeholder; /** * @since 1.5.0 */ - private GameModePlaceholderReplacer replacer; + private final GameModePlaceholderReplacer replacer; GameModePlaceholder(String placeholder, GameModePlaceholderReplacer replacer) { this.placeholder = placeholder; diff --git a/src/main/java/world/bentobox/bentobox/lists/package-info.java b/src/main/java/world/bentobox/bentobox/lists/package-info.java new file mode 100644 index 000000000..d50fe1765 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/lists/package-info.java @@ -0,0 +1,7 @@ +/** + * This is where lists of things are put. For example Flags. + * + * @author tastybento, Poslovitch + * + */ +package world.bentobox.bentobox.lists; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java b/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java index 9e0ae0d45..b7853161d 100644 --- a/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java @@ -62,14 +62,18 @@ public class AddonsManager { private static final String GAMEMODE = "[gamemode]."; @NonNull - private List addons; + private final List addons; @NonNull - private Map<@NonNull Addon, @Nullable AddonClassLoader> loaders; + private final Map<@NonNull Addon, @Nullable AddonClassLoader> loaders; + @NonNull + private final Map<@NonNull Addon, @Nullable Plugin> pladdons; @NonNull private final Map> classes; - private BentoBox plugin; - private @NonNull Map<@NonNull String, @Nullable GameModeAddon> worldNames; - private @NonNull Map<@NonNull Addon, @NonNull List> listeners; + private final BentoBox plugin; + private @NonNull + final Map<@NonNull String, @Nullable GameModeAddon> worldNames; + private @NonNull + final Map<@NonNull Addon, @NonNull List> listeners; private final PluginLoader pluginLoader; @@ -77,6 +81,7 @@ public AddonsManager(@NonNull BentoBox plugin) { this.plugin = plugin; addons = new ArrayList<>(); loaders = new HashMap<>(); + pladdons = new HashMap<>(); classes = new HashMap<>(); listeners = new HashMap<>(); worldNames = new HashMap<>(); @@ -159,6 +164,9 @@ private void loadAddon(@NonNull File f) { if (pladdon instanceof Pladdon) { addon = ((Pladdon) pladdon).getAddon(); addon.setDescription(AddonClassLoader.asDescription(data)); + // Mark pladdon as enabled. + ((Pladdon) pladdon).setEnabled(); + pladdons.put(addon, pladdon); } else { plugin.logError("Could not load pladdon!"); return; @@ -211,8 +219,7 @@ private void initializeAddon(Addon addon) { // Run the onLoad. addon.onLoad(); // if game mode, get the world name and generate - if (addon instanceof GameModeAddon && !addon.getState().equals(State.DISABLED)) { - GameModeAddon gameMode = (GameModeAddon) addon; + if (addon instanceof GameModeAddon gameMode && !addon.getState().equals(State.DISABLED)) { if (!gameMode.getWorldSettings().getWorldName().isEmpty()) { worldNames.put(gameMode.getWorldSettings().getWorldName().toLowerCase(Locale.ENGLISH), gameMode); } @@ -282,8 +289,7 @@ private void enableAddon(Addon addon) { plugin.log("Enabling " + addon.getDescription().getName() + " (" + addon.getDescription().getVersion() + ")..."); try { // If this is a GameModeAddon create the worlds, register it and load the blueprints - if (addon instanceof GameModeAddon) { - GameModeAddon gameMode = (GameModeAddon) addon; + if (addon instanceof GameModeAddon gameMode) { // Create the gameWorlds gameMode.createWorlds(); plugin.getIWM().addGameMode(gameMode); @@ -296,8 +302,7 @@ private void enableAddon(Addon addon) { plugin.log(addon.getDescription().getName() + " is disabled."); return; } - if (addon instanceof GameModeAddon) { - GameModeAddon gameMode = (GameModeAddon) addon; + if (addon instanceof GameModeAddon gameMode) { // Set the worlds for the commands gameMode.getPlayerCommand().ifPresent(c -> c.setWorld(gameMode.getOverWorld())); gameMode.getAdminCommand().ifPresent(c -> c.setWorld(gameMode.getOverWorld())); @@ -327,7 +332,7 @@ private void handleAddonIncompatibility(@NonNull Addon addon, LinkageError e) { plugin.logWarning("NOTE: DO NOT report this as a bug from BentoBox."); StringBuilder a = new StringBuilder(); addon.getDescription().getAuthors().forEach(author -> a.append(author).append(" ")); - plugin.getLogger().log(Level.SEVERE, "Please report this stack trace to the addon's author(s): " + a.toString(), e); + plugin.getLogger().log(Level.SEVERE, "Please report this stack trace to the addon's author(s): " + a, e); } @@ -404,6 +409,7 @@ public void disableAddons() { plugin.getCommandsManager().unregisterCommands(); // Clear all maps listeners.clear(); + pladdons.clear(); addons.clear(); loaders.clear(); classes.clear(); @@ -605,7 +611,7 @@ private void disable(@NonNull Addon addon) { try { addon.onDisable(); } catch (Exception e) { - plugin.logError("Error occured when disabling addon " + addon.getDescription().getName()); + plugin.logError("Error occurred when disabling addon " + addon.getDescription().getName()); plugin.logError("Report this to the addon's author(s)"); addon.getDescription().getAuthors().forEach(plugin::logError); plugin.logStacktrace(e); @@ -621,7 +627,11 @@ private void disable(@NonNull Addon addon) { addon.setState(State.DISABLED); loaders.remove(addon); } - + // Disable pladdons + if (pladdons.containsKey(addon)) { + this.pluginLoader.disablePlugin(Objects.requireNonNull(this.pladdons.get(addon))); + pladdons.remove(addon); + } // Remove it from the addons list addons.remove(addon); } diff --git a/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java b/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java index a14612107..2c852c593 100644 --- a/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java @@ -35,13 +35,13 @@ public class BlueprintClipboardManager { private static final String LOAD_ERROR = "Could not load blueprint file - does not exist : "; - private File blueprintFolder; + private final File blueprintFolder; private BlueprintClipboard clipboard; private Gson gson; - private BentoBox plugin; + private final BentoBox plugin; public BlueprintClipboardManager(BentoBox plugin, File blueprintFolder) { this(plugin, blueprintFolder, null); @@ -77,7 +77,7 @@ private void getGson() { /** * Load a file to clipboard * @param fileName - filename in blueprints folder - * @throws IOException - if there's a load error with unziping or name + * @throws IOException - if there's a load error with unzipping or name */ public void load(String fileName) throws IOException { clipboard = new BlueprintClipboard(loadBlueprint(fileName)); diff --git a/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java b/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java index 8d7978686..6ef338ee2 100644 --- a/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java @@ -67,13 +67,15 @@ public class BlueprintsManager { * Inner map's key is the uniqueId of the blueprint bundle so it's * easy to get from a UI */ - private @NonNull Map> blueprintBundles; + private @NonNull + final Map> blueprintBundles; /** * Map of blueprints. There can be many blueprints per game mode addon * Inner map's key is the blueprint's name so it's easy to get from a UI */ - private @NonNull Map> blueprints; + private @NonNull + final Map> blueprints; /** * Gson used for serializing/deserializing the bundle class @@ -82,7 +84,8 @@ public class BlueprintsManager { private final @NonNull BentoBox plugin; - private @NonNull Set blueprintsLoaded; + private @NonNull + final Set blueprintsLoaded; public BlueprintsManager(@NonNull BentoBox plugin) { diff --git a/src/main/java/world/bentobox/bentobox/managers/CommandsManager.java b/src/main/java/world/bentobox/bentobox/managers/CommandsManager.java index 73e9297a9..571ed90bc 100644 --- a/src/main/java/world/bentobox/bentobox/managers/CommandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/CommandsManager.java @@ -18,7 +18,7 @@ public class CommandsManager { @NonNull - private Map<@NonNull String, @NonNull CompositeCommand> commands = new HashMap<>(); + private final Map<@NonNull String, @NonNull CompositeCommand> commands = new HashMap<>(); private SimpleCommandMap commandMap; public void registerCommand(@NonNull CompositeCommand command) { diff --git a/src/main/java/world/bentobox/bentobox/managers/FlagsManager.java b/src/main/java/world/bentobox/bentobox/managers/FlagsManager.java index 49bcd4d85..1c35ab0a7 100644 --- a/src/main/java/world/bentobox/bentobox/managers/FlagsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/FlagsManager.java @@ -23,8 +23,9 @@ */ public class FlagsManager { - private @NonNull BentoBox plugin; - private Map<@NonNull Flag, @Nullable Addon> flags = new HashMap<>(); + private @NonNull + final BentoBox plugin; + private final Map<@NonNull Flag, @Nullable Addon> flags = new HashMap<>(); /** * Stores the flag listeners that have already been registered into Bukkit's API to avoid duplicates. @@ -32,7 +33,7 @@ public class FlagsManager { * This helps to make sure each flag listener is loaded correctly. * @see #registerListeners() */ - private Map<@NonNull Listener, @NonNull Boolean> registeredListeners = new HashMap<>(); + private final Map<@NonNull Listener, @NonNull Boolean> registeredListeners = new HashMap<>(); public FlagsManager(@NonNull BentoBox plugin) { this.plugin = plugin; diff --git a/src/main/java/world/bentobox/bentobox/managers/GameModePlaceholderManager.java b/src/main/java/world/bentobox/bentobox/managers/GameModePlaceholderManager.java index 3bab214d4..ba044cd1f 100644 --- a/src/main/java/world/bentobox/bentobox/managers/GameModePlaceholderManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/GameModePlaceholderManager.java @@ -19,7 +19,7 @@ @Deprecated public class GameModePlaceholderManager { - private BentoBox plugin; + private final BentoBox plugin; public GameModePlaceholderManager(BentoBox plugin) { this.plugin = plugin; diff --git a/src/main/java/world/bentobox/bentobox/managers/HooksManager.java b/src/main/java/world/bentobox/bentobox/managers/HooksManager.java index 9aa59843b..409fa73a5 100644 --- a/src/main/java/world/bentobox/bentobox/managers/HooksManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/HooksManager.java @@ -14,11 +14,11 @@ */ public class HooksManager { - private BentoBox plugin; + private final BentoBox plugin; /** * List of successfully registered hooks. */ - private List hooks; + private final List hooks; public HooksManager(BentoBox plugin) { this.plugin = plugin; diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java index 9f7e959eb..99206a296 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java @@ -26,12 +26,12 @@ */ public class IslandDeletionManager implements Listener { - private BentoBox plugin; + private final BentoBox plugin; /** * Queue of islands to delete */ - private Database handler; - private Set inDeletion; + private final Database handler; + private final Set inDeletion; public IslandDeletionManager(BentoBox plugin) { this.plugin = plugin; @@ -52,7 +52,7 @@ public void onBentoBoxReady(BentoBoxReadyEvent e) { plugin.log("There are " + toBeDeleted.size() + " islands pending deletion."); toBeDeleted.forEach(di -> { if (di.getLocation() == null || di.getLocation().getWorld() == null) { - plugin.logError("Island queued for deletion refers to a non-existant game world. Skipping..."); + plugin.logError("Island queued for deletion refers to a non-existent game world. Skipping..."); toBeRemoved.add(di); } else { plugin.log("Resuming deletion of island at " + di.getLocation().getWorld().getName() + " " + Util.xyz(di.getLocation().toVector())); diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java index 18de91cb6..10a2f363a 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java @@ -34,11 +34,11 @@ */ public class IslandWorldManager { - private BentoBox plugin; + private final BentoBox plugin; /** * Map associating Worlds (Overworld, Nether and End) with the GameModeAddon that creates them. */ - private Map<@NonNull World, @NonNull GameModeAddon> gameModes; + private final Map<@NonNull World, @NonNull GameModeAddon> gameModes; /** * Manages worlds registered with BentoBox diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index bc0bfb1d8..b7c433759 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -66,7 +66,7 @@ */ public class IslandsManager { - private BentoBox plugin; + private final BentoBox plugin; // Tree species to boat material map private static final Map TREE_TO_BOAT = ImmutableMap.builder(). @@ -81,7 +81,7 @@ public class IslandsManager { * One island can be spawn, this is the one - otherwise, this value is null */ @NonNull - private Map<@NonNull World, @Nullable Island> spawn; + private final Map<@NonNull World, @Nullable Island> spawn; @NonNull private Database handler; @@ -90,20 +90,22 @@ public class IslandsManager { * The last locations where an island were put. * This is not stored persistently and resets when the server starts */ - private Map last; + private final Map last; // Island Cache @NonNull private IslandCache islandCache; // Quarantined islands @NonNull - private Map> quarantineCache; + private final Map> quarantineCache; // Deleted islands @NonNull - private List deletedIslands; + private final List deletedIslands; private boolean isSaveTaskRunning; + private final Set goingHome; + /** * Islands Manager * @param plugin - plugin @@ -119,6 +121,8 @@ public IslandsManager(@NonNull BentoBox plugin){ // This list should always be empty unless database deletion failed // In that case a purge utility may be required in the future deletedIslands = new ArrayList<>(); + // Mid-teleport players going home + goingHome = new HashSet<>(); } /** @@ -144,7 +148,7 @@ public Location bigScan(@NonNull Location l, int i) { depth = i; } else { Optional island = getIslandAt(l); - if (!island.isPresent()) { + if (island.isEmpty()) { return null; } i = island.get().getProtectionRange(); @@ -270,31 +274,12 @@ public boolean checkIfSafe(@Nullable World world, @NonNull Material ground, @Non return false; } // Known unsafe blocks - switch (ground) { + return switch (ground) { // Unsafe - case ANVIL: - case BARRIER: - case CACTUS: - case END_PORTAL: - case END_ROD: - case FIRE: - case FLOWER_POT: - case LADDER: - case LEVER: - case TALL_GRASS: - case PISTON_HEAD: - case MOVING_PISTON: - case TORCH: - case WALL_TORCH: - case TRIPWIRE: - case WATER: - case COBWEB: - case NETHER_PORTAL: - case MAGMA_BLOCK: - return false; - default: - return true; - } + case ANVIL, BARRIER, CACTUS, END_PORTAL, END_ROD, FIRE, FLOWER_POT, LADDER, LEVER, TALL_GRASS, PISTON_HEAD, + MOVING_PISTON, TORCH, WALL_TORCH, TRIPWIRE, WATER, COBWEB, NETHER_PORTAL, MAGMA_BLOCK -> false; + default -> true; + }; } /** @@ -324,7 +309,7 @@ public Island createIsland(@NonNull Location location, @Nullable UUID owner) { // This should never happen, so although this is a potential infinite loop I'm going to leave it here because // it will be bad if this does occur and the server should crash. plugin.logWarning("Duplicate island UUID occurred"); - island.setUniqueId(gmName + UUID.randomUUID().toString()); + island.setUniqueId(gmName + UUID.randomUUID()); } if (islandCache.addIsland(island)) { return island; @@ -453,7 +438,7 @@ public void setIslandCache(@NonNull IslandCache islandCache) { * * @param world - world to check * @param uuid - the player's UUID - * @return Location of the center of the player's protection area or null if an island does not exist. + * @return Location of the center of the player's protection area or null if an island does not exist. * Returns an island location OR a team island location */ @Nullable @@ -503,7 +488,7 @@ public Set getMembers(@NonNull World world, @NonNull UUID playerUUID) { * Will update the value based on world settings or island owner permissions (if online). * If the island is unowned, then this value will be 0. * @param island - island - * @param rank {@link RanksManager.MEMBER_RANK}, {@link RanksManager.COOP_RANK}, or {@link RanksManager.TRUSTED_RANK} + * @param rank {@link RanksManager#MEMBER_RANK}, {@link RanksManager#COOP_RANK}, or {@link RanksManager#TRUSTED_RANK} * @return max number of members. If negative, then this means unlimited. * @since 1.16.0 */ @@ -540,7 +525,7 @@ public int getMaxMembers(@NonNull Island island, int rank) { /** * Sets the island max member size. * @param island - island - * @param rank {@link RanksManager.MEMBER_RANK}, {@link RanksManager.COOP_RANK}, or {@link RanksManager.TRUSTED_RANK} + * @param rank {@link RanksManager#MEMBER_RANK}, {@link RanksManager#COOP_RANK}, or {@link RanksManager#TRUSTED_RANK} * @param maxMembers - max number of members. If negative, then this means unlimited. Null means the world * default will be used. * @since 1.16.0 @@ -875,7 +860,7 @@ private void migrateHomes(@NonNull World world, @NonNull UUID uuid, String name, } if (island.getOwner().equals(uuid)) { // Owner - island.setHomes(homes.entrySet().stream().collect(Collectors.toMap(this::getHomeName, Map.Entry::getKey))); + island.setHomes(homes.entrySet().stream().collect(Collectors.toMap(this::getHomeName, Map.Entry::getKey))); plugin.getPlayers().clearHomeLocations(world, uuid); } } @@ -919,7 +904,7 @@ public Location getHomeLocation(@Nullable Island island, String name) { * @since 1.16.0 */ public boolean removeHomeLocation(@Nullable Island island, String name) { - return island == null ? false : island.removeHome(name); + return island != null && island.removeHome(name); } /** @@ -930,7 +915,7 @@ public boolean removeHomeLocation(@Nullable Island island, String name) { * @return true if successful, false if not */ public boolean renameHomeLocation(@Nullable Island island, String oldName, String newName) { - return island == null ? false : island.renameHome(oldName, newName); + return island != null && island.renameHome(oldName, newName); } /** @@ -1076,6 +1061,7 @@ private CompletableFuture homeTeleportAsync(@NonNull World world, @NonN CompletableFuture result = new CompletableFuture<>(); User user = User.getInstance(player); user.sendMessage("commands.island.go.teleport"); + goingHome.add(user.getUniqueId()); // Stop any gliding player.setGliding(false); // Check if the player is a passenger in a boat @@ -1172,7 +1158,13 @@ private void teleported(World world, User user, String name, boolean newIsland, if (plugin.getIWM().isOnJoinResetXP(world)) { user.getPlayer().setTotalExperience(0); } + + // Set the game mode + user.setGameMode(plugin.getIWM().getDefaultGameMode(world)); + } + // Remove from mid-teleport set + goingHome.remove(user.getUniqueId()); } /** @@ -1200,7 +1192,7 @@ public void spawnTeleport(@NonNull World world, @NonNull Player player) { player.leaveVehicle(); // Remove the boat so they don't lie around everywhere boat.remove(); - Material boatMat = Material.getMaterial(((Boat) boat).getWoodType().toString() + "_BOAT"); + Material boatMat = Material.getMaterial(((Boat) boat).getWoodType() + "_BOAT"); if (boatMat == null) { boatMat = Material.OAK_BOAT; } @@ -1796,7 +1788,7 @@ public void resetFlag(World world, Flag flag) { * @since 1.7.0 */ public boolean nameExists(@NonNull World world, @NonNull String name) { - return getIslands(world).stream().filter(island -> island.getName() != null).map(Island::getName) + return getIslands(world).stream().map(Island::getName).filter(Objects::nonNull) .anyMatch(n -> ChatColor.stripColor(n).equals(ChatColor.stripColor(name))); } @@ -1898,4 +1890,12 @@ public CompletableFuture checkTeams(User user, World world) { return r; } + /** + * Is user mid home teleport? + * @return true or false + */ + public boolean isGoingHome(User user) { + return goingHome.contains(user.getUniqueId()); + } + } diff --git a/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java b/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java index 9f0a4838e..934148c78 100644 --- a/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java @@ -35,8 +35,8 @@ */ public class LocalesManager { - private BentoBox plugin; - private Map languages = new HashMap<>(); + private final BentoBox plugin; + private final Map languages = new HashMap<>(); private static final String LOCALE_FOLDER = "locales"; private static final String BENTOBOX = "BentoBox"; private static final String SPACER = "*************************************************"; diff --git a/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java index bf5d8b9ca..b7e538c04 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java @@ -21,7 +21,7 @@ */ public class PlaceholdersManager { - private BentoBox plugin; + private final BentoBox plugin; public PlaceholdersManager(BentoBox plugin) { this.plugin = plugin; diff --git a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java index 5d5c260c9..03609dd48 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java @@ -28,12 +28,12 @@ public class PlayersManager { - private BentoBox plugin; + private final BentoBox plugin; private Database handler; - private Database names; + private final Database names; - private Map playerCache; - private Set inTeleport; + private final Map playerCache; + private final Set inTeleport; private boolean isSaveTaskRunning; diff --git a/src/main/java/world/bentobox/bentobox/managers/WebManager.java b/src/main/java/world/bentobox/bentobox/managers/WebManager.java index d06c236b7..dbd5ef1d5 100644 --- a/src/main/java/world/bentobox/bentobox/managers/WebManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/WebManager.java @@ -34,11 +34,15 @@ */ public class WebManager { - private @NonNull BentoBox plugin; + private @NonNull + final BentoBox plugin; private @Nullable GitHubWebAPI gitHub; - private @NonNull List addonsCatalog; - private @NonNull List gamemodesCatalog; - private @NonNull Map> contributors; + private @NonNull + final List addonsCatalog; + private @NonNull + final List gamemodesCatalog; + private @NonNull + final Map> contributors; public WebManager(@NonNull BentoBox plugin) { this.plugin = plugin; diff --git a/src/main/java/world/bentobox/bentobox/managers/island/DefaultNewIslandLocationStrategy.java b/src/main/java/world/bentobox/bentobox/managers/island/DefaultNewIslandLocationStrategy.java index 11d3a117f..532699419 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/DefaultNewIslandLocationStrategy.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/DefaultNewIslandLocationStrategy.java @@ -23,7 +23,7 @@ public class DefaultNewIslandLocationStrategy implements NewIslandLocationStrategy { /** - * The amount times to tolerate island check returning blocks without kwnon + * The amount times to tolerate island check returning blocks without known * island. */ protected static final Integer MAX_UNOWNED_ISLANDS = 20; @@ -102,7 +102,7 @@ protected Result isIsland(Location location) { // Block check if (plugin.getIWM().isCheckForBlocks(world) && !plugin.getIWM().isUseOwnGenerator(world) - && Arrays.asList(BlockFace.values()).stream().anyMatch(bf -> + && Arrays.stream(BlockFace.values()).anyMatch(bf -> !location.getBlock().getRelative(bf).isEmpty() && !location.getBlock().getRelative(bf).getType().equals(Material.WATER))) { // Block found diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java index c0cf5278c..a577d601d 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -1,15 +1,12 @@ package world.bentobox.bentobox.managers.island; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import org.bukkit.Location; import org.bukkit.World; @@ -115,7 +112,7 @@ public boolean deleteIslandFromCache(@NonNull Island island) { grids.putIfAbsent(island.getWorld(), new IslandGrid()); return grids.get(island.getWorld()).removeFromGrid(island); } - + /** * Delete island from the cache by ID. Does not remove blocks. * @param uniqueId - island unique ID @@ -180,12 +177,10 @@ public Collection getIslands() { @NonNull public Collection getIslands(@NonNull World world) { World overworld = Util.getWorld(world); - List islandsInWorld = islandsByLocation.entrySet().stream() - .filter(entry -> overworld.equals(Util.getWorld(entry.getKey().getWorld()))) // shouldn't make NPEs - .map(Map.Entry::getValue) - .collect(Collectors.toCollection(ArrayList::new)); - return Collections.unmodifiableCollection(islandsInWorld); + return islandsByLocation.entrySet().stream() + .filter(entry -> overworld.equals(Util.getWorld(entry.getKey().getWorld()))) // shouldn't make NPEs + .map(Map.Entry::getValue).toList(); } /** diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java index bf49c8dd3..db404f1a3 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java @@ -12,8 +12,8 @@ * */ class IslandGrid { - private TreeMap> grid = new TreeMap<>(); - private BentoBox plugin = BentoBox.getInstance(); + private final TreeMap> grid = new TreeMap<>(); + private final BentoBox plugin = BentoBox.getInstance(); /** * Adds island to grid @@ -39,7 +39,7 @@ public boolean addToGrid(Island island) { if (firstLoaded.getOwner().equals(island.getOwner())) { // Find out which one is the original if (firstLoaded.getCreatedDate() > island.getCreatedDate()) { - plugin.logError("Same owner duplicate. Swaping based on creation date."); + plugin.logError("Same owner duplicate. Swapping based on creation date."); // FirstLoaded is the newer firstLoaded = new Island(island); zEntry.put(island.getMinZ(), firstLoaded); diff --git a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java index 5ccc74dbe..80c2cd9bf 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -26,14 +26,14 @@ * */ public class NewIsland { - private BentoBox plugin; + private final BentoBox plugin; private Island island; private final User user; private final Reason reason; private final World world; private String name; private final boolean noPaste; - private GameModeAddon addon; + private final GameModeAddon addon; private NewIslandLocationStrategy locationStrategy; diff --git a/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java b/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java index 24bca0842..f2e30c157 100644 --- a/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java +++ b/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java @@ -16,6 +16,6 @@ public interface NMSAbstraction { * @param blockData - block data to set the block * @param applyPhysics - apply physics or not */ - public void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics); + void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics); } diff --git a/src/main/java/world/bentobox/bentobox/nms/package-info.java b/src/main/java/world/bentobox/bentobox/nms/package-info.java new file mode 100644 index 000000000..f0a4744d3 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/nms/package-info.java @@ -0,0 +1,12 @@ +/** + * Contains NMS related classes. + * + *

+ * Due to limitations in Maven and public building on CI's, only the most recent version of Minecraft + * is supported. + *

+ * + * @author tastybento + * + */ +package world.bentobox.bentobox.nms; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java b/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java index 304ae8c79..cee6b2d2f 100644 --- a/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java @@ -9,7 +9,6 @@ import java.util.Map.Entry; import java.util.stream.Collectors; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.World; @@ -53,7 +52,7 @@ public class BlueprintManagementPanel { public static final int MAX_BP_SLOT = 35; private static final String INSTRUCTION = "instruction"; private Entry selected; - private Map blueprints = new HashMap<>(); + private final Map blueprints = new HashMap<>(); private final User user; private final GameModeAddon addon; @@ -242,7 +241,7 @@ private PanelItem getSlotIcon(GameModeAddon addon, BlueprintBundle bb) { protected PanelItem getBundleIcon(BlueprintBundle bb) { return new PanelItemBuilder() .name(t("edit-description")) - .description(bb.getDescription().stream().map(l -> ChatColor.translateAlternateColorCodes('&', l)).collect(Collectors.toList())) + .description(bb.getDescription().stream().map(Util::translateColorCodes).collect(Collectors.toList())) .icon(bb.getIcon()) .clickHandler((panel, u, clickType, slot) -> { u.closeInventory(); @@ -257,21 +256,22 @@ private PanelItem getWorldInstrTile(Environment env) { Material icon; String worldName; switch (env) { - case NORMAL: + case NORMAL -> { icon = Material.GRASS_BLOCK; worldName = normalBlueprint.getName(); - break; - case NETHER: + } + case NETHER -> { icon = Material.NETHERRACK; worldName = netherBlueprint.getName(); - break; - case THE_END: + } + case THE_END -> { icon = Material.END_STONE; worldName = endBlueprint.getName(); - break; - default: + } + default -> { icon = Material.STONE; worldName = Util.prettifyText(env.name()); + } } return new PanelItemBuilder() @@ -339,7 +339,7 @@ private PanelItem getNoPermissionIcon() { protected PanelItem getBlueprintItem(GameModeAddon addon, int pos, BlueprintBundle bb, Blueprint blueprint) { // Create description List desc = blueprint.getDescription() == null ? new ArrayList<>() : blueprint.getDescription(); - desc = desc.stream().map(l -> ChatColor.translateAlternateColorCodes('&', l)).collect(Collectors.toList()); + desc = desc.stream().map(Util::translateColorCodes).collect(Collectors.toList()); if ((!blueprint.equals(endBlueprint) && !blueprint.equals(normalBlueprint) && !blueprint.equals(netherBlueprint))) { if ((pos > MIN_WORLD_SLOT && pos < MAX_WORLD_SLOT)) { desc.add(t("remove")); diff --git a/src/main/java/world/bentobox/bentobox/panels/IconChanger.java b/src/main/java/world/bentobox/bentobox/panels/IconChanger.java index 7b138e65a..809b15455 100644 --- a/src/main/java/world/bentobox/bentobox/panels/IconChanger.java +++ b/src/main/java/world/bentobox/bentobox/panels/IconChanger.java @@ -21,10 +21,10 @@ */ public class IconChanger implements PanelListener { - private GameModeAddon addon; - private BlueprintBundle bb; - private BlueprintManagementPanel blueprintManagementPanel; - private BentoBox plugin; + private final GameModeAddon addon; + private final BlueprintBundle bb; + private final BlueprintManagementPanel blueprintManagementPanel; + private final BentoBox plugin; /** * Change the icon of a blueprint bundle or blueprint diff --git a/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java b/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java index b4d5a21d5..b1a002bc6 100644 --- a/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.stream.Collectors; -import org.bukkit.ChatColor; import org.eclipse.jdt.annotation.NonNull; import world.bentobox.bentobox.BentoBox; @@ -16,6 +15,8 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; import world.bentobox.bentobox.managers.BlueprintsManager; +import world.bentobox.bentobox.util.Util; + /** * Displays the available BlueprintBundles to pick up as the island. @@ -49,7 +50,7 @@ public static void openPanel(@NonNull CompositeCommand command, @NonNull User us // Add an item PanelItem item = new PanelItemBuilder() .name(bb.getDisplayName()) - .description(bb.getDescription().stream().map(l -> ChatColor.translateAlternateColorCodes('&', l)).collect(Collectors.toList())) + .description(bb.getDescription().stream().map(Util::translateColorCodes).collect(Collectors.toList())) .icon(bb.getIcon()).clickHandler((panel, user1, clickType, slot1) -> { user1.closeInventory(); command.execute(user1, label, Collections.singletonList(bb.getUniqueId())); diff --git a/src/main/java/world/bentobox/bentobox/panels/LanguagePanel.java b/src/main/java/world/bentobox/bentobox/panels/LanguagePanel.java index cf4c2e830..ca2e96ef0 100644 --- a/src/main/java/world/bentobox/bentobox/panels/LanguagePanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/LanguagePanel.java @@ -1,6 +1,7 @@ package world.bentobox.bentobox.panels; import java.util.Locale; +import java.util.Objects; import org.apache.commons.lang.WordUtils; import org.bukkit.ChatColor; @@ -38,11 +39,8 @@ public static void openPanel(User user) { BentoBoxLocale language = localesManager.getLanguages().get(locale); ItemStack localeBanner = language.getBanner(); - if (localeBanner != null) { - localeIcon.icon(localeBanner); - } else { - localeIcon.icon(new ItemStack(Material.WHITE_BANNER, 1)); // Set to a blank banner. - } + // Set to a blank banner. + localeIcon.icon(Objects.requireNonNullElseGet(localeBanner, () -> new ItemStack(Material.WHITE_BANNER, 1))); localeIcon.name(ChatColor.WHITE + WordUtils.capitalize(locale.getDisplayName(user.getLocale()))) .clickHandler((panel, u, click, slot) -> { BentoBox.getInstance().getPlayers().setLocale(u.getUniqueId(), locale.toLanguageTag()); diff --git a/src/main/java/world/bentobox/bentobox/panels/ManagementPanel.java b/src/main/java/world/bentobox/bentobox/panels/ManagementPanel.java index b1f5a0e6e..3c6293c6a 100644 --- a/src/main/java/world/bentobox/bentobox/panels/ManagementPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/ManagementPanel.java @@ -53,86 +53,86 @@ public static void openPanel(@NonNull User user, View view) { int i = 0; List addons; switch (view) { - case GAMEMODES: - addons = plugin.getAddonsManager().getGameModeAddons(); - if (addons.isEmpty()) { - looksEmpty(builder, user); - break; - } - for (Addon addon : addons) { - GameModeAddon gameModeAddon = (GameModeAddon) addon; - PanelItem addonItem = new PanelItemBuilder() - .icon(addon.getDescription().getIcon()) - .name(user.getTranslation(LOCALE_REF + "views.gamemodes.gamemode.name", TextVariables.NAME, addon.getDescription().getName())) - .description(user.getTranslation(LOCALE_REF + "views.gamemodes.gamemode.description", - "[islands]", String.valueOf(addon.getIslands().getIslandCount(gameModeAddon.getOverWorld())))) - .clickHandler((panel, user1, clickType, slot) -> { - if (clickType.equals(ClickType.MIDDLE)) { - CreditsPanel.openPanel(user, addon); - } - return true; - }) - .build(); - - builder.item(startSlot + i, addonItem); - - PanelItem blueprints = new PanelItemBuilder() - .icon(Material.STRUCTURE_BLOCK) - .name(user.getTranslation(LOCALE_REF + "views.gamemodes.blueprints.name")) - .description(user.getTranslation(LOCALE_REF + "views.gamemodes.blueprints.description")) - .clickHandler((panel, user1, clickType, slot) -> { - new BlueprintManagementPanel(plugin, user, gameModeAddon).openPanel(); - return true; - }) - .build(); - - builder.item(startSlot + i + 9, blueprints); - i++; - } - break; - case ADDONS: - addons = plugin.getAddonsManager().getEnabledAddons().stream().filter(addon -> !(addon instanceof GameModeAddon)).collect(Collectors.toList()); - if (addons.isEmpty()) { - looksEmpty(builder, user); - break; - } - for (Addon addon : addons) { - PanelItem addonItem = new PanelItemBuilder() - .icon(addon.getDescription().getIcon()) - .name(ChatColor.WHITE + addon.getDescription().getName()) - .clickHandler((panel, user1, clickType, slot) -> { - if (clickType.equals(ClickType.MIDDLE)) { - CreditsPanel.openPanel(user, addon); - } - return true; - }) - .build(); - - builder.item(startSlot + i, addonItem); - i++; - if (builder.slotOccupied(startSlot + i)) { - i = i+2; + case GAMEMODES -> { + addons = plugin.getAddonsManager().getGameModeAddons(); + if (addons.isEmpty()) { + looksEmpty(builder, user); + break; + } + for (Addon addon : addons) { + GameModeAddon gameModeAddon = (GameModeAddon) addon; + PanelItem addonItem = new PanelItemBuilder() + .icon(addon.getDescription().getIcon()) + .name(user.getTranslation(LOCALE_REF + "views.gamemodes.gamemode.name", TextVariables.NAME, addon.getDescription().getName())) + .description(user.getTranslation(LOCALE_REF + "views.gamemodes.gamemode.description", + "[islands]", String.valueOf(addon.getIslands().getIslandCount(gameModeAddon.getOverWorld())))) + .clickHandler((panel, user1, clickType, slot) -> { + if (clickType.equals(ClickType.MIDDLE)) { + CreditsPanel.openPanel(user, addon); + } + return true; + }) + .build(); + + builder.item(startSlot + i, addonItem); + + PanelItem blueprints = new PanelItemBuilder() + .icon(Material.STRUCTURE_BLOCK) + .name(user.getTranslation(LOCALE_REF + "views.gamemodes.blueprints.name")) + .description(user.getTranslation(LOCALE_REF + "views.gamemodes.blueprints.description")) + .clickHandler((panel, user1, clickType, slot) -> { + new BlueprintManagementPanel(plugin, user, gameModeAddon).openPanel(); + return true; + }) + .build(); + + builder.item(startSlot + i + 9, blueprints); + i++; } } - break; - case HOOKS: - if (plugin.getHooks().getHooks().isEmpty()) { - looksEmpty(builder, user); - break; + case ADDONS -> { + addons = plugin.getAddonsManager().getEnabledAddons().stream().filter(addon -> !(addon instanceof GameModeAddon)).collect(Collectors.toList()); + if (addons.isEmpty()) { + looksEmpty(builder, user); + break; + } + for (Addon addon : addons) { + PanelItem addonItem = new PanelItemBuilder() + .icon(addon.getDescription().getIcon()) + .name(ChatColor.WHITE + addon.getDescription().getName()) + .clickHandler((panel, user1, clickType, slot) -> { + if (clickType.equals(ClickType.MIDDLE)) { + CreditsPanel.openPanel(user, addon); + } + return true; + }) + .build(); + + builder.item(startSlot + i, addonItem); + i++; + if (builder.slotOccupied(startSlot + i)) { + i = i + 2; + } + } } - for (Hook hook : plugin.getHooks().getHooks()) { - PanelItem hookItem = new PanelItemBuilder() - .icon(hook.getIcon()) - .name(ChatColor.WHITE + hook.getPluginName()) - .build(); - - builder.item(startSlot + i, hookItem); - i++; - if (builder.slotOccupied(startSlot + i)) { - i = i+2; + case HOOKS -> { + if (plugin.getHooks().getHooks().isEmpty()) { + looksEmpty(builder, user); + break; + } + for (Hook hook : plugin.getHooks().getHooks()) { + PanelItem hookItem = new PanelItemBuilder() + .icon(hook.getIcon()) + .name(ChatColor.WHITE + hook.getPluginName()) + .build(); + + builder.item(startSlot + i, hookItem); + i++; + if (builder.slotOccupied(startSlot + i)) { + i = i + 2; + } } } - break; } // Setup a few more buttons @@ -196,15 +196,9 @@ private static void setupHeader(PanelBuilder builder, User user, View view) { }); switch (view) { - case GAMEMODES: - gamemodesIconBuilder.glow(true); - break; - case ADDONS: - addonsIconBuilder.glow(true); - break; - case HOOKS: - hooksIconBuilder.glow(true); - break; + case GAMEMODES -> gamemodesIconBuilder.glow(true); + case ADDONS -> addonsIconBuilder.glow(true); + case HOOKS -> hooksIconBuilder.glow(true); } builder.item(1, gamemodesIconBuilder.build()); @@ -235,16 +229,9 @@ private static void setupHeader(PanelBuilder builder, User user, View view) { TextVariables.VERSION, serverVersion != null ? serverVersion.toString() : user.getTranslation("general.invalid"))); switch (compatibility) { - case COMPATIBLE: - case SUPPORTED: - compatibilityItemBuilder.icon(Material.GREEN_CONCRETE); - break; - case NOT_SUPPORTED: - compatibilityItemBuilder.icon(Material.ORANGE_CONCRETE); - break; - case INCOMPATIBLE: - compatibilityItemBuilder.icon(Material.RED_CONCRETE); - break; + case COMPATIBLE, SUPPORTED -> compatibilityItemBuilder.icon(Material.GREEN_CONCRETE); + case NOT_SUPPORTED -> compatibilityItemBuilder.icon(Material.ORANGE_CONCRETE); + case INCOMPATIBLE -> compatibilityItemBuilder.icon(Material.RED_CONCRETE); } builder.item(7, compatibilityItemBuilder.build()); diff --git a/src/main/java/world/bentobox/bentobox/panels/package-info.java b/src/main/java/world/bentobox/bentobox/panels/package-info.java new file mode 100644 index 000000000..48ad3f9c2 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/panels/package-info.java @@ -0,0 +1,11 @@ +/** + * Contains non-API panel implementations used by BentoBox + * + *

+ * These are GUIs such as the settings, blueprints, island creation panels, etc. + * They use the Panel API. + *

+ * @author tastybento + * + */ +package world.bentobox.bentobox.panels; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java b/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java index 9e9e2d97c..cc3dcbcd7 100644 --- a/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java +++ b/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java @@ -135,27 +135,22 @@ public Map getTabIcons() { icons.put(5, Flags.LOCK.toPanelItem(plugin, user, island, false)); } // Add the mode icon - switch(plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId())) { - case ADVANCED: - icons.put(7, new PanelItemBuilder().icon(Material.GOLD_INGOT) + switch (plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId())) { + case ADVANCED -> icons.put(7, new PanelItemBuilder().icon(Material.GOLD_INGOT) .name(user.getTranslation(PROTECTION_PANEL + "mode.advanced.name")) .description(user.getTranslation(PROTECTION_PANEL + "mode.advanced.description"), "", user.getTranslation(CLICK_TO_SWITCH, TextVariables.NEXT, user.getTranslation(PROTECTION_PANEL + "mode.expert.name"))) .clickHandler(this) .build()); - break; - case EXPERT: - icons.put(7, new PanelItemBuilder().icon(Material.NETHER_BRICK) + case EXPERT -> icons.put(7, new PanelItemBuilder().icon(Material.NETHER_BRICK) .name(user.getTranslation(PROTECTION_PANEL + "mode.expert.name")) .description(user.getTranslation(PROTECTION_PANEL + "mode.expert.description"), "", user.getTranslation(CLICK_TO_SWITCH, TextVariables.NEXT, user.getTranslation(PROTECTION_PANEL + "mode.basic.name"))) .clickHandler(this) .build()); - break; - default: - icons.put(7, new PanelItemBuilder().icon(Material.IRON_INGOT) + default -> icons.put(7, new PanelItemBuilder().icon(Material.IRON_INGOT) .name(user.getTranslation(PROTECTION_PANEL + "mode.basic.name")) .description(user.getTranslation(PROTECTION_PANEL + "mode.basic.description"), "", user.getTranslation(CLICK_TO_SWITCH, @@ -219,8 +214,7 @@ public Island getIsland() { public boolean onClick(Panel panel, User user, ClickType clickType, int slot) { // Cycle the mode plugin.getPlayers().setFlagsDisplayMode(user.getUniqueId(), plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId()).getNext()); - if (panel instanceof TabbedPanel) { - TabbedPanel tp = ((TabbedPanel)panel); + if (panel instanceof TabbedPanel tp) { tp.setActivePage(0); tp.refreshPanel(); user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_OFF, 1F, 1F); diff --git a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java index fc81f2104..593b550d8 100644 --- a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java +++ b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java @@ -7,7 +7,6 @@ import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; -import org.bukkit.World.Environment; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.generator.ChunkGenerator; @@ -33,22 +32,38 @@ public class DeleteIslandChunks { private int chunkX; private int chunkZ; private BukkitTask task; - private IslandDeletion di; + private final IslandDeletion di; private boolean inDelete; - private BentoBox plugin; + private final BentoBox plugin; private NMSAbstraction nms; + private final World netherWorld; + private final World endWorld; public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) { this.plugin = plugin; this.chunkX = di.getMinXChunk(); this.chunkZ = di.getMinZChunk(); this.di = di; + // Nether + if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { + netherWorld = plugin.getIWM().getNetherWorld(di.getWorld()); + } else { + netherWorld = null; + } + // End + if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { + endWorld = plugin.getIWM().getEndWorld(di.getWorld()); + } else { + endWorld = null; + } + // NMS try { this.nms = Util.getNMS(); } catch (Exception e) { plugin.logError("Could not delete chunks because of NMS error"); return; } + // Fire event IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETE_CHUNKS).build(); regenerateChunks(); @@ -62,13 +77,15 @@ private void regenerateChunks() { inDelete = true; for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) { boolean last = i == plugin.getSettings().getDeleteSpeed() -1; + final int x = chunkX; + final int z = chunkZ; plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> // Overworld - processChunk(gm, Environment.NORMAL, chunkX, chunkZ).thenRun(() -> + processChunk(gm, di.getWorld(), x, z).thenRun(() -> // Nether - processChunk(gm, Environment.NETHER, chunkX, chunkZ).thenRun(() -> + processChunk(gm, netherWorld, x, z).thenRun(() -> // End - processChunk(gm, Environment.THE_END, chunkX, chunkZ).thenRun(() -> finish(last))))); + processChunk(gm, endWorld, x, z).thenRun(() -> finish(last, x))))); chunkZ++; if (chunkZ > di.getMaxZChunk()) { chunkZ = di.getMinZChunk(); @@ -79,8 +96,8 @@ private void regenerateChunks() { } - private void finish(boolean last) { - if (chunkX > di.getMaxXChunk()) { + private void finish(boolean last, int x) { + if (x > di.getMaxXChunk()) { // Fire event IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build(); // We're done @@ -91,41 +108,26 @@ private void finish(boolean last) { } } - private CompletableFuture processChunk(GameModeAddon gm, Environment env, int x, int z) { - World world = di.getWorld(); - switch (env) { - case NETHER: - // Nether - if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { - world = plugin.getIWM().getNetherWorld(di.getWorld()); - } else { - return CompletableFuture.completedFuture(false); - } - break; - case THE_END: - // End - if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { - world = plugin.getIWM().getEndWorld(di.getWorld()); - } else { - return CompletableFuture.completedFuture(false); - } - break; - default: - break; - } - if (PaperLib.isChunkGenerated(world, x, z)) { + private CompletableFuture processChunk(GameModeAddon gm, World world, int x, int z) { + if (world != null) { CompletableFuture r = new CompletableFuture<>(); - PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(r, gm, chunk)); + PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(r, gm, chunk, x, z)); return r; } return CompletableFuture.completedFuture(false); } - private void regenerateChunk(CompletableFuture r, GameModeAddon gm, Chunk chunk) { + private void regenerateChunk(CompletableFuture r, GameModeAddon gm, Chunk chunk, int x, int z) { // Clear all inventories Arrays.stream(chunk.getTileEntities()).filter(te -> (te instanceof InventoryHolder)) .filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ())) .forEach(te -> ((InventoryHolder)te).getInventory().clear()); + // Remove all entities + for (Entity e : chunk.getEntities()) { + if (!(e instanceof Player)) { + e.remove(); + } + } // Reset blocks MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment()); ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete"); diff --git a/src/main/java/world/bentobox/bentobox/util/FileLister.java b/src/main/java/world/bentobox/bentobox/util/FileLister.java index 836ad575b..42e027d12 100644 --- a/src/main/java/world/bentobox/bentobox/util/FileLister.java +++ b/src/main/java/world/bentobox/bentobox/util/FileLister.java @@ -20,7 +20,7 @@ * @author Poslovitch */ public class FileLister{ - private Plugin plugin; + private final Plugin plugin; public FileLister(Plugin level){ plugin = level; diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java new file mode 100644 index 000000000..99886efd0 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -0,0 +1,140 @@ +package world.bentobox.bentobox.util; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.BentoBox; +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.RanksManager; + +/** + * @author tastybento + * @since 1.17.3 + */ +public class IslandInfo { + + private final BentoBox plugin; + private final Island island; + private final @Nullable UUID owner; + private final World world; + + + /** + * @param plugin + * @param island Island to show info + */ + public IslandInfo(Island island) { + this.plugin = BentoBox.getInstance(); + this.island = island; + this.owner = island.getOwner(); + this.world = island.getWorld(); + } + + /** + * Shows admin info of this island + * @param user user asking + */ + public void showAdminInfo(User user) { + user.sendMessage("commands.admin.info.title"); + user.sendMessage("commands.admin.info.island-uuid", "[uuid]", island.getUniqueId()); + if (owner == null) { + user.sendMessage("commands.admin.info.unowned"); + } else { + user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(owner), "[uuid]", owner.toString()); + + // Fixes #getLastPlayed() returning 0 when it is the owner's first connection. + long lastPlayed = (Bukkit.getOfflinePlayer(owner).getLastPlayed() != 0) ? + Bukkit.getOfflinePlayer(owner).getLastPlayed() : Bukkit.getOfflinePlayer(owner).getFirstPlayed(); + String formattedDate; + try { + String dateTimeFormat = plugin.getLocalesManager().get("commands.admin.info.last-login-date-time-format"); + formattedDate = new SimpleDateFormat(dateTimeFormat).format(new Date(lastPlayed)); + } catch (NullPointerException | IllegalArgumentException ignored) { + formattedDate = new Date(lastPlayed).toString(); + } + user.sendMessage("commands.admin.info.last-login","[date]", formattedDate); + + user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner))); + String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); + String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world)); + user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); + // Show team members + showMembers(user); + } + Vector location = island.getProtectionCenter().toVector(); + user.sendMessage("commands.admin.info.island-protection-center", TextVariables.XYZ, Util.xyz(location)); + user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(island.getCenter().toVector())); + user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(new Vector(island.getMinX(), 0, island.getMinZ())), "[xz2]", Util.xyz(new Vector(island.getMaxX(), 0, island.getMaxZ()))); + user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(island.getProtectionRange())); + user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(island.getMaxEverProtectionRange())); + user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX(), 0, island.getMaxProtectedZ()))); + if (island.isSpawn()) { + user.sendMessage("commands.admin.info.is-spawn"); + } + if (!island.getBanned().isEmpty()) { + user.sendMessage("commands.admin.info.banned-players"); + island.getBanned().forEach(u -> user.sendMessage("commands.admin.info.banned-format", TextVariables.NAME, plugin.getPlayers().getName(u))); + } + if (island.getPurgeProtected()) { + user.sendMessage("commands.admin.info.purge-protected"); + } + } + + + /** + * Shows info of this island to this user. + * @param user the User who is requesting it + * @return always true + */ + public boolean showInfo(User user) { + user.sendMessage("commands.admin.info.title"); + if (owner == null) { + user.sendMessage("commands.admin.info.unowned"); + } else { + user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(owner)); + user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner))); + String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); + String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world)); + user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); + // Show team members + showMembers(user); + } + Vector location = island.getProtectionCenter().toVector(); + user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(location)); + user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(island.getProtectionRange())); + user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX(), 0, island.getMaxProtectedZ()))); + if (island.isSpawn()) { + user.sendMessage("commands.admin.info.is-spawn"); + } + if (!island.getBanned().isEmpty()) { + user.sendMessage("commands.admin.info.banned-players"); + island.getBanned().forEach(u -> user.sendMessage("commands.admin.info.banned-format", TextVariables.NAME, plugin.getPlayers().getName(u))); + } + return true; + } + + /** + * Shows the members of this island to this user. + * @param user the User who is requesting it + */ + public void showMembers(User user) { + user.sendMessage("commands.admin.info.team-members-title"); + island.getMembers().forEach((u, i) -> { + if (owner.equals(u)) { + user.sendMessage("commands.admin.info.team-owner-format", TextVariables.NAME, plugin.getPlayers().getName(u) + , "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i))); + } else if (i > RanksManager.VISITOR_RANK){ + user.sendMessage("commands.admin.info.team-member-format", TextVariables.NAME, plugin.getPlayers().getName(u) + , "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i))); + } + }); + } +} diff --git a/src/main/java/world/bentobox/bentobox/util/ItemParser.java b/src/main/java/world/bentobox/bentobox/util/ItemParser.java index 7b751483b..feb21ae42 100644 --- a/src/main/java/world/bentobox/bentobox/util/ItemParser.java +++ b/src/main/java/world/bentobox/bentobox/util/ItemParser.java @@ -1,17 +1,25 @@ package world.bentobox.bentobox.util; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import org.bukkit.Bukkit; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.block.banner.Pattern; import org.bukkit.block.banner.PatternType; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BannerMeta; -import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.*; import org.bukkit.potion.PotionData; import org.bukkit.potion.PotionType; +import org.eclipse.jdt.annotation.Nullable; + +import java.lang.reflect.Field; +import java.util.MissingFormatArgumentException; +import java.util.UUID; import world.bentobox.bentobox.BentoBox; + /** * Utility class that parses a String into an ItemStack. * It is used for converting config file entries to objects. @@ -20,63 +28,123 @@ */ public class ItemParser { - private ItemParser() {} + /** + * Parse given string to ItemStack. + * @param text String value of item stack. + * @return ItemStack of parsed item or null. + */ + public static ItemStack parse(String text) { + return ItemParser.parse(text, null); + } - public static ItemStack parse(String s){ - if (s == null) { - return null; - } - String[] part = s.split(":"); - // Material-specific handling - if (part[0].contains("POTION") || part[0].equalsIgnoreCase("TIPPED_ARROW")) { - return potion(part); - } else if (part[0].contains("BANNER")) { - return banner(part); + /** + * Parse given string to ItemStack. + * @param text String value of item stack. + * @param defaultItemStack Material that should be returned if parsing failed. + * @return ItemStack of parsed item or defaultItemStack. + */ + @Nullable + public static ItemStack parse(@Nullable String text, @Nullable ItemStack defaultItemStack) { + if (text == null || text.isBlank()) { + // Text does not exist or is empty. + return defaultItemStack; } - // Generic handling - if (part.length == 2) { - // Material:Qty - return two(part); - } else if (part.length == 3) { - // Material:Durability:Qty - return three(part); - } - return null; - } + String[] part = text.split(":"); - private static ItemStack two(String[] part) { - int reqAmount; try { - reqAmount = Integer.parseInt(part[1]); - } catch (Exception e) { - return null; + // Check if there are more properties for the item stack + if (part.length == 1) { + // Parse material directly. It does not have any extra properties. + return new ItemStack(Material.valueOf(text.toUpperCase())); + } + // Material-specific handling + else if (part[0].contains("POTION") || part[0].equalsIgnoreCase("TIPPED_ARROW")) { + // Parse Potions and Tipped Arrows + return parsePotion(part); + } else if (part[0].contains("BANNER")) { + // Parse Banners + return parseBanner(part); + } else if (part[0].equalsIgnoreCase("PLAYER_HEAD")) { + // Parse Player Heads + return parsePlayerHead(part); + } + // Generic handling + else if (part.length == 2) { + // Material:Qty + return parseItemQuantity(part); + } else if (part.length == 3) { + // Material:Durability:Qty + return parseItemDurabilityAndQuantity(part); + } + } catch (Exception exception) { + BentoBox.getInstance().logError("Could not parse item " + text + " " + exception.getLocalizedMessage()); } + return defaultItemStack; + } + + + /** + * This method parses array of 2 items into an item stack. + * First array element is material, while second array element is integer, that represents item count. + * Example: + * DIAMOND:20 + * @param part String array that contains 2 elements. + * @return ItemStack with material from first array element and amount based on second array element. + */ + private static ItemStack parseItemQuantity(String[] part) { + int reqAmount = Integer.parseInt(part[1]); Material reqItem = Material.getMaterial(part[0].toUpperCase(java.util.Locale.ENGLISH)); + if (reqItem == null) { - return null; + throw new IllegalArgumentException(part[0] + " is not a valid Material."); } + return new ItemStack(reqItem, reqAmount); } - private static ItemStack three(String[] part) { + + /** + * This method parses array of 3 items into an item stack. + * First array element is material, while second and third array element are integers. + * The middle element represents durability, while third element represents quantity. + * Example: + * IRON_SWORD:20:1 + * @param part String array that contains 3 elements. + * @return ItemStack with material from first array element, durability from second element and amount based on third array element. + */ + private static ItemStack parseItemDurabilityAndQuantity(String[] part) { // Rearrange - String[] twoer = {part[0], part[2]}; - return two(twoer); + String[] parsable = {part[0], part[2]}; + ItemStack durability = parseItemQuantity(parsable); + + ItemMeta meta = durability.getItemMeta(); + + if (meta instanceof Damageable) { + ((Damageable) meta).setDamage(Integer.parseInt(part[1])); + durability.setItemMeta(meta); + } + + return durability; } - private static ItemStack potion(String[] part) { + + /** + * This method parses array of 6 items into an item stack. + * Format: + * POTION:NAME::::QTY + * Example: + * POTION:STRENGTH:1:EXTENDED:SPLASH:1 + * @param part String array that contains 6 elements. + * @return Potion with given properties. + */ + private static ItemStack parsePotion(String[] part) { if (part.length != 6) { - return null; - } - int reqAmount; - try { - reqAmount = Integer.parseInt(part[5]); - } catch (Exception e) { - return null; + throw new MissingFormatArgumentException("Potion parsing requires 6 parts."); } + /* * # Format POTION:NAME::::QTY # LEVEL, EXTENDED, SPLASH, LINGER are optional. @@ -103,35 +171,118 @@ private static ItemStack potion(String[] part) { boolean isExtended = part[3].equalsIgnoreCase("EXTENDED"); PotionData data = new PotionData(type, isExtended, isUpgraded); potionMeta.setBasePotionData(data); - - result.setAmount(reqAmount); + result.setItemMeta(potionMeta); + result.setAmount(Integer.parseInt(part[5])); return result; } - private static ItemStack banner(String[] part) { - try { - if (part.length >= 2) { - Material bannerMat = Material.getMaterial(part[0]); - if (bannerMat == null) { - BentoBox.getInstance().logError("Could not parse banner item " + part[0] + " so using a white banner."); - bannerMat = Material.WHITE_BANNER; - } - ItemStack result = new ItemStack(bannerMat, Integer.parseInt(part[1])); - - BannerMeta meta = (BannerMeta) result.getItemMeta(); - if (meta != null) { - for (int i = 2; i < part.length; i += 2) { - meta.addPattern(new Pattern(DyeColor.valueOf(part[i + 1]), PatternType.valueOf(part[i]))); - } - result.setItemMeta(meta); + + /** + * This method parses array of multiple elements for the Banner. + * @param part String array that contains at least 2 elements. + * @return Banner as item stack. + */ + private static ItemStack parseBanner(String[] part) { + if (part.length >= 2) { + Material bannerMat = Material.getMaterial(part[0]); + if (bannerMat == null) { + BentoBox.getInstance().logError("Could not parse banner item " + part[0] + " so using a white banner."); + bannerMat = Material.WHITE_BANNER; + } + ItemStack result = new ItemStack(bannerMat, Integer.parseInt(part[1])); + + BannerMeta meta = (BannerMeta) result.getItemMeta(); + if (meta != null) { + for (int i = 2; i < part.length; i += 2) { + meta.addPattern(new Pattern(DyeColor.valueOf(part[i + 1]), PatternType.valueOf(part[i]))); } + result.setItemMeta(meta); + } + + return result; + } else { + throw new MissingFormatArgumentException("Banner parsing requires at least 2 parts."); + } + } + + + /** + * This method parses array of 2 to 3 elements that represents player head. + * Format: + * PLAYER_HEAD::QTY + * PLAYER_HEAD: + * PLAYER_HEAD:QTY + * Example: + * PLAYER_HEAD:1 + * PLAYER_HEAD:BONNe1704 + * PLAYER_HEAD:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWY1ZjE1OTg4NmNjNTMxZmZlYTBkOGFhNWY5MmVkNGU1ZGE2NWY3MjRjMDU3MGFmODZhOTBiZjAwYzY3YzQyZSJ9fX0:1 + * @param part String array that contains at least 2 elements. + * @return Player head with given properties. + */ + @SuppressWarnings("deprecation") + private static ItemStack parsePlayerHead(String[] part) { + ItemStack playerHead; + + if (part.length == 3) { + String[] parsable = {part[0], part[2]}; + // create parse item and quantity. + playerHead = parseItemQuantity(parsable); + } else if (isNumeric(part[1])) { + // there is no meta item for player head. + return parseItemQuantity(part); + } else { + // create new player head item stack. + playerHead = new ItemStack(Material.PLAYER_HEAD); + } + + // Set correct Skull texture + try { + SkullMeta meta = (SkullMeta) playerHead.getItemMeta(); - return result; + if (part[1].length() < 17) { + // Minecraft player names are in length between 3 and 16 chars. + meta.setOwner(part[1]); + } else if (part[1].length() == 32) { + // trimmed UUID length are 32 chars. + meta.setOwningPlayer(Bukkit.getOfflinePlayer( + UUID.fromString(part[1].replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")))); + } else if (part[1].length() == 36) { + // full UUID length are 36 chars. + meta.setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(part[1]))); } else { - return null; + // If chars are more than 36, apparently it is base64 encoded texture. + GameProfile profile = new GameProfile(UUID.randomUUID(), ""); + profile.getProperties().put("textures", new Property("textures", part[1])); + + // Null pointer will be caught and ignored. + Field profileField = meta.getClass().getDeclaredField("profile"); + profileField.setAccessible(true); + profileField.set(meta, profile); } - } catch (Exception e) { - return null; + + // Apply new meta to the item. + playerHead.setItemMeta(meta); + } catch (Exception ignored) {} + + return playerHead; + } + + + /** + * Check if given sting is an integer. + * @param string Value that must be checked. + * @return {@code true} if value is integer, {@code false} otherwise. + */ + private static boolean isNumeric(String string) { + if(string == null || string.equals("")) { + return false; + } + + try { + Integer.parseInt(string); + return true; + } catch (NumberFormatException e) { + return false; } } } diff --git a/src/main/java/world/bentobox/bentobox/util/Pair.java b/src/main/java/world/bentobox/bentobox/util/Pair.java index 775c5f111..d2109480a 100644 --- a/src/main/java/world/bentobox/bentobox/util/Pair.java +++ b/src/main/java/world/bentobox/bentobox/util/Pair.java @@ -67,10 +67,9 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof Pair)) { + if (!(obj instanceof Pair other)) { return false; } - Pair other = (Pair) obj; if (x == null) { if (other.x != null) { return false; diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java index 82eda60b5..953952dcb 100644 --- a/src/main/java/world/bentobox/bentobox/util/Util.java +++ b/src/main/java/world/bentobox/bentobox/util/Util.java @@ -11,6 +11,8 @@ import java.util.concurrent.CompletableFuture; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang.Validate; @@ -55,7 +57,10 @@ * @author Poslovitch */ public class Util { - + /** + * Use standard color code definition: &. + */ + private static final Pattern HEX_PATTERN = Pattern.compile("&#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"); private static final String NETHER = "_nether"; private static final String THE_END = "_the_end"; private static String serverVersion = null; @@ -278,38 +283,23 @@ public static List listJarFiles(JarFile jar, String folderPath, String s * @return degrees */ public static float blockFaceToFloat(BlockFace face) { - switch (face) { - case EAST: - return 90F; - case EAST_NORTH_EAST: - return 67.5F; - case NORTH_EAST: - return 45F; - case NORTH_NORTH_EAST: - return 22.5F; - case NORTH_NORTH_WEST: - return 337.5F; - case NORTH_WEST: - return 315F; - case SOUTH: - return 180F; - case SOUTH_EAST: - return 135F; - case SOUTH_SOUTH_EAST: - return 157.5F; - case SOUTH_SOUTH_WEST: - return 202.5F; - case SOUTH_WEST: - return 225F; - case WEST: - return 270F; - case WEST_NORTH_WEST: - return 292.5F; - case WEST_SOUTH_WEST: - return 247.5F; - default: - return 0F; - } + return switch (face) { + case EAST -> 90F; + case EAST_NORTH_EAST -> 67.5F; + case NORTH_EAST -> 45F; + case NORTH_NORTH_EAST -> 22.5F; + case NORTH_NORTH_WEST -> 337.5F; + case NORTH_WEST -> 315F; + case SOUTH -> 180F; + case SOUTH_EAST -> 135F; + case SOUTH_SOUTH_EAST -> 157.5F; + case SOUTH_SOUTH_WEST -> 202.5F; + case SOUTH_WEST -> 225F; + case WEST -> 270F; + case WEST_NORTH_WEST -> 292.5F; + case WEST_SOUTH_WEST -> 247.5F; + default -> 0F; + }; } /** @@ -538,6 +528,44 @@ private static boolean isJUnitTest() { return false; } + + /** + * This method translates color codes in given string and strips whitespace after them. + * This code parses both: hex and old color codes. + * @param textToColor Text which color codes must be parsed. + * @return String text with parsed colors and stripped whitespaces after them. + */ + @NonNull + public static String translateColorCodes(@NonNull String textToColor) { + // Use matcher to find hex patterns in given text. + Matcher matcher = HEX_PATTERN.matcher(textToColor); + // Increase buffer size by 32 like it is in bungee cord api. Use buffer because it is sync. + StringBuilder buffer = new StringBuilder(textToColor.length() + 32); + + while (matcher.find()) { + String group = matcher.group(1); + + if (group.length() == 6) { + // Parses #ffffff to a color text. + matcher.appendReplacement(buffer, ChatColor.COLOR_CHAR + "x" + + ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(1) + + ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(3) + + ChatColor.COLOR_CHAR + group.charAt(4) + ChatColor.COLOR_CHAR + group.charAt(5)); + } else { + // Parses #fff to a color text. + matcher.appendReplacement(buffer, ChatColor.COLOR_CHAR + "x" + + ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(0) + + ChatColor.COLOR_CHAR + group.charAt(1) + ChatColor.COLOR_CHAR + group.charAt(1) + + ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(2)); + } + } + + // transform normal codes and strip spaces after color code. + return Util.stripSpaceAfterColorCodes( + ChatColor.translateAlternateColorCodes('&', matcher.appendTail(buffer).toString())); + } + + /** * Strips spaces immediately after color codes. Used by {@link User#getTranslation(String, String...)}. * @param textToStrip - text to strip diff --git a/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java b/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java index e171b9c79..04012b0e1 100644 --- a/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java +++ b/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java @@ -72,7 +72,7 @@ public static void getHead(PanelItem panelItem, HeadRequester requester) { HeadCache cache = cachedHeads.get(panelItem.getPlayerHeadName()); - // Get value from config. Multiply value to 60 000 as internally it uses miliseconds. + // Get value from config. Multiply value to 60 000 as internally it uses milliseconds. // Config value stores minutes. long cacheTimeout = BentoBox.getInstance().getSettings().getPlayerHeadCacheTime() * 60 * 1000; diff --git a/src/main/java/world/bentobox/bentobox/util/package-info.java b/src/main/java/world/bentobox/bentobox/util/package-info.java new file mode 100644 index 000000000..3b6d6e5c3 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/util/package-info.java @@ -0,0 +1,12 @@ +/** + * API that provides useful (and not so useful) utility functions. + * + *

+ * Look here before you write your own utility function. If it isn't here, but would be useful + * the submit a PR so others can avoid duplicating code! + *

+ * + * @author various + * + */ +package world.bentobox.bentobox.util; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java index 7136461e4..9879cb469 100644 --- a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java +++ b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java @@ -300,7 +300,7 @@ public static class Builder { private String failureMessage = ""; private Location location; private Runnable runnable; - private CompletableFuture result = new CompletableFuture<>(); + private final CompletableFuture result = new CompletableFuture<>(); public Builder(BentoBox plugin) { this.plugin = plugin; @@ -380,7 +380,7 @@ public Builder location(Location location) { /** * Try to teleport the player - * @return CompletableFuture that will become true if successfull and false if not + * @return CompletableFuture that will become true if successful and false if not * @since 1.14.0 */ @Nullable diff --git a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java index 54b365fab..fb966c959 100644 --- a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java +++ b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java @@ -17,7 +17,7 @@ public class ServerCompatibility { // ---- SINGLETON ---- - private static ServerCompatibility instance = new ServerCompatibility(); + private static final ServerCompatibility instance = new ServerCompatibility(); public static ServerCompatibility getInstance() { return instance; @@ -54,7 +54,7 @@ public enum Compatibility { */ INCOMPATIBLE(false); - private boolean canLaunch; + private final boolean canLaunch; Compatibility(boolean canLaunch) { this.canLaunch = canLaunch; @@ -85,7 +85,7 @@ public enum ServerSoftware { */ UNKNOWN(Compatibility.INCOMPATIBLE); - private Compatibility compatibility; + private final Compatibility compatibility; /** * @since 1.14.0 */ @@ -190,7 +190,7 @@ public enum ServerVersion { V1_17_1(Compatibility.COMPATIBLE) ; - private Compatibility compatibility; + private final Compatibility compatibility; ServerVersion(Compatibility compatibility) { this.compatibility = compatibility; @@ -308,9 +308,9 @@ public boolean isVersion(@NonNull ServerVersion... versions) { } /** - * Returns whether the server runs on the specified softwares. + * Returns whether the server runs on the specified software. * @param softwares the {@link ServerSoftware}s to check. - * @return {@code true} if the server runs on on of these softwares, {@code false} otherwise. + * @return {@code true} if the server runs on on of these software, {@code false} otherwise. * @since 1.5.0 */ public boolean isSoftware(@NonNull ServerSoftware... softwares) { diff --git a/src/main/java/world/bentobox/bentobox/versions/package-info.java b/src/main/java/world/bentobox/bentobox/versions/package-info.java new file mode 100644 index 000000000..65cd12b0e --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/versions/package-info.java @@ -0,0 +1,11 @@ +/** + * Holds a class that decides whether BentoBox is compatible with the server or not. + * + *

+ * This has to be updated with each new release of the server. + *

+ * + * @author Poslovich + * + */ +package world.bentobox.bentobox.versions; \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/web/catalog/CatalogEntry.java b/src/main/java/world/bentobox/bentobox/web/catalog/CatalogEntry.java index fea1bc1cf..2a73ffd6c 100644 --- a/src/main/java/world/bentobox/bentobox/web/catalog/CatalogEntry.java +++ b/src/main/java/world/bentobox/bentobox/web/catalog/CatalogEntry.java @@ -13,16 +13,22 @@ */ public class CatalogEntry { - private int slot; + private final int slot; /** * Defaults to {@link Material#PAPER}. */ - private @NonNull Material icon; - private @NonNull String name; - private @NonNull String description; - private @Nullable String topic; - private @Nullable String tag; - private @NonNull String repository; + private @NonNull + final Material icon; + private @NonNull + final String name; + private @NonNull + final String description; + private @Nullable + final String topic; + private @Nullable + final String tag; + private @NonNull + final String repository; public CatalogEntry(@NonNull JsonObject object) { this.slot = object.get("slot").getAsInt(); diff --git a/src/main/java/world/bentobox/bentobox/web/credits/Contributor.java b/src/main/java/world/bentobox/bentobox/web/credits/Contributor.java index c6d2f28ff..8d0bf328b 100644 --- a/src/main/java/world/bentobox/bentobox/web/credits/Contributor.java +++ b/src/main/java/world/bentobox/bentobox/web/credits/Contributor.java @@ -9,8 +9,9 @@ */ public class Contributor { - private @NonNull String name; - private int commits; + private @NonNull + final String name; + private final int commits; public Contributor(@NonNull String name, int commits) { this.name = name; diff --git a/src/main/java/world/bentobox/bentobox/web/package-info.java b/src/main/java/world/bentobox/bentobox/web/package-info.java new file mode 100644 index 000000000..ec8c9b812 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/web/package-info.java @@ -0,0 +1,7 @@ +/** + * Fetches data from the web, such as the catalog. + * + * @author Poslovitch + * + */ +package world.bentobox.bentobox.web; \ No newline at end of file diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index f35bbb9b4..ce5daf42a 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -3,7 +3,7 @@ meta: authors: - xXjojojXx - Poslovitch - banner: RED_BANNER:1:STRIPE_RIGHT:BLACK:LEFT_STRIPE:YELLOW + banner: RED_BANNER:1:STRIPE_RIGHT:BLACK:STRIPE_LEFT:YELLOW commands: admin: setrank: diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index f080ab77f..351640149 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -10,9 +10,10 @@ description: ${project.description} load: STARTUP -loadbefore: [Multiverse-Core, Residence] +loadbefore: [Pladdon, Multiverse-Core, Residence] softdepend: + - Citizens - Vault - PlaceholderAPI - dynmap diff --git a/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java b/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java index d4e684c5b..6bb9c3e84 100644 --- a/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java +++ b/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java @@ -301,7 +301,7 @@ public void testGetAddonByName() { * Utility methods */ private void createJarArchive(File archiveFile, List tobeJaredList) { - byte buffer[] = new byte[BUFFER_SIZE]; + byte[] buffer = new byte[BUFFER_SIZE]; // Open archive file try (FileOutputStream stream = new FileOutputStream(archiveFile)) { try (JarOutputStream out = new JarOutputStream(stream, new Manifest())) { diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java index 1f5c90a81..4cf7dad10 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java @@ -1,27 +1,35 @@ package world.bentobox.bentobox.api.commands.admin; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; +import java.util.Collections; import java.util.Optional; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.NonNull; 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.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -35,7 +43,9 @@ import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; /** @@ -43,16 +53,32 @@ * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, User.class }) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class}) public class AdminInfoCommandTest { - private BentoBox plugin; - private CompositeCommand ac; - private UUID uuid; + @Mock + private CompositeCommand ic; + @Mock private User user; + @Mock private IslandsManager im; + @Mock private PlayersManager pm; - private UUID notUUID; + + private Island island; + + private AdminInfoCommand iic; + + @Mock + private Player player; + @Mock + private World world; + @Mock + private PlaceholdersManager phm; + @Mock + private @NonNull Location location; + @Mock + private IslandWorldManager iwm; /** * @throws java.lang.Exception @@ -60,69 +86,62 @@ public class AdminInfoCommandTest { @Before public void setUp() throws Exception { // Set up plugin - plugin = mock(BentoBox.class); + BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); - Util.setPlugin(plugin); + // IWM + when(plugin.getIWM()).thenReturn(iwm); + when(plugin.getRanksManager()).thenReturn(new RanksManager()); + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); // Command manager CommandsManager cm = mock(CommandsManager.class); when(plugin.getCommandsManager()).thenReturn(cm); // Player - Player p = mock(Player.class); - // Sometimes use Mockito.withSettings().verboseLogging() - user = mock(User.class); - when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); - notUUID = UUID.randomUUID(); - while(notUUID.equals(uuid)) { - notUUID = UUID.randomUUID(); - } + when(player.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); when(user.getUniqueId()).thenReturn(uuid); - when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); + when(user.getWorld()).thenReturn(world); + when(user.getPlayer()).thenReturn(player); + when(user.isPlayer()).thenReturn(true); + //user = User.getInstance(player); + // Set the User class plugin as this one User.setPlugin(plugin); - // Parent command has no aliases - ac = mock(CompositeCommand.class); - when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); - - // Island World Manager - IslandWorldManager iwm = mock(IslandWorldManager.class); - when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); - when(plugin.getIWM()).thenReturn(iwm); - + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + when(plugin.getLocalesManager()).thenReturn(lm); + // Return the same string + when(phm.replacePlaceholders(any(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + when(plugin.getPlaceholdersManager()).thenReturn(phm); + // Translate + when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getTranslation(anyString(), anyString(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); - // Player has island to begin with - im = mock(IslandsManager.class); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); - when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); - when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); - when(im.getOwner(Mockito.any(),Mockito.any())).thenReturn(uuid); + // Island manager + island = new Island(location, uuid, 100); + when(location.toVector()).thenReturn(new Vector(1,2,3)); when(plugin.getIslands()).thenReturn(im); + Optional optionalIsland = Optional.of(island); + when(im.getIslandAt(any())).thenReturn(optionalIsland); + when(im.getIsland(any(), any(UUID.class))).thenReturn(island); - // Has team - pm = mock(PlayersManager.class); - when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); - + // Players manager when(plugin.getPlayers()).thenReturn(pm); + when(pm.getUUID(any())).thenReturn(uuid); - // Server & Scheduler - BukkitScheduler sch = mock(BukkitScheduler.class); - PowerMockito.mockStatic(Bukkit.class); - when(Bukkit.getScheduler()).thenReturn(sch); - - // Locales - LocalesManager lm = mock(LocalesManager.class); - when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); - when(plugin.getLocalesManager()).thenReturn(lm); - - // Addon - when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty()); + // Command + iic = new AdminInfoCommand(ic); } + /** + * @throws java.lang.Exception + */ @After public void tearDown() { User.clearUsers(); @@ -130,93 +149,111 @@ public void tearDown() { } /** - * Test method for {@link AdminInfoCommand#execute(User, String, List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#setup()}. */ @Test - public void testExecuteNoTargetConsole() { - AdminInfoCommand itl = new AdminInfoCommand(ac); - CommandSender sender = mock(CommandSender.class); - User console = User.getInstance(sender); - assertFalse(itl.execute(console, itl.getLabel(), new ArrayList<>())); - // Show help + public void testSetup() { + assertEquals("mod.info", iic.getPermission()); + assertFalse(iic.isOnlyPlayer()); + assertEquals("commands.admin.info.parameters", iic.getParameters()); + assertEquals("commands.admin.info.description", iic.getDescription()); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminInfoCommand#execute(User, String, List)} . + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteUnknownPlayer() { - AdminInfoCommand itl = new AdminInfoCommand(ac); - String[] name = {"tastybento"}; - when(pm.getUUID(Mockito.any())).thenReturn(null); - assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("general.errors.unknown-player", "[name]", name[0]); + public void testExecuteUserStringListOfStringTooManyArgs() { + assertFalse(iic.execute(user, "", Arrays.asList("hdhh", "hdhdhd"))); + verify(user).sendMessage("commands.help.header", "[label]", "commands.help.console"); } /** - * Test method for . + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecutePlayerHasNoIsland() { - AdminInfoCommand itl = new AdminInfoCommand(ac); - String[] name = {"tastybento"}; - when(pm.getUUID(Mockito.any())).thenReturn(notUUID); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); - when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); - when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(null); - assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage(Mockito.eq("general.errors.player-has-no-island")); + public void testExecuteUserStringListOfStringNoArgsConsole() { + CommandSender console = mock(CommandSender.class); + User sender = User.getInstance(console); + assertFalse(iic.execute(sender, "", Collections.emptyList())); + verify(user, never()).sendMessage("commands.help.header", "[label]", "commands.help.console"); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminInfoCommand#execute(User, String, List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteSuccess() { - AdminInfoCommand itl = new AdminInfoCommand(ac); - String[] name = {"tastybento"}; - when(pm.getUUID(Mockito.any())).thenReturn(notUUID); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); - Island is = mock(Island.class); - when(im.getIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(is); - assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(is).showInfo(Mockito.eq(user)); + public void testExecuteUserStringListOfStringNoArgsNoIsland() { + when(im.getIslandAt(any())).thenReturn(Optional.empty()); + assertTrue(iic.execute(user, "", Collections.emptyList())); + verify(user).sendMessage("commands.admin.info.no-island"); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminInfoCommand#execute(User, String, List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteUserNotOnIsland() { - when(user.isPlayer()).thenReturn(true); - AdminInfoCommand itl = new AdminInfoCommand(ac); - // No island here - when(im.getIslandAt(Mockito.any())).thenReturn(Optional.empty()); - assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); - // Confirm other verifications - Mockito.verify(user).sendMessage("commands.admin.info.no-island"); + public void testExecuteUserStringListOfStringNoArgsSuccess() { + assertTrue(iic.execute(user, "", Collections.emptyList())); + verify(user).sendMessage("commands.admin.info.title"); + verify(user).sendMessage(eq("commands.admin.info.island-uuid"), eq("[uuid]"), any()); + verify(user).sendMessage(eq("commands.admin.info.owner"), eq("[owner]"), eq(null), eq("[uuid]"), any()); + verify(user).sendMessage(eq("commands.admin.info.last-login"), eq("[date]"), any()); + verify(user).sendMessage("commands.admin.info.deaths", "[number]", "0"); + verify(user).sendMessage("commands.admin.info.resets-left", "[number]", "0", "[total]", "0"); + verify(user).sendMessage("commands.admin.info.team-members-title"); + verify(user).sendMessage("commands.admin.info.team-owner-format", "[name]", null, "[rank]", "ranks.owner"); + verify(user).sendMessage("commands.admin.info.island-protection-center", "[xyz]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.island-center", "[xyz]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.island-coords", "[xz1]", "0,0,0", "[xz2]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.protection-range", "[range]", "100"); + verify(user).sendMessage("commands.admin.info.max-protection-range", "[range]", "100"); + verify(user).sendMessage("commands.admin.info.protection-coords", "[xz1]", "0,0,0", "[xz2]", "0,0,0"); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminInfoCommand#execute(User, String, List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteSuccessUserOnIsland() { - when(user.isPlayer()).thenReturn(true); - AdminInfoCommand itl = new AdminInfoCommand(ac); - Location loc = mock(Location.class); + public void testExecuteUserStringListOfStringArgsSuccess() { + assertTrue(iic.execute(user, "", Collections.singletonList("tastybento"))); + verify(user).sendMessage("commands.admin.info.title"); + verify(user).sendMessage(eq("commands.admin.info.island-uuid"), eq("[uuid]"), any()); + verify(user).sendMessage(eq("commands.admin.info.owner"), eq("[owner]"), eq(null), eq("[uuid]"), any()); + verify(user).sendMessage(eq("commands.admin.info.last-login"), eq("[date]"), any()); + verify(user).sendMessage("commands.admin.info.deaths", "[number]", "0"); + verify(user).sendMessage("commands.admin.info.resets-left", "[number]", "0", "[total]", "0"); + verify(user).sendMessage("commands.admin.info.team-members-title"); + verify(user).sendMessage("commands.admin.info.team-owner-format", "[name]", null, "[rank]", "ranks.owner"); + verify(user).sendMessage("commands.admin.info.island-protection-center", "[xyz]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.island-center", "[xyz]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.island-coords", "[xz1]", "0,0,0", "[xz2]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.protection-range", "[range]", "100"); + verify(user).sendMessage("commands.admin.info.max-protection-range", "[range]", "100"); + verify(user).sendMessage("commands.admin.info.protection-coords", "[xz1]", "0,0,0", "[xz2]", "0,0,0"); + + } - // Island has owner - Island is = mock(Island.class); - when(is.getOwner()).thenReturn(uuid); - when(is.showInfo(Mockito.any())).thenReturn(true); - Optional opi = Optional.of(is); - when(im.getIslandAt(Mockito.any())).thenReturn(opi); - when(user.getLocation()).thenReturn(loc); + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringArgsNoIsland() { + when(im.getIsland(any(), any(UUID.class))).thenReturn(null); + assertFalse(iic.execute(user, "", Collections.singletonList("tastybento"))); + verify(user).sendMessage("general.errors.player-has-no-island"); + } + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.AdminInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringArgsUnknownPlayer() { + PowerMockito.mockStatic(Util.class); + when(Util.getUUID(any())).thenReturn(null); + assertFalse(iic.execute(user, "", Collections.singletonList("tastybento"))); + verify(user).sendMessage("general.errors.unknown-player", "[name]", "tastybento"); - assertTrue(itl.execute(user, itl.getLabel(), new ArrayList<>())); - // Confirm other verifications - Mockito.verify(is).showInfo(Mockito.eq(user)); } -} \ No newline at end of file + +} diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommandTest.java index 22d7b7178..4f5e64f4f 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminResetFlagsCommandTest.java @@ -58,7 +58,7 @@ public class AdminResetFlagsCommandTest { @Mock private CompositeCommand ac; - private UUID uuid = UUID.randomUUID(); + private final UUID uuid = UUID.randomUUID(); @Mock private IslandsManager im; @Mock diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java index 15f13eddd..c44e696ab 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java @@ -2,7 +2,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -90,15 +93,15 @@ public void setUp() throws Exception { // Player has island to begin with im = mock(IslandsManager.class); - when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); - when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); - when(im.getOwner(Mockito.any(), Mockito.any())).thenReturn(uuid); + when(im.hasIsland(any(), any(User.class))).thenReturn(true); + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + when(im.isOwner(any(), any())).thenReturn(true); + when(im.getOwner(any(), any())).thenReturn(uuid); when(plugin.getIslands()).thenReturn(im); // Has team pm = mock(PlayersManager.class); - when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.inTeam(any(), eq(uuid))).thenReturn(true); when(plugin.getPlayers()).thenReturn(pm); @@ -112,16 +115,16 @@ public void setUp() throws Exception { // Locales LocalesManager lm = mock(LocalesManager.class); - when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(lm.get(any(), any())).thenReturn("mock translation"); when(plugin.getLocalesManager()).thenReturn(lm); // Island World Manager IslandWorldManager iwm = mock(IslandWorldManager.class); - when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); when(plugin.getIWM()).thenReturn(iwm); // Addon - when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty()); + when(iwm.getAddon(any())).thenReturn(Optional.empty()); } @@ -158,16 +161,16 @@ public void testExecuteUnknownPlayer() { String[] name = {"tastybento", "poslovich"}; // Unknown owner - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(null); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(null); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); assertFalse(itl.execute(user, ac.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("general.errors.unknown-player", "[name]", "tastybento"); + verify(user).sendMessage("general.errors.unknown-player", "[name]", "tastybento"); // Unknown target - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(null); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(null); assertFalse(itl.execute(user, ac.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("general.errors.unknown-player", "[name]", "poslovich"); + verify(user).sendMessage("general.errors.unknown-player", "[name]", "poslovich"); } /** @@ -178,13 +181,13 @@ public void testExecuteTargetTargetInTeam() { AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); String[] name = {"tastybento", "poslovich"}; - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); - when(im.inTeam(Mockito.any(), Mockito.eq(notUUID))).thenReturn(true); + when(im.inTeam(any(), eq(notUUID))).thenReturn(true); assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage(Mockito.eq("commands.island.team.invite.errors.already-on-team")); + verify(user).sendMessage(eq("commands.island.team.invite.errors.already-on-team")); } @@ -196,14 +199,14 @@ public void testExecuteAddNoIsland() { AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); String[] name = {"tastybento", "poslovich"}; - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); // No island, - when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.hasIsland(any(), eq(uuid))).thenReturn(false); assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("general.errors.player-has-no-island"); + verify(user).sendMessage("general.errors.player-has-no-island"); } @@ -215,21 +218,21 @@ public void testExecuteAddNotOwner() { AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); String[] name = {"tastybento", "poslovich"}; - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); // Has island, has team, but not an owner - when(im.hasIsland(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); - when(im.inTeam(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); - when(im.getOwner(Mockito.any(),Mockito.eq(uuid))).thenReturn(notUUID); + when(im.hasIsland(any(),eq(uuid))).thenReturn(true); + when(im.inTeam(any(),eq(uuid))).thenReturn(true); + when(im.getOwner(any(),eq(uuid))).thenReturn(notUUID); // Island Island island = mock(Island.class); - when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(island); + when(im.getIsland(any(), eq(uuid))).thenReturn(island); assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("commands.admin.team.add.name-not-owner", "[name]", "tastybento"); - Mockito.verify(island).showMembers(Mockito.any()); + verify(user).sendMessage("commands.admin.team.add.name-not-owner", "[name]", "tastybento"); + verify(user).sendMessage("commands.admin.info.team-members-title"); } /** @@ -240,19 +243,19 @@ public void testExecuteAddTargetHasIsland() { AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); String[] name = {"tastybento", "poslovich"}; - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); // Has island, has team, is owner - when(im.hasIsland(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); - when(im.inTeam(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); - when(im.getOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(uuid); + when(im.hasIsland(any(),eq(uuid))).thenReturn(true); + when(im.inTeam(any(),eq(uuid))).thenReturn(true); + when(im.getOwner(any(), eq(uuid))).thenReturn(uuid); // Target has island - when(im.hasIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(true); + when(im.hasIsland(any(), eq(notUUID))).thenReturn(true); assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("commands.admin.team.add.name-has-island", "[name]", "poslovich"); + verify(user).sendMessage("commands.admin.team.add.name-has-island", "[name]", "poslovich"); } @@ -264,18 +267,18 @@ public void testExecuteAddTargetHasIslandNoTeam() { AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); String[] name = {"tastybento", "poslovich"}; - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); // Has island, no team - when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); - when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.hasIsland(any(), eq(uuid))).thenReturn(true); + when(im.inTeam(any(), eq(uuid))).thenReturn(false); // Target has island - when(im.hasIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(true); + when(im.hasIsland(any(), eq(notUUID))).thenReturn(true); assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(user).sendMessage("commands.admin.team.add.name-has-island", "[name]", "poslovich"); + verify(user).sendMessage("commands.admin.team.add.name-has-island", "[name]", "poslovich"); } @@ -287,29 +290,29 @@ public void testExecuteSuccess() { AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); String[] name = {"tastybento", "poslovich"}; - when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); - when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + when(pm.getUUID(eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); // Has island, no team - when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); - when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.hasIsland(any(), eq(uuid))).thenReturn(true); + when(im.inTeam(any(), eq(uuid))).thenReturn(false); // Target has no island - when(im.hasIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(false); + when(im.hasIsland(any(), eq(notUUID))).thenReturn(false); // Island Island island = mock(Island.class); - when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(island); + when(im.getIsland(any(), eq(uuid))).thenReturn(island); // Player name - when(pm.getName(Mockito.eq(uuid))).thenReturn("tastybento"); - when(pm.getName(Mockito.eq(notUUID))).thenReturn("poslovich"); + when(pm.getName(eq(uuid))).thenReturn("tastybento"); + when(pm.getName(eq(notUUID))).thenReturn("poslovich"); when(plugin.getPlayers()).thenReturn(pm); // Success assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); - Mockito.verify(im).setJoinTeam(Mockito.eq(island), Mockito.eq(notUUID)); - Mockito.verify(user).sendMessage("commands.admin.team.add.success", TextVariables.NAME, name[1], "[owner]", name[0]); + verify(im).setJoinTeam(eq(island), eq(notUUID)); + verify(user).sendMessage("commands.admin.team.add.success", TextVariables.NAME, name[1], "[owner]", name[0]); } } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommandTest.java index 6161b07a4..aed70d1c0 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommandTest.java @@ -190,7 +190,7 @@ public void testExecuteKickOwner() { assertTrue(itl.canExecute(user, itl.getLabel(), Collections.singletonList("tastybento"))); assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("tastybento"))); verify(user).sendMessage(eq("commands.admin.team.kick.cannot-kick-owner")); - verify(is).showMembers(any()); + verify(user).sendMessage("commands.admin.info.team-members-title"); verify(im, never()).removePlayer(eq(world), eq(notUUID)); verify(pm, never()).clearHomeLocations(eq(world), eq(notUUID)); verify(user, never()).sendMessage(eq("commands.admin.team.kick.success"), anyString(), anyString(), anyString(), anyString()); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java index ce858a90c..a9a2c752f 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java @@ -162,7 +162,7 @@ public void setUp() throws Exception { // Locales LocalesManager lm = mock(LocalesManager.class); - when(lm.get(Mockito.any(), Mockito.any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); when(plugin.getLocalesManager()).thenReturn(lm); // Return the same string PlaceholdersManager phm = mock(PlaceholdersManager.class); @@ -172,8 +172,8 @@ public void setUp() throws Exception { // Notifier when(plugin.getNotifier()).thenReturn(notifier); - // Util strip spaces - when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); // Command igc = new IslandGoCommand(ic); @@ -186,6 +186,16 @@ public void tearDown() { Mockito.framework().clearInlineMocks(); } + /** + * Test method for {@link IslandGoCommand#canExecute(User, String, List)} + */ + @Test + public void testExecuteMidTeleport() { + when(im.isGoingHome(user)).thenReturn(true); + assertFalse(igc.canExecute(user, igc.getLabel(), Collections.emptyList())); + verify(player).sendMessage("commands.island.go.teleport"); + } + /** * Test method for {@link IslandGoCommand#canExecute(User, String, List)} */ diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java index ef19a03f0..db650ebb5 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java @@ -5,7 +5,9 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -15,9 +17,12 @@ import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.NonNull; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -25,6 +30,7 @@ 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; @@ -34,10 +40,12 @@ 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.LocalesManager; import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; /** @@ -50,13 +58,13 @@ public class IslandInfoCommandTest { @Mock private CompositeCommand ic; + @Mock private User user; @Mock private IslandsManager im; @Mock private PlayersManager pm; - @Mock private Island island; private IslandInfoCommand iic; @@ -67,6 +75,10 @@ public class IslandInfoCommandTest { private World world; @Mock private PlaceholdersManager phm; + @Mock + private @NonNull Location location; + @Mock + private IslandWorldManager iwm; /** * @throws java.lang.Exception @@ -77,6 +89,12 @@ public void setUp() throws Exception { BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); + // IWM + when(plugin.getIWM()).thenReturn(iwm); + when(plugin.getRanksManager()).thenReturn(new RanksManager()); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); // Command manager CommandsManager cm = mock(CommandsManager.class); when(plugin.getCommandsManager()).thenReturn(cm); @@ -84,10 +102,12 @@ public void setUp() throws Exception { // Player when(player.isOp()).thenReturn(false); UUID uuid = UUID.randomUUID(); - when(player.getUniqueId()).thenReturn(uuid); - when(player.getName()).thenReturn("tastybento"); - when(player.getWorld()).thenReturn(world); - user = User.getInstance(player); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getName()).thenReturn("tastybento"); + when(user.getWorld()).thenReturn(world); + when(user.getPlayer()).thenReturn(player); + when(user.isPlayer()).thenReturn(true); + //user = User.getInstance(player); // Set the User class plugin as this one User.setPlugin(plugin); @@ -98,12 +118,16 @@ public void setUp() throws Exception { // Return the same string when(phm.replacePlaceholders(any(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); when(plugin.getPlaceholdersManager()).thenReturn(phm); + // Translate + when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getTranslation(anyString(), anyString(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); // Island manager + island = new Island(location, uuid, 100); + when(location.toVector()).thenReturn(new Vector(1,2,3)); when(plugin.getIslands()).thenReturn(im); Optional optionalIsland = Optional.of(island); when(im.getIslandAt(any())).thenReturn(optionalIsland); - when(island.showInfo(any())).thenReturn(true); when(im.getIsland(any(), any(UUID.class))).thenReturn(island); // Players manager @@ -141,7 +165,7 @@ public void testSetup() { @Test public void testExecuteUserStringListOfStringTooManyArgs() { assertFalse(iic.execute(user, "", Arrays.asList("hdhh", "hdhdhd"))); - verify(player).sendMessage("commands.help.header"); + verify(user).sendMessage("commands.help.header", "[label]", "commands.help.console"); } /** @@ -152,17 +176,7 @@ public void testExecuteUserStringListOfStringNoArgsConsole() { CommandSender console = mock(CommandSender.class); User sender = User.getInstance(console); assertFalse(iic.execute(sender, "", Collections.emptyList())); - verify(console).sendMessage("commands.help.header"); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandInfoCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfStringNoArgsNoIslandFalseInfo() { - when(island.showInfo(any())).thenReturn(false); - assertFalse(iic.execute(user, "", Collections.emptyList())); - verify(player).sendMessage("commands.admin.info.no-island"); + verify(user, never()).sendMessage("commands.help.header", "[label]", "commands.help.console"); } /** @@ -172,7 +186,7 @@ public void testExecuteUserStringListOfStringNoArgsNoIslandFalseInfo() { public void testExecuteUserStringListOfStringNoArgsNoIsland() { when(im.getIslandAt(any())).thenReturn(Optional.empty()); assertFalse(iic.execute(user, "", Collections.emptyList())); - verify(player).sendMessage("commands.admin.info.no-island"); + verify(user).sendMessage("commands.admin.info.no-island"); } /** @@ -181,7 +195,15 @@ public void testExecuteUserStringListOfStringNoArgsNoIsland() { @Test public void testExecuteUserStringListOfStringNoArgsSuccess() { assertTrue(iic.execute(user, "", Collections.emptyList())); - verify(island).showInfo(any()); + verify(user).sendMessage("commands.admin.info.title"); + verify(user).sendMessage(eq("commands.admin.info.owner"), eq("[owner]"), eq(null)); + verify(user).sendMessage("commands.admin.info.deaths", "[number]", "0"); + verify(user).sendMessage("commands.admin.info.resets-left", "[number]", "0", "[total]", "0"); + verify(user).sendMessage("commands.admin.info.team-members-title"); + verify(user).sendMessage("commands.admin.info.team-owner-format", "[name]", null, "[rank]", "ranks.owner"); + verify(user).sendMessage("commands.admin.info.island-center", "[xyz]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.protection-range", "[range]", "100"); + verify(user).sendMessage("commands.admin.info.protection-coords", "[xz1]", "0,0,0", "[xz2]", "0,0,0"); } /** @@ -190,7 +212,15 @@ public void testExecuteUserStringListOfStringNoArgsSuccess() { @Test public void testExecuteUserStringListOfStringArgsSuccess() { assertTrue(iic.execute(user, "", Collections.singletonList("tastybento"))); - verify(island).showInfo(any()); + verify(user).sendMessage("commands.admin.info.title"); + verify(user).sendMessage(eq("commands.admin.info.owner"), eq("[owner]"), eq(null)); + verify(user).sendMessage("commands.admin.info.deaths", "[number]", "0"); + verify(user).sendMessage("commands.admin.info.resets-left", "[number]", "0", "[total]", "0"); + verify(user).sendMessage("commands.admin.info.team-members-title"); + verify(user).sendMessage("commands.admin.info.team-owner-format", "[name]", null, "[rank]", "ranks.owner"); + verify(user).sendMessage("commands.admin.info.island-center", "[xyz]", "0,0,0"); + verify(user).sendMessage("commands.admin.info.protection-range", "[range]", "100"); + verify(user).sendMessage("commands.admin.info.protection-coords", "[xz1]", "0,0,0", "[xz2]", "0,0,0"); } /** @@ -200,7 +230,7 @@ public void testExecuteUserStringListOfStringArgsSuccess() { public void testExecuteUserStringListOfStringArgsNoIsland() { when(im.getIsland(any(), any(UUID.class))).thenReturn(null); assertFalse(iic.execute(user, "", Collections.singletonList("tastybento"))); - verify(player).sendMessage("general.errors.player-has-no-island"); + verify(user).sendMessage("general.errors.player-has-no-island"); } /** @@ -210,8 +240,8 @@ public void testExecuteUserStringListOfStringArgsNoIsland() { public void testExecuteUserStringListOfStringArgsUnknownPlayer() { when(pm.getUUID(any())).thenReturn(null); assertFalse(iic.execute(user, "", Collections.singletonList("tastybento"))); - verify(player).sendMessage("general.errors.unknown-player"); - verify(phm).replacePlaceholders(player, "general.errors.unknown-player"); + verify(user).sendMessage("general.errors.unknown-player", "[name]", "tastybento"); + } } diff --git a/src/test/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapterTest.java b/src/test/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapterTest.java index 2399f3912..97240fff6 100644 --- a/src/test/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapterTest.java +++ b/src/test/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapterTest.java @@ -23,7 +23,7 @@ public class LogEntryListAdapterTest { private LogEntryListAdapter a; private YamlConfiguration config; - private List history = new LinkedList<>(); + private final List history = new LinkedList<>(); private UUID target; private UUID issuer; private List toLog; diff --git a/src/test/java/world/bentobox/bentobox/database/sql/mysql/MySQLDatabaseHandlerTest.java b/src/test/java/world/bentobox/bentobox/database/sql/mysql/MySQLDatabaseHandlerTest.java index b7a788282..eb0cfff35 100644 --- a/src/test/java/world/bentobox/bentobox/database/sql/mysql/MySQLDatabaseHandlerTest.java +++ b/src/test/java/world/bentobox/bentobox/database/sql/mysql/MySQLDatabaseHandlerTest.java @@ -66,7 +66,7 @@ public class MySQLDatabaseHandlerTest { "}"; private MySQLDatabaseHandler handler; private Island instance; - private String UNIQUE_ID = "xyz"; + private final String UNIQUE_ID = "xyz"; @Mock private MySQLDatabaseConnector dbConn; @Mock diff --git a/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java b/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java index 71d87891e..2779bbbeb 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java @@ -320,7 +320,7 @@ public void testBannedCommandsWithBannedFallingCommandNoFlag() { */ class MyWorldSettings implements WorldSettings { - private Map worldFlags = new HashMap<>(); + private final Map worldFlags = new HashMap<>(); @Override public @NonNull List getOnLeaveCommands() { diff --git a/src/test/java/world/bentobox/bentobox/listeners/BlockEndDragonTest.java b/src/test/java/world/bentobox/bentobox/listeners/BlockEndDragonTest.java index 251db34a3..11ad13ffb 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/BlockEndDragonTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/BlockEndDragonTest.java @@ -277,7 +277,7 @@ public void testOnEndBlockBreak() { */ class MyWorldSettings implements WorldSettings { - private Map worldFlags = new HashMap<>(); + private final Map worldFlags = new HashMap<>(); @Override public @NonNull List getOnLeaveCommands() { diff --git a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java index 763625cf1..6a1bc21d7 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java @@ -194,10 +194,10 @@ public void setUp() throws Exception { // Util PowerMockito.mockStatic(Util.class); when(Util.getWorld(any())).thenReturn(world); - when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); // user text - LocalesManager lm = mock(LocalesManager.class); when(plugin.getLocalesManager()).thenReturn(lm); when(lm.get(any(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); diff --git a/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java b/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java index 3e043401d..eedbac464 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java @@ -128,8 +128,8 @@ public void setUp() throws Exception { class MyView extends InventoryView { - private Inventory top; - private String name; + private final Inventory top; + private final String name; /** * @param name diff --git a/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java index b1f0dd5ce..d46c61259 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java @@ -7,7 +7,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -19,6 +18,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Server; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Block; @@ -154,6 +154,9 @@ public void setUp() throws Exception { BukkitScheduler sch = mock(BukkitScheduler.class); PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getScheduler()).thenReturn(sch); + Server server = mock(Server.class); + when(server.getAllowNether()).thenReturn(true); + when(Bukkit.getServer()).thenReturn(server); // Locales LocalesManager lm = mock(LocalesManager.class); @@ -284,7 +287,7 @@ public void testonIslandPortalHome() { when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); np.onIslandPortal(e); assertTrue(e.isCancelled()); - verify(im, times(2)).homeTeleportAsync(any(), eq(player)); + verify(im).homeTeleportAsync(any(), eq(player)); } /** diff --git a/src/test/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListenerTest.java index ee85f7d23..daec95f7e 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListenerTest.java @@ -129,8 +129,8 @@ public void setUp() throws Exception { // Block when(block.getLocation()).thenReturn(location); - // Util strip spaces - when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); // Set up class ssp = new StandardSpawnProtectionListener(plugin); diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java index c4d8aafce..ba9131e91 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java @@ -161,9 +161,8 @@ public void setUp() throws Exception { PowerMockito.mockStatic(Util.class); when(Util.getWorld(any())).thenReturn(mock(World.class)); - // Util strip spaces - when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); - + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java index ad3cac559..8c722f717 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java @@ -56,9 +56,9 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup { @Mock private Block clickedBlock; - private Map inHandItems = new EnumMap<>(Material.class); + private final Map inHandItems = new EnumMap<>(Material.class); - private Map clickedBlocks = new EnumMap<>(Material.class); + private final Map clickedBlocks = new EnumMap<>(Material.class); private void setFlags() { inHandItems.put(Material.ENDER_PEARL, Flags.ENDER_PEARL); diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/FireListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/FireListenerTest.java index 3764bde23..f43634ea8 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/FireListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/FireListenerTest.java @@ -61,7 +61,7 @@ public class FireListenerTest { @Mock private World world; - private Map worldFlags = new HashMap<>(); + private final Map worldFlags = new HashMap<>(); @Before public void setUp() { diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java index b67dd9706..be381b07a 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java @@ -230,8 +230,8 @@ public void setUp() throws Exception { // Addon when(iwm.getAddon(any())).thenReturn(Optional.empty()); - // Util strip spaces - when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); } @@ -303,6 +303,8 @@ public void testOnEntityDamageOnPlayerByZombie() { when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); when(damager.getWorld()).thenReturn(world); when(damagee.getWorld()).thenReturn(world); + when(damager.getLocation()).thenReturn(loc); + when(damagee.getLocation()).thenReturn(loc); EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, new EnumMap<>(ImmutableMap.of(DamageModifier.BASE, 0D)), new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); @@ -337,7 +339,8 @@ public void testOnEntityDamageOnPlayerByZombieVisitorProtected() { when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); when(damager.getWorld()).thenReturn(world); when(damagee.getWorld()).thenReturn(world); - + when(damager.getLocation()).thenReturn(loc); + when(damagee.getLocation()).thenReturn(loc); // Protect visitors List visitorProtectionList = new ArrayList<>(); visitorProtectionList.add("ENTITY_ATTACK"); @@ -367,7 +370,8 @@ public void testOnEntityDamageOnVisitorByZombieVisitorProtected() { Entity damagee = mock(Player.class); when(damager.getWorld()).thenReturn(world); when(damagee.getWorld()).thenReturn(world); - + when(damager.getLocation()).thenReturn(loc); + when(damagee.getLocation()).thenReturn(loc); // Protect visitors when(iwm.getIvSettings(world)).thenReturn(Collections.singletonList("ENTITY_ATTACK")); // This player is a visitor @@ -391,7 +395,8 @@ public void testOnEntityDamageOnVisitorByZombieVisitorProtectedWrongWorld() { when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); when(damager.getWorld()).thenReturn(world); when(damagee.getWorld()).thenReturn(world); - + when(damager.getLocation()).thenReturn(loc); + when(damagee.getLocation()).thenReturn(loc); // Protect visitors List visitorProtectionList = new ArrayList<>(); visitorProtectionList.add("ENTITY_ATTACK"); @@ -418,7 +423,8 @@ public void testOnEntityDamageOnVisitorByZombieVisitorProtectedWrongDamage() { when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); when(damager.getWorld()).thenReturn(world); when(damagee.getWorld()).thenReturn(world); - + when(damager.getLocation()).thenReturn(loc); + when(damagee.getLocation()).thenReturn(loc); // Protect visitors List visitorProtectionList = new ArrayList<>(); visitorProtectionList.add("ENTITY_ATTACK"); @@ -448,7 +454,8 @@ public void testOnEntityDamageOnVisitorByZombieVisitorNotProtected() { when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); when(damager.getWorld()).thenReturn(world); when(damagee.getWorld()).thenReturn(world); - + when(damager.getLocation()).thenReturn(loc); + when(damagee.getLocation()).thenReturn(loc); // This player is a visitor when(im.userIsOnIsland(any(), any())).thenReturn(false); @@ -532,9 +539,8 @@ public void testOnEntityDamageOnPVPAllowed() { when(im.userIsOnIsland(any(), any())).thenReturn(false); when(iwm.getIvSettings(any())).thenReturn(Collections.singletonList("ENTITY_ATTACK")); new PVPListener().onEntityDamage(e); - // visitor should be protected - assertTrue(e.isCancelled()); - verify(notifier).notify(any(), eq(Flags.INVINCIBLE_VISITORS.getHintReference())); + // visitor should not be protected + assertFalse(e.isCancelled()); } @@ -606,9 +612,8 @@ public void testOnEntityDamagePVPAllowedProjectile() { when(im.userIsOnIsland(any(), any())).thenReturn(false); when(iwm.getIvSettings(any())).thenReturn(Collections.singletonList("ENTITY_ATTACK")); new PVPListener().onEntityDamage(e); - // visitor should be protected - assertTrue(e.isCancelled()); - verify(notifier).notify(any(), eq(Flags.INVINCIBLE_VISITORS.getHintReference())); + // visitor should not be protected + assertFalse(e.isCancelled()); } @@ -723,9 +728,8 @@ public void testOnFishingProtectVisitors() { when(im.userIsOnIsland(any(), any())).thenReturn(false); when(iwm.getIvSettings(any())).thenReturn(Collections.singletonList("ENTITY_ATTACK")); new PVPListener().onFishing(pfe); - // visitor should be protected - assertTrue(pfe.isCancelled()); - verify(notifier).notify(any(), eq(Flags.INVINCIBLE_VISITORS.getHintReference())); + // visitor should not be protected + assertFalse(pfe.isCancelled()); } /** @@ -785,10 +789,13 @@ public void testOnSplashPotionSplashNoPlayers() { ThrownPotion tp = mock(ThrownPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); // Create a damage map Map map = new HashMap<>(); map.put(zombie, 100D); map.put(creeper, 10D); + when(zombie.getLocation()).thenReturn(loc); + when(creeper.getLocation()).thenReturn(loc); PotionSplashEvent e = new PotionSplashEvent(tp, map); new PVPListener().onSplashPotionSplash(e); assertFalse(e.isCancelled()); @@ -805,6 +812,7 @@ public void testOnSplashPotionSplash() { ThrownPotion tp = mock(ThrownPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); // Create a damage map Map map = new HashMap<>(); map.put(player2, 100D); @@ -835,6 +843,7 @@ public void testOnSplashPotionSplashSelfInflicted() { ThrownPotion tp = mock(ThrownPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); // Create a damage map Map map = new HashMap<>(); map.put(player, 100D); @@ -862,6 +871,7 @@ public void testOnSplashPotionSplashAllowPVP() { ThrownPotion tp = mock(ThrownPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); // Create a damage map Map map = new HashMap<>(); map.put(player2, 100D); @@ -887,6 +897,7 @@ public void testOnSplashPotionSplashAllowPVPProtectVisitors() { ThrownPotion tp = mock(ThrownPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); // Create a damage map Map map = new HashMap<>(); map.put(player2, 100D); @@ -898,11 +909,10 @@ public void testOnSplashPotionSplashAllowPVPProtectVisitors() { when(im.userIsOnIsland(any(), any())).thenReturn(false); when(iwm.getIvSettings(any())).thenReturn(Collections.singletonList("ENTITY_ATTACK")); new PVPListener().onSplashPotionSplash(e); - // visitor should be protected - assertFalse(e.getAffectedEntities().contains(player2)); + // visitor should not be protected + assertTrue(e.getAffectedEntities().contains(player2)); assertTrue(e.getAffectedEntities().contains(zombie)); assertTrue(e.getAffectedEntities().contains(creeper)); - verify(notifier).notify(any(), eq(Flags.INVINCIBLE_VISITORS.getHintReference())); // Wrong world wrongWorld(); @@ -919,6 +929,7 @@ public void testOnLingeringPotionSplash() { LingeringPotion tp = mock(LingeringPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); AreaEffectCloud cloud = mock(AreaEffectCloud.class); LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); new PVPListener().onLingeringPotionSplash(e); @@ -938,6 +949,7 @@ public void testOnLingeringPotionSplashNonHuman() { LingeringPotion tp = mock(LingeringPotion.class); when(tp.getShooter()).thenReturn(creeper); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); AreaEffectCloud cloud = mock(AreaEffectCloud.class); LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); new PVPListener().onLingeringPotionSplash(e); @@ -959,6 +971,7 @@ public void testOnLingeringPotionDamageNoPVP() { LingeringPotion tp = mock(LingeringPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); AreaEffectCloud cloud = mock(AreaEffectCloud.class); when(cloud.getWorld()).thenReturn(world); LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); @@ -995,6 +1008,7 @@ public void testOnLingeringPotionDamagePVP() { LingeringPotion tp = mock(LingeringPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); AreaEffectCloud cloud = mock(AreaEffectCloud.class); when(cloud.getWorld()).thenReturn(world); LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); @@ -1029,6 +1043,7 @@ public void testOnLingeringPotionDamageNoPVPVisitor() { LingeringPotion tp = mock(LingeringPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); AreaEffectCloud cloud = mock(AreaEffectCloud.class); when(cloud.getWorld()).thenReturn(world); LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); @@ -1069,6 +1084,7 @@ public void testOnLingeringPotionDamagePVPVisitor() { LingeringPotion tp = mock(LingeringPotion.class); when(tp.getShooter()).thenReturn(player); when(tp.getWorld()).thenReturn(world); + when(tp.getLocation()).thenReturn(loc); AreaEffectCloud cloud = mock(AreaEffectCloud.class); when(cloud.getWorld()).thenReturn(world); LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListenerTest.java index b80c776a4..4a3df7dd2 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListenerTest.java @@ -204,8 +204,8 @@ public void setUp() throws Exception { // Flags Flags.ENTER_EXIT_MESSAGES.setSetting(world, true); - // Util strip spaces - when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); } @After diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListenerTest.java index b88b496b4..4697a8d61 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListenerTest.java @@ -27,6 +27,7 @@ import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.World.Environment; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent; @@ -43,6 +44,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -79,6 +81,10 @@ public class InvincibleVisitorsListenerTest { private Optional optionalIsland; @Mock private GameModeAddon addon; + @Mock + private Location location; + @Mock + private World world; /** * @throws java.lang.Exception @@ -113,10 +119,12 @@ public void setUp() throws Exception { when(panel.getItems()).thenReturn(map); // Sometimes use Mockito.withSettings().verboseLogging() when(user.inWorld()).thenReturn(true); - when(user.getWorld()).thenReturn(mock(World.class)); - when(player.getWorld()).thenReturn(mock(World.class)); - when(user.getLocation()).thenReturn(mock(Location.class)); - when(player.getLocation()).thenReturn(mock(Location.class)); + when(user.getWorld()).thenReturn(world); + when(player.getWorld()).thenReturn(world); + when(location.getWorld()).thenReturn(world); + when(user.getLocation()).thenReturn(location); + when(player.getLocation()).thenReturn(location); + when(world.getEnvironment()).thenReturn(Environment.NORMAL); when(user.getPlayer()).thenReturn(player); when(user.hasPermission(anyString())).thenReturn(true); when(user.getTranslation(anyString())).thenReturn("panel"); @@ -127,6 +135,8 @@ public void setUp() throws Exception { PowerMockito.mockStatic(Util.class); when(Util.getWorld(any())).thenReturn(mock(World.class)); when(Util.prettifyText(anyString())).thenCallRealMethod(); + // Util translate color codes (used in user translate methods) + when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); FlagsManager fm = mock(FlagsManager.class); Flag flag = mock(Flag.class); when(flag.isSetForWorld(any())).thenReturn(false); diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java index 8c486d774..73f134462 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java @@ -12,6 +12,7 @@ import java.util.logging.Logger; import org.bukkit.Bukkit; +import org.bukkit.FluidCollisionMode; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -27,6 +28,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.plugin.PluginManager; +import org.bukkit.util.RayTraceResult; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -60,7 +62,7 @@ public class ObsidianScoopingListenerTest { @Mock private BentoBox plugin; @Mock - private Player who; + private Player p; @Mock private IslandWorldManager iwm; @Mock @@ -93,16 +95,19 @@ public void setUp() throws Exception { listener = new ObsidianScoopingListener(); // Mock player - when(who.getWorld()).thenReturn(world); + when(p.getWorld()).thenReturn(world); + RayTraceResult rtr = mock(RayTraceResult.class); + when(p.rayTraceBlocks(5, FluidCollisionMode.ALWAYS)).thenReturn(rtr); + when(rtr.getHitBlock()).thenReturn(clickedBlock); Location location = mock(Location.class); when(location.getWorld()).thenReturn(world); when(location.getBlockX()).thenReturn(0); when(location.getBlockY()).thenReturn(0); when(location.getBlockZ()).thenReturn(0); - when(who.getLocation()).thenReturn(location); + when(p.getLocation()).thenReturn(location); - when(who.getInventory()).thenReturn(mock(PlayerInventory.class)); + when(p.getInventory()).thenReturn(mock(PlayerInventory.class)); // Worlds when(plugin.getIWM()).thenReturn(iwm); @@ -130,7 +135,7 @@ public void setUp() throws Exception { // Put player on island when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); // Set as survival - when(who.getGameMode()).thenReturn(GameMode.SURVIVAL); + when(p.getGameMode()).thenReturn(GameMode.SURVIVAL); // Locales when(plugin.getLocalesManager()).thenReturn(lm); @@ -208,7 +213,7 @@ public void testOnPlayerInteractObsidianManyBucketsInHand() { @Test public void testOnPlayerInteractNotInWorld() { - PlayerInteractEvent event = new PlayerInteractEvent(who, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); + PlayerInteractEvent event = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); // Test not in world when(iwm.inWorld(any(World.class))).thenReturn(false); when(iwm.inWorld(any(Location.class))).thenReturn(false); @@ -224,11 +229,11 @@ public void testOnPlayerInteractInWorld() { @Test public void testOnPlayerInteractGameModes() { - PlayerInteractEvent event = new PlayerInteractEvent(who, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); + PlayerInteractEvent event = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); // Test different game modes for (GameMode gm : GameMode.values()) { - when(who.getGameMode()).thenReturn(gm); + when(p.getGameMode()).thenReturn(gm); if (!gm.equals(GameMode.SURVIVAL)) { assertFalse(listener.onPlayerInteract(event)); } @@ -237,10 +242,10 @@ public void testOnPlayerInteractGameModes() { @Test public void testOnPlayerInteractSurvivalNotOnIsland() { - PlayerInteractEvent event = new PlayerInteractEvent(who, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); + PlayerInteractEvent event = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); // Set as survival - when(who.getGameMode()).thenReturn(GameMode.SURVIVAL); + when(p.getGameMode()).thenReturn(GameMode.SURVIVAL); // Positive test with 1 bucket in the stack inHand = Material.BUCKET; @@ -262,7 +267,7 @@ private void testEvent() { when(airBlock.getType()).thenReturn(Material.AIR); ObsidianScoopingListener listener = new ObsidianScoopingListener(); - PlayerInteractEvent event = new PlayerInteractEvent(who, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); + PlayerInteractEvent event = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST); if (!item.getType().equals(Material.BUCKET) || !clickedBlock.getType().equals(Material.OBSIDIAN)) { assertFalse(listener.onPlayerInteract(event)); diff --git a/src/test/java/world/bentobox/bentobox/lists/GameModePlaceholderTest.java b/src/test/java/world/bentobox/bentobox/lists/GameModePlaceholderTest.java index 52fc327bc..7de4357d5 100644 --- a/src/test/java/world/bentobox/bentobox/lists/GameModePlaceholderTest.java +++ b/src/test/java/world/bentobox/bentobox/lists/GameModePlaceholderTest.java @@ -58,7 +58,7 @@ public class GameModePlaceholderTest { private IslandWorldManager iwm; @Mock private IslandsManager im; - private RanksManager rm = new RanksManager(); + private final RanksManager rm = new RanksManager(); @Mock private @Nullable Location location; diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java index 976a40b0f..d0f259b4b 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java @@ -56,7 +56,7 @@ public class BlueprintClipboardManagerTest { private File blueprintFolder; - private String json = "{\n" + + private final String json = "{\n" + " \"name\": \"blueprint\",\n" + " \"attached\": {},\n" + " \"entities\": {},\n" + @@ -78,7 +78,7 @@ public class BlueprintClipboardManagerTest { " \"bedrock\": [-2.0, -16.0, -1.0]\n" + "}"; - private String jsonNoBedrock = "{\n" + + private final String jsonNoBedrock = "{\n" + " \"name\": \"blueprint\",\n" + " \"attached\": {},\n" + " \"entities\": {},\n" + diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java index 357ddbf30..ae96e552c 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java @@ -667,7 +667,7 @@ public void testRenameBlueprint() { * Utility methods */ private void createJarArchive(File archiveFile, File folder, List tobeJaredList) { - byte buffer[] = new byte[BUFFER_SIZE]; + byte[] buffer = new byte[BUFFER_SIZE]; // Open archive file try (FileOutputStream stream = new FileOutputStream(archiveFile)) { try (JarOutputStream out = new JarOutputStream(stream, new Manifest())) { diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java index 2eafddbf6..9567fabe2 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java @@ -138,7 +138,7 @@ public void testOnBentoBoxReadyNullWorld() { BentoBoxReadyEvent e = new BentoBoxReadyEvent(); idm.onBentoBoxReady(e); verify(plugin).log("There are 1 islands pending deletion."); - verify(plugin).logError("Island queued for deletion refers to a non-existant game world. Skipping..."); + verify(plugin).logError("Island queued for deletion refers to a non-existent game world. Skipping..."); } /** diff --git a/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java b/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java index cea1f3251..fd11d337b 100644 --- a/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java @@ -49,7 +49,7 @@ public class IslandCacheTest { @Mock private Island island; // UUID - private UUID owner = UUID.randomUUID(); + private final UUID owner = UUID.randomUUID(); @Mock private Location location; // Test class diff --git a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java index eae058f24..87c690e32 100644 --- a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java @@ -93,7 +93,7 @@ public class NewIslandTest { @Mock private BlueprintBundle bpb; - private UUID uuid = UUID.randomUUID(); + private final UUID uuid = UUID.randomUUID(); @Mock private BlueprintsManager bpm; diff --git a/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java b/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java index a9dd12849..7ebecd6cd 100644 --- a/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java +++ b/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java @@ -27,18 +27,24 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import world.bentobox.bentobox.BentoBox; + + @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class}) +@PrepareForTest({BentoBox.class, Bukkit.class}) public class ItemParserTest { private PotionMeta potionMeta; private BannerMeta bannerMeta; + private ItemStack defaultItem; @Before public void setUp() throws Exception { PowerMockito.mockStatic(Bukkit.class); + PowerMockito.mockStatic(BentoBox.class); ItemFactory itemFactory = mock(ItemFactory.class); when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(BentoBox.getInstance()).thenReturn(mock(BentoBox.class)); potionMeta = mock(PotionMeta.class); /* when(itemFactory.getItemMeta(Mockito.eq(Material.POTION))).thenReturn(potionMeta); @@ -62,6 +68,7 @@ public void setUp() throws Exception { } }); + defaultItem = new ItemStack(Material.STONE); } @After @@ -72,16 +79,19 @@ public void tearDown() { @Test public void testParseNull() { assertNull(ItemParser.parse(null)); + assertEquals(defaultItem, ItemParser.parse(null, defaultItem)); } @Test public void testParseBlank() { assertNull(ItemParser.parse("")); + assertEquals(defaultItem, ItemParser.parse("", defaultItem)); } @Test public void testParseNoColons() { assertNull(ItemParser.parse("NOCOLONS")); + assertEquals(defaultItem, ItemParser.parse("NOCOLONS", defaultItem)); } /* @@ -253,6 +263,8 @@ public void testParseTwoItem() { @Test public void testParseBadTwoItem() { assertNull(ItemParser.parse("STNE:5")); + assertEquals(defaultItem, ItemParser.parse("STNE:3", defaultItem)); + assertEquals(defaultItem, ItemParser.parse("STNE:Z", defaultItem)); } @Test @@ -265,5 +277,8 @@ public void testParseThreeItem() { @Test public void testParseBadThreeItem() { assertNull(ItemParser.parse("STNE:5:5")); + assertEquals(defaultItem, ItemParser.parse("STNE:5:5", defaultItem)); + assertEquals(defaultItem, ItemParser.parse("STNE:AA:5", defaultItem)); + assertEquals(defaultItem, ItemParser.parse("WOODEN_SWORD:4:AA", defaultItem)); } } diff --git a/src/test/java/world/bentobox/bentobox/util/UtilTest.java b/src/test/java/world/bentobox/bentobox/util/UtilTest.java index 639a58e08..684a2ff63 100644 --- a/src/test/java/world/bentobox/bentobox/util/UtilTest.java +++ b/src/test/java/world/bentobox/bentobox/util/UtilTest.java @@ -41,6 +41,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import net.md_5.bungee.api.ChatColor; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; @@ -125,7 +126,7 @@ public void setUp() throws Exception { when(phm.replacePlaceholders(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); when(plugin.getLocalesManager()).thenReturn(lm); - + } @After @@ -458,7 +459,7 @@ public void testRunCommandsConsoleCommandFail() { Bukkit.dispatchCommand(sender, "replace tastybento"); verify(plugin).logError("Could not execute test command as console: replace tastybento"); } - + /** * Test for {@link Util#broadcast(String, String...)} */ @@ -468,7 +469,7 @@ public void testBroadcastStringStringNoPlayers() { int result = Util.broadcast("test.key", TextVariables.DESCRIPTION, "hello"); assertEquals(0, result); } - + /** * Test for {@link Util#broadcast(String, String...)} */ @@ -476,6 +477,38 @@ public void testBroadcastStringStringNoPlayers() { public void testBroadcastStringStringHasPerm() { int result = Util.broadcast("test.key", TextVariables.DESCRIPTION, "hello"); assertEquals(11, result); - + + } + + /** + * Test for {@link Util#translateColorCodes(String)} + */ + @Test + public void testTranslateColorCodesAmpersand() { + assertEquals("", Util.translateColorCodes("")); + assertEquals("abcdef ABCDEF", Util.translateColorCodes("abcdef ABCDEF")); + assertEquals("white space after ", Util.translateColorCodes("white space after ")); + assertEquals("§ared color", Util.translateColorCodes("&a red color")); + assertEquals("§a big space", Util.translateColorCodes("&a big space")); + assertEquals("§ared color", Util.translateColorCodes("&ared color")); + assertEquals("§ared §bcolor §cgreen §fheheh", Util.translateColorCodes("&ared &bcolor &c green &f heheh")); + } + + /** + * Test for {@link Util#translateColorCodes(String)} + */ + @Test + public void testTranslateColorCodesHex() { + // Use Bungee Chat parsing for single color test to validate correct parsing + assertEquals(ChatColor.of("#ff0000").toString(), Util.translateColorCodes("&#ff0000")); + assertEquals(ChatColor.of("#ff2200").toString(), Util.translateColorCodes("&#f20")); + + assertEquals("&#f single char", Util.translateColorCodes("&#f single char")); + assertEquals("&#f0 two chars", Util.translateColorCodes("&#f0 two chars")); + assertEquals("§x§f§f§0§0§0§0shorten hex", Util.translateColorCodes("&#f00 shorten hex")); + assertEquals("§x§f§f§0§0§0§01 four chars", Util.translateColorCodes("&#f001 four chars")); + assertEquals("§x§f§f§0§0§0§01f five chars", Util.translateColorCodes("&#f001f five chars")); + assertEquals("§x§f§f§0§0§0§0full hex", Util.translateColorCodes("&#ff0000 full hex")); + assertEquals("&#ggg outside hex range", Util.translateColorCodes("&#ggg outside hex range")); } }