diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1683344a2..4191081bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,22 +10,22 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'adopt' java-version: '17' - name: Cache SonarCloud packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -38,7 +38,7 @@ jobs: - run: mvn --batch-mode clean org.jacoco:jacoco-maven-plugin:prepare-agent install - run: mkdir staging && cp target/*.jar staging - name: Save artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: Package path: staging diff --git a/pom.xml b/pom.xml index f857231fa..d7c1a3747 100644 --- a/pom.xml +++ b/pom.xml @@ -73,10 +73,10 @@ 42.2.18 5.0.1 - 1.19.4-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT - 1.19.3-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT 3.0.0 1.7.1 2.10.9 @@ -88,7 +88,7 @@ -LOCAL - 1.23.2 + 1.24.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 956ff030e..56d02eb37 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -5,6 +5,9 @@ import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -36,7 +39,6 @@ import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; import world.bentobox.bentobox.managers.HooksManager; -import world.bentobox.bentobox.managers.IslandChunkDeletionManager; import world.bentobox.bentobox.managers.IslandDeletionManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; @@ -52,7 +54,7 @@ * Main BentoBox class * @author tastybento, Poslovitch */ -public class BentoBox extends JavaPlugin { +public class BentoBox extends JavaPlugin implements Listener { private static BentoBox instance; @@ -71,7 +73,6 @@ public class BentoBox extends JavaPlugin { private HooksManager hooksManager; private PlaceholdersManager placeholdersManager; private IslandDeletionManager islandDeletionManager; - private IslandChunkDeletionManager islandChunkDeletionManager; private WebManager webManager; // Settings @@ -227,7 +228,7 @@ private void completeSetup(long loadTime) { // Make sure all worlds are already registered to Multiverse. hooksManager.registerHook(new MultiverseCoreHook()); hooksManager.registerHook(new MyWorldsHook()); - islandWorldManager.registerWorldsToMultiverse(); + islandWorldManager.registerWorldsToMultiverse(true); // TODO: re-enable after implementation //hooksManager.registerHook(new DynmapHook()); @@ -300,8 +301,9 @@ private void registerListeners() { manager.registerEvents(new BannedCommands(this), this); // Death counter manager.registerEvents(new DeathListener(this), this); + // MV unregister + manager.registerEvents(this, this); // Island Delete Manager - islandChunkDeletionManager = new IslandChunkDeletionManager(this); islandDeletionManager = new IslandDeletionManager(this); manager.registerEvents(islandDeletionManager, this); } @@ -321,6 +323,15 @@ public void onDisable() { if (islandsManager != null) { islandsManager.shutdown(); } + + } + + @EventHandler + public void onServerStop(ServerCommandEvent e) { + if (islandWorldManager != null && (e.getCommand().equalsIgnoreCase("stop") || e.getCommand().equalsIgnoreCase("restart"))) { + // Unregister any MV worlds if () { + islandWorldManager.registerWorldsToMultiverse(false); + } } /** @@ -531,13 +542,6 @@ public IslandDeletionManager getIslandDeletionManager() { return islandDeletionManager; } - /** - * @return the islandChunkDeletionManager - */ - public IslandChunkDeletionManager getIslandChunkDeletionManager() { - return islandChunkDeletionManager; - } - /** * @return an optional of the Bstats instance * @since 1.1 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 8301b13b3..93700da36 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -48,6 +48,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi */ private boolean onlyPlayer = false; + /** + * True if the command is only for the console + * @since 1.24.0 + */ + private boolean onlyConsole = false; + + /** * True if command is a configurable rank */ @@ -241,7 +248,6 @@ public boolean execute(@NonNull CommandSender sender, @NonNull String label, Str CompositeCommand cmd = getCommandFromArgs(args); String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label; List cmdArgs = Arrays.asList(args).subList(cmd.subCommandLevel, args.length); - // Call return cmd.call(user, cmdLabel, cmdArgs); } @@ -257,17 +263,21 @@ public boolean execute(@NonNull CommandSender sender, @NonNull String label, Str */ public boolean call(User user, String cmdLabel, List cmdArgs) { // Check for console and permissions - if (onlyPlayer && !user.isPlayer()) { + if (isOnlyPlayer() && !user.isPlayer()) { user.sendMessage("general.errors.use-in-game"); return false; } + if (isOnlyConsole() && user.isPlayer()) { + user.sendMessage("general.errors.use-in-console"); + return false; + } + if (!this.runPermissionCheck(user)) { // Error message is displayed by permission check. return false; } - // Set the user's addon context user.setAddon(addon); // Execute and trim args @@ -513,6 +523,14 @@ public boolean isOnlyPlayer() { return onlyPlayer; } + /** + * Check if this command is only for consoles. + * @return true or false + */ + public boolean isOnlyConsole() { + return onlyConsole; + } + /** * Sets whether this command should only be run by players. * If this is set to {@code true}, this command will only be runnable by objects implementing {@link Player}. @@ -525,6 +543,18 @@ public void setOnlyPlayer(boolean onlyPlayer) { this.onlyPlayer = onlyPlayer; } + /** + * Sets whether this command should only be run in the console. + * This is for commands that dump a lot of data or are for debugging. + * The default value provided when instantiating this CompositeCommand is {@code false}. + * Therefore, this method should only be used in case you want to explicitly edit the value. + * @param onlyConsole {@code true} if this command should only be run in the console. + * @since 1.24.0 + */ + public void setOnlyConsole(boolean onlyConsole) { + this.onlyConsole = onlyConsole; + } + /** * Sets locale reference to this command's description. * It is used to display the help of this command. @@ -623,16 +653,17 @@ public void inheritPermission() { @Override @NonNull public List tabComplete(final @NonNull CommandSender sender, final @NonNull String alias, final String[] args) { - List options = new ArrayList<>(); // Get command object based on args entered so far CompositeCommand command = getCommandFromArgs(args); // Check for console and permissions - if (command.isOnlyPlayer() && !(sender instanceof Player)) { - return options; + if ((command.isOnlyPlayer() && !(sender instanceof Player)) + || (command.isOnlyConsole() && sender instanceof Player)) { + return List.of(); } if (command.getPermission() != null && !command.getPermission().isEmpty() && !sender.hasPermission(command.getPermission()) && !sender.isOp()) { - return options; + return List.of(); } + List options = new ArrayList<>(); // Add any tab completion from the subcommand options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(ArrayList::new)); if (command.hasSubCommands()) { @@ -654,17 +685,26 @@ public List tabComplete(final @NonNull CommandSender sender, final @NonN } /** - * Returns a list containing all the labels of the subcommands for the provided CompositeCommand excluding any hidden commands + * Returns a list containing all the labels of the subcommands for the provided + * CompositeCommand excluding any hidden commands * @param sender the CommandSender * @param command the CompositeCommand to get the subcommands from * @return a list of subcommands labels or an empty list. */ @NonNull private List getSubCommandLabels(@NonNull CommandSender sender, @NonNull CompositeCommand command) { - return command.getSubCommands().values().stream() - .filter(cmd -> !cmd.isHidden()) - .filter(cmd -> !cmd.isOnlyPlayer() || sender.isOp() || (sender instanceof Player && cmd.getPermission() != null && (cmd.getPermission().isEmpty() || sender.hasPermission(cmd.getPermission()))) ) - .map(CompositeCommand::getLabel).toList(); + List result = new ArrayList<>(); + for (CompositeCommand cc: command.getSubCommands().values()) { + // Player or not + if (sender instanceof Player) { + if (!cc.isHidden() && !cc.isOnlyConsole() && (cc.getPermission().isEmpty() || sender.hasPermission(cc.getPermission()))) { + result.add(cc.getLabel()); + } + } else if (!cc.isOnlyPlayer()) { + result.add(cc.getLabel()); + } + } + return result; } /** 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 f335769cc..ca3281996 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 @@ -35,7 +35,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()) { - getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user), () -> + getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user, getAddon()), () -> user.sendMessage("commands.admin.info.no-island")); return true; } @@ -48,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) { - new IslandInfo(island).showAdminInfo(user); + new IslandInfo(island).showAdminInfo(user, getAddon()); 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/blueprints/AdminBlueprintCopyCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java index 8b54ac28f..403ec8891 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java @@ -19,7 +19,7 @@ public AdminBlueprintCopyCommand(AdminBlueprintCommand parent) @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.copy"); setParametersHelp("commands.admin.blueprint.copy.parameters"); setDescription("commands.admin.blueprint.copy.description"); } @@ -37,7 +37,7 @@ public boolean execute(User user, String label, List args) AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent(); BlueprintClipboard clipboard = - parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard()); + parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard()); boolean copyAir = args.stream().anyMatch(key -> key.equalsIgnoreCase("air")); boolean copyBiome = args.stream().anyMatch(key -> key.equalsIgnoreCase("biome")); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java index 65bbb830f..082ffef35 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java @@ -26,7 +26,7 @@ public AdminBlueprintDeleteCommand(AdminBlueprintCommand parent) @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.delete"); this.setParametersHelp("commands.admin.blueprint.delete.parameters"); this.setDescription("commands.admin.blueprint.delete.description"); } @@ -47,10 +47,10 @@ public boolean execute(User user, String label, List args) if (this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).containsKey(blueprintName)) { this.askConfirmation(user, user.getTranslation("commands.admin.blueprint.delete.confirmation"), - () -> { - this.getPlugin().getBlueprintsManager().deleteBlueprint(this.getAddon(), blueprintName); - user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName); - }); + () -> { + this.getPlugin().getBlueprintsManager().deleteBlueprint(this.getAddon(), blueprintName); + user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName); + }); return true; } else diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java index 57f19325a..27ff01429 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java @@ -22,7 +22,7 @@ public AdminBlueprintListCommand(AdminBlueprintCommand parent) @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.list"); this.setDescription("commands.admin.blueprint.list.description"); } @@ -54,8 +54,8 @@ public boolean execute(User user, String label, List args) FilenameFilter blueprintFilter = (File dir, String name) -> name.endsWith(BlueprintsManager.BLUEPRINT_SUFFIX); List blueprintList = Arrays.stream(Objects.requireNonNull(blueprints.list(blueprintFilter))). - map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())). - toList(); + map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())). + toList(); if (blueprintList.isEmpty()) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java index 6bbef1502..1d0d766fa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java @@ -17,7 +17,7 @@ public AdminBlueprintLoadCommand(AdminBlueprintCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.load"); setParametersHelp("commands.admin.blueprint.load.parameters"); setDescription("commands.admin.blueprint.load.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java index df254b3bb..066e6b9fa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java @@ -19,7 +19,7 @@ public AdminBlueprintOriginCommand(AdminBlueprintCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.origin"); setParametersHelp("commands.admin.blueprint.origin.parameters"); setDescription("commands.admin.blueprint.origin.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java index 046222cf4..eb50009c1 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java @@ -15,7 +15,7 @@ public AdminBlueprintPasteCommand(AdminBlueprintCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.paste"); setParametersHelp("commands.admin.blueprint.paste.parameters"); setDescription("commands.admin.blueprint.paste.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java index 534124ce6..18d004f68 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java @@ -15,7 +15,7 @@ public AdminBlueprintPos1Command(AdminBlueprintCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.pos1"); setParametersHelp("commands.admin.blueprint.pos1.parameters"); setDescription("commands.admin.blueprint.pos1.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java index 9c1bcae44..76ea8e0fb 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java @@ -15,7 +15,7 @@ public AdminBlueprintPos2Command(AdminBlueprintCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.pos2"); setParametersHelp("commands.admin.blueprint.pos2.parameters"); setDescription("commands.admin.blueprint.pos2.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java index 568e061e3..e677dfa0b 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java @@ -27,7 +27,7 @@ public AdminBlueprintRenameCommand(AdminBlueprintCommand parent) @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.rename"); this.setParametersHelp("commands.admin.blueprint.rename.parameters"); this.setDescription("commands.admin.blueprint.rename.description"); } @@ -83,8 +83,8 @@ public boolean execute(User user, String label, List args) { // Ask for confirmation to overwrite this.askConfirmation(user, - user.getTranslation("commands.admin.blueprint.file-exists"), - () -> this.rename(user, from, to, args.get(1))); + user.getTranslation("commands.admin.blueprint.file-exists"), + () -> this.rename(user, from, to, args.get(1))); } else { @@ -102,11 +102,11 @@ private void rename(User user, String blueprintName, String fileName, String dis this.getPlugin().getBlueprintsManager().renameBlueprint(this.getAddon(), blueprint, fileName, displayName); user.sendMessage("commands.admin.blueprint.rename.success", - "[old]", - blueprintName, - TextVariables.NAME, - blueprint.getName(), - "[display]", - blueprint.getDisplayName()); + "[old]", + blueprintName, + TextVariables.NAME, + blueprint.getName(), + "[display]", + blueprint.getDisplayName()); } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java index f4c5a749d..9f1baeb90 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java @@ -25,7 +25,7 @@ public AdminBlueprintSaveCommand(AdminBlueprintCommand parent) @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.save"); this.setParametersHelp("commands.admin.blueprint.save.parameters"); this.setDescription("commands.admin.blueprint.save.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java index 4ef5a2241..32fbe2e06 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java @@ -22,7 +22,7 @@ public AdminDeathsAddCommand(AdminDeathsCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.add"); setDescription("commands.admin.deaths.add.description"); setParametersHelp("commands.admin.deaths.add.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java index 74824702e..bdad9bff9 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java @@ -22,7 +22,7 @@ public AdminDeathsRemoveCommand(AdminDeathsCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.remove"); setDescription("commands.admin.deaths.remove.description"); setParametersHelp("commands.admin.deaths.remove.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java index 4780ce3cb..d7b8a01bc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java @@ -21,7 +21,7 @@ public AdminDeathsResetCommand(AdminDeathsCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.reset"); setDescription("commands.admin.deaths.reset.description"); setParametersHelp("commands.admin.deaths.reset.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java index ce99330c1..8d69486c6 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java @@ -21,7 +21,7 @@ public AdminDeathsSetCommand(AdminDeathsCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.set"); setDescription("commands.admin.deaths.set.description"); setParametersHelp("commands.admin.deaths.set.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java index 272d3ddd0..4e07099c2 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java @@ -16,7 +16,7 @@ public AdminPurgeProtectCommand(CompositeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.protect"); setOnlyPlayer(true); setDescription("commands.admin.purge.protect.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java index bb5a04a0c..3f5bde9aa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java @@ -19,7 +19,7 @@ public AdminPurgeStatusCommand(AdminPurgeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.status"); setOnlyPlayer(false); setParametersHelp("commands.admin.purge.status.parameters"); setDescription("commands.admin.purge.status.description"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java index da1a4830f..84aeb6190 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java @@ -13,7 +13,7 @@ public AdminPurgeStopCommand(CompositeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.stop"); setOnlyPlayer(false); setDescription("commands.admin.purge.stop.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java index 7d9696d15..b137f1e43 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java @@ -17,7 +17,7 @@ public AdminPurgeUnownedCommand(AdminPurgeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.unowned"); setOnlyPlayer(false); setParametersHelp("commands.admin.purge.unowned.parameters"); setDescription("commands.admin.purge.unowned.description"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java index 70a1b876a..566e4a30c 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java @@ -25,7 +25,7 @@ public AdminRangeAddCommand(AdminRangeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.range.add"); setDescription("commands.admin.range.add.description"); setParametersHelp("commands.admin.range.add.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java index 5615ec5e7..4679bc2bc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java @@ -25,7 +25,7 @@ public AdminRangeRemoveCommand(AdminRangeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.range.remove"); setDescription("commands.admin.range.remove.description"); setParametersHelp("commands.admin.range.remove.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java index ff46727f9..cc681ddea 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java @@ -22,7 +22,7 @@ public AdminResetsAddCommand(AdminResetsCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.add"); setDescription("commands.admin.resets.add.description"); setParametersHelp("commands.admin.resets.add.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java index b4feaed2c..3b459b032 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java @@ -22,7 +22,7 @@ public AdminResetsRemoveCommand(AdminResetsCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.remove"); setDescription("commands.admin.resets.remove.description"); setParametersHelp("commands.admin.resets.remove.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java index 00925a187..490f654b0 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java @@ -22,7 +22,7 @@ public AdminResetsResetCommand(CompositeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.remove"); setDescription("commands.admin.resets.reset.description"); setParametersHelp("commands.admin.resets.reset.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java index 34d5c9997..025dbcad5 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java @@ -16,7 +16,7 @@ public AdminResetsSetCommand(CompositeCommand parent) { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.set"); setDescription("commands.admin.resets.set.description"); setParametersHelp("commands.admin.resets.set.parameters"); } 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 b5feecab1..78816d6cc 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 @@ -21,7 +21,7 @@ public AdminTeamAddCommand(CompositeCommand parent) { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.add"); setParametersHelp("commands.admin.team.add.parameters"); setDescription("commands.admin.team.add.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java index 954022967..a682d0abc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java @@ -20,7 +20,7 @@ public AdminTeamDisbandCommand(CompositeCommand parent) { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.disband"); setParametersHelp("commands.admin.team.disband.parameters"); setDescription("commands.admin.team.disband.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java index 601a4cb8f..b739b2a24 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java @@ -14,7 +14,7 @@ public AdminTeamFixCommand(CompositeCommand parent) { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.fix"); setDescription("commands.admin.team.fix.description"); } 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 77c5174a3..efce9f268 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 @@ -30,7 +30,7 @@ public AdminTeamKickCommand(CompositeCommand parent) { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.kick"); setParametersHelp("commands.admin.team.kick.parameters"); setDescription("commands.admin.team.kick.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java index 2001367cc..fa7800283 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java @@ -25,7 +25,7 @@ public AdminTeamSetownerCommand(CompositeCommand parent) { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.setowner"); setParametersHelp("commands.admin.team.setowner.parameters"); setDescription("commands.admin.team.setowner.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java index 8b10540fb..a60aeeb41 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java @@ -24,6 +24,10 @@ public class IslandDeletehomeCommand extends ConfirmableCommand { private @Nullable Island island; + /** + * Deletes a home + * @param islandCommand parent command + */ public IslandDeletehomeCommand(CompositeCommand islandCommand) { super(islandCommand, "deletehome"); } @@ -51,11 +55,11 @@ public boolean canExecute(User user, String label, List args) { return false; } - // check command permission + // check command ranks int rank = Objects.requireNonNull(island).getRank(user); if (rank < island.getRankCommand(getUsage())) { user.sendMessage("general.errors.insufficient-rank", - TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank))); + TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank))); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java index c74349d20..f8b66f38e 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java @@ -6,6 +6,7 @@ import org.bukkit.ChatColor; import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -93,8 +94,18 @@ public boolean execute(User user, String label, List args) { } // Everything's good! - Objects.requireNonNull(getIslands().getIsland(getWorld(), user)).setName(name); + Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), user)); + String previousName = island.getName(); + island.setName(name); user.sendMessage("commands.island.setname.success", TextVariables.NAME, name); + // Fire the IslandNameEvent + new IslandEvent.IslandEventBuilder() + .island(island) + .involvedPlayer(user.getUniqueId()) + .reason(IslandEvent.Reason.NAME) + .previousName(previousName) + .admin(false) + .build(); return true; } } 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 701d553d3..15c0109db 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 @@ -7,7 +7,9 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; import world.bentobox.bentobox.database.objects.Island; @@ -169,7 +171,17 @@ public enum Reason { * Event that will fire any time a player's rank changes on an island. * @since 1.13.0 */ - RANK_CHANGE + RANK_CHANGE, + /** + * Event that will fire when an island is named or renamed + * @since 1.24.0 + */ + NAME, + /** + * Event that will fire when the info command is executed. Allows addons to add to it + * @since 1.24.0 + */ + INFO } public static IslandEventBuilder builder() { @@ -213,6 +225,14 @@ public static class IslandEventBuilder { * @since 1.13.0 */ private int newRank; + /** + * @since 1.24.0 Previous name of island + */ + private String previousName; + /** + * @since 1.24.0 GameMode addon causing this event + */ + private Addon addon; public IslandEventBuilder island(Island island) { this.island = island; @@ -305,6 +325,26 @@ public IslandEventBuilder rankChange(int oldRank, int newRank){ return this; } + /** + * Sets the previous name of the island + * @param previousName previous name. May be null. + * @since 1.24.0 + */ + public IslandEventBuilder previousName(@Nullable String previousName) { + this.previousName = previousName; + return this; + } + + /** + * Addon that triggered this event, e.g. BSkyBlock + * @param addon Addon. + * @since 1.24.0 + */ + public IslandEventBuilder addon(Addon addon) { + this.addon = addon; + return this; + } + private IslandBaseEvent getEvent() { return switch (reason) { case EXPEL -> new IslandExpelEvent(island, player, admin, location); @@ -329,6 +369,8 @@ private IslandBaseEvent getEvent() { case RESERVED -> new IslandReservedEvent(island, player, admin, location); case RANK_CHANGE -> new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); case NEW_ISLAND -> new IslandNewIslandEvent(island, player, admin, location); + case NAME -> new IslandNameEvent(island, player, admin, location, previousName); + case INFO -> new IslandInfoEvent(island, player, admin, location, addon); default -> new IslandGeneralEvent(island, player, admin, location); }; } @@ -345,5 +387,6 @@ public IslandBaseEvent build() { Bukkit.getPluginManager().callEvent(newEvent); return newEvent; } + } } diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java new file mode 100644 index 000000000..d2a38e611 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java @@ -0,0 +1,55 @@ +package world.bentobox.bentobox.api.events.island; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.event.HandlerList; +import org.eclipse.jdt.annotation.NonNull; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.events.IslandBaseEvent; +import world.bentobox.bentobox.database.objects.Island; + +/** + * Fired when an a player reuqets info about an island + * Cancellation has no effect. + * @since 1.24.0 + */ +public class IslandInfoEvent extends IslandBaseEvent { + + private final Addon addon; + private static final HandlerList handlers = new HandlerList(); + + @Override + public @NonNull HandlerList getHandlers() { + return getHandlerList(); + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * @param island island + * @param player player asking for the info + * @param admin true if this is an admin request + * @param location location of the player asking for the info + * @param addon the addon parent that is calling this info command, e.g., BSkyBlock + */ + public IslandInfoEvent(Island island, UUID player, boolean admin, Location location, Addon addon) { + // Final variables have to be declared in the constructor + super(island, player, admin, location); + this.addon = addon; + } + + /** + * @return the gameMode that is for this island + */ + public Addon getAddon() { + return addon; + } + + + + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java new file mode 100644 index 000000000..81b02e1f2 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java @@ -0,0 +1,44 @@ +package world.bentobox.bentobox.api.events.island; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.event.HandlerList; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.api.events.IslandBaseEvent; +import world.bentobox.bentobox.database.objects.Island; + +/** + * Fired when an a player names or renames an island. + * Cancellation has no effect. + */ +public class IslandNameEvent extends IslandBaseEvent { + + private final String previousName; + private static final HandlerList handlers = new HandlerList(); + + @Override + public @NonNull HandlerList getHandlers() { + return getHandlerList(); + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public IslandNameEvent(Island island, UUID player, boolean admin, Location location, @Nullable String previousName) { + // Final variables have to be declared in the constructor + super(island, player, admin, location); + this.previousName = previousName; + } + + /** + * @return the previous name of the island, if any. May be null if no name previously used. + */ + @Nullable + public String getPreviousNameIsland() { + return previousName; + } +} \ No newline at end of file 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 b1fbd273b..369f700f0 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java @@ -125,6 +125,7 @@ public boolean checkIsland(@NonNull Event e, @Nullable Player player, @Nullable * @return true if the check is okay, false if it was disallowed */ public boolean checkIsland(@NonNull Event e, @Nullable Player player, @Nullable Location loc, @NonNull Flag flag, boolean silent) { + // Set user user = player == null ? null : User.getInstance(player); if (loc == null) { 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 e347f200a..6354bae6a 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java @@ -19,6 +19,9 @@ */ public class PanelItem { + /** + * @return an empty PanelItem + */ public static PanelItem empty() { return new PanelItemBuilder().build(); } @@ -58,6 +61,9 @@ public PanelItem(PanelItemBuilder builtItem) { } + /** + * @return the icon itemstack + */ public ItemStack getItem() { return icon; } @@ -135,14 +141,14 @@ public void setGlow(boolean glow) { public boolean isPlayerHead() { return playerHeadName != null && !playerHeadName.isEmpty(); } - + /** * @return the playerHeadName * @since 1.9.0 */ public String getPlayerHeadName() { return playerHeadName; - } + } /** * Click handler interface 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 7af3d6b8a..70bd4781b 100644 --- a/src/main/java/world/bentobox/bentobox/api/user/User.java +++ b/src/main/java/world/bentobox/bentobox/api/user/User.java @@ -472,7 +472,10 @@ private String replacePrefixes(String translation, String[] variables) { // Then replace variables if (variables.length > 1) { for (int i = 0; i < variables.length; i += 2) { - translation = translation.replace(variables[i], variables[i + 1]); + // Prevent a NPE if the substituting variable is null + if (variables[i + 1] != null) { + translation = translation.replace(variables[i], variables[i + 1]); + } } } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index c0dff17bc..dd035adc3 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -18,6 +18,7 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; import org.bukkit.entity.ChestedHorse; @@ -221,8 +222,10 @@ private BlueprintBlock bluePrintBlock(Vector pos, Block block, boolean copyBiome // Signs if (blockState instanceof Sign sign) { - b.setSignLines(Arrays.asList(sign.getLines())); - b.setGlowingText(sign.isGlowingText()); + for (Side side : Side.values()) { + b.setSignLines(side, Arrays.asList(sign.getSide(side).getLines())); + b.setGlowingText(side, sign.getSide(side).isGlowingText()); + } } // Set block data if (blockState.getData() instanceof Attachable) { 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 7b2584087..f7b9e5a4e 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java @@ -6,6 +6,7 @@ import org.bukkit.block.Biome; import org.bukkit.block.banner.Pattern; +import org.bukkit.block.sign.Side; import org.bukkit.inventory.ItemStack; import com.google.gson.annotations.Expose; @@ -21,6 +22,8 @@ public class BlueprintBlock { @Expose private List signLines; @Expose + private List signLines2; + @Expose private Map inventory; @Expose private BlueprintCreatureSpawner creatureSpawner; @@ -36,6 +39,8 @@ public class BlueprintBlock { private List bannerPatterns; @Expose private boolean glowingText; + @Expose + private boolean glowingText2; public BlueprintBlock(String blockData) { this.blockData = blockData; @@ -57,14 +62,20 @@ public void setBlockData(String blockData) { /** * @return the signLines + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public List getSignLines() { return signLines; } /** * @param signLines the signLines to set + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public void setSignLines(List signLines) { this.signLines = signLines; } @@ -129,17 +140,73 @@ public void setBiome(Biome biome) { /** * @return the glowingText + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public boolean isGlowingText() { return glowingText; } /** * @param glowingText the glowingText to set + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public void setGlowingText(boolean glowingText) { this.glowingText = glowingText; } + /** + * @param side side of sign + * @param glowingText the glowingText to set + * @since 1.24.0 + */ + public void setGlowingText(Side side, boolean glowingText) { + switch (side) { + case FRONT -> this.glowingText = glowingText; + default -> this.glowingText2 = glowingText; + }; + + } + + /** + * @param side side of sign + * @return the glowingText + * @since 1.24.0 + */ + public boolean isGlowingText(Side side) { + return switch (side) { + case FRONT -> glowingText; + default -> glowingText2; + }; + } + + /** + * @param side side of sign + * @return the signLines + * @since 1.24.0 + */ + public List getSignLines(Side side) { + return switch (side) { + case FRONT -> signLines; + default -> signLines2; + }; + } + + /** + * @param side side of sign + * @param signLines the signLines to set + * @since 1.24.0 + */ + public void setSignLines(Side side, List signLines) { + switch (side) { + case FRONT -> this.signLines = signLines; + default -> this.signLines2 = signLines; + }; + } + + } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java index 4f769afbc..1f511565e 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java @@ -28,7 +28,7 @@ public void setup() { @Override public boolean execute(User user, String label, List args) { user.sendRawMessage("About " + BentoBox.getInstance().getDescription().getName() + " v" + BentoBox.getInstance().getDescription().getVersion() + ":"); - user.sendRawMessage("Copyright (c) 2017 - 2021 Tastybento, Poslovitch and the BentoBoxWorld contributors"); + user.sendRawMessage("Copyright (c) 2017 - 2023 Tastybento, Poslovitch and the BentoBoxWorld contributors"); user.sendRawMessage("See https://www.eclipse.org/legal/epl-2.0/ for license information."); return true; } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java index 8fa9f3480..9662a88ee 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java @@ -24,6 +24,7 @@ public void setup() { new BentoBoxReloadCommand(this); new BentoBoxLocaleCommand(this); new BentoBoxHelpCommand(this); + new BentoBoxPermsCommand(this); // Database names with a 2 in them are migration databases if (getPlugin().getSettings().getDatabaseType().name().contains("2")) { new BentoBoxMigrateCommand(this); diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java new file mode 100644 index 000000000..fc20c211f --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java @@ -0,0 +1,82 @@ +package world.bentobox.bentobox.commands; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.permissions.Permission; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +/** + * Displays permissions that have been set by BentoBox. + * + * @author tastybento + */ +public class BentoBoxPermsCommand extends CompositeCommand { + + /** + * Info command + * @param parent - command parent + */ + public BentoBoxPermsCommand(CompositeCommand parent) { + super(parent, "perms"); + } + + @Override + public void setup() { + setPermission("bentobox.admin.perms"); + setParametersHelp("commands.bentobox.perms.parameters"); + setDescription("commands.bentobox.perms.description"); + this.setOnlyConsole(true); + } + + @Override + public boolean execute(User user, String label, List args) { + // Loop all the known top-level commands + getPlugin().getCommandsManager().getCommands().values().stream().distinct().forEach(cc -> { + if (cc.getAddon() == null) { + user.sendMessage("*** BentoBox effective perms:"); + } else if (cc.getAddon() instanceof GameModeAddon gma) { + user.sendRawMessage("**** " + gma.getDescription().getName() + " effective perms:"); + } else { + user.sendRawMessage("**** " + cc.getAddon().getDescription().getName() + " effective perms:"); + } + user.sendRawMessage("permissions:"); + printData(user, cc, cc.getLabel()); + printSubCommandData(user, cc, cc.getLabel()); + }); + return true; + } + + private void printData(User user, CompositeCommand cc, String label) { + if (cc.getPermission().isBlank()) return; + String desc = user.getTranslation(cc.getWorld(), cc.getDescription()); + user.sendRawMessage(" " + cc.getPermission() + ":"); + user.sendRawMessage(" description: Allow use of '/" + label + "' command - " + desc); + Permission p = Bukkit.getPluginManager().getPermission(cc.getPermission()); + if (p != null) { + user.sendRawMessage(" default: " + p.getDefault().name()); + } else { + user.sendRawMessage(" default: OP"); // If not def + } + + } + + /** + * Iterates over sub-commands + * @param user user + * @param parent parent command + * @param label + */ + private void printSubCommandData(User user, CompositeCommand parent, String label) { + for (CompositeCommand cc : parent.getSubCommands().values()) { + if (cc.getLabel().equalsIgnoreCase("help")) continue; // Ignore the help command + String newLabel = label + " " + cc.getLabel(); + printData(user, cc, newLabel); + printSubCommandData(user, cc, newLabel); + } + + } +} diff --git a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java index 62ba2abe9..f58bfd87f 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java @@ -49,6 +49,12 @@ public void registerWorld(World world, boolean islandWorld) { } } + @Override + public void unregisterWorld(World world) { + String cmd = "mv remove " + world.getName(); + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd); + } + @Override public boolean hook() { return true; // The hook process shouldn't fail diff --git a/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java b/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java index 4f79a96c3..daa9fcaa2 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java @@ -13,8 +13,17 @@ public interface WorldManagementHook { /** * Register the world with the World Management hook * + * * @param world - world to register * @param islandWorld - if true, then this is an island world */ void registerWorld(World world, boolean islandWorld); + + /** + * Unregisters a world. + * @param world - world to unregister + */ + default void unregisterWorld(World world) { + // Do nothing + } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java b/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java index c644eb1a5..cb3678ee3 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java +++ b/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java @@ -57,20 +57,19 @@ public void onVisitorCommand(PlayerCommandPreprocessEvent e) { } private boolean checkCmd(String cmd, String[] args) { - // Commands are guilty until proven innocent :-) - boolean banned = true; // Get the elements of the banned command by splitting it String[] bannedSplit = cmd.toLowerCase(java.util.Locale.ENGLISH).split(" "); // If the banned command has the same number of elements or less than the entered command then it may be banned - if (bannedSplit.length <= args.length) { + if (bannedSplit.length <= args.length) { for (int i = 0; i < bannedSplit.length; i++) { - if (!bannedSplit[i].equals(args[i])) { - banned = false; - break; + if (!bannedSplit[i].equalsIgnoreCase(args[i])) { + return false; } } + } else { + return false; } - return banned; + return true; } /** 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 2dbd511a3..8f082e66c 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,6 +1,5 @@ package world.bentobox.bentobox.listeners.flags.protection; -import java.util.Map; import java.util.Optional; import org.bukkit.FluidCollisionMode; @@ -8,6 +7,8 @@ import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; +import org.bukkit.block.BrushableBlock; +import org.bukkit.block.Sign; import org.bukkit.block.data.Waterlogged; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -18,8 +19,6 @@ import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.player.PlayerInteractEvent; -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; @@ -31,31 +30,12 @@ public class BlockInteractionListener extends FlagListener { - /** - * These cover materials in another server version. This avoids run time errors due to unknown enum values, at the - * expense of a string comparison - */ - private static final Map stringFlags; - private static final String CHEST = "CHEST"; - - static - { - stringFlags = Map.of( - "ACACIA_CHEST_BOAT", CHEST, - "BIRCH_CHEST_BOAT", CHEST, - "JUNGLE_CHEST_BOAT", CHEST, - "DARK_OAK_CHEST_BOAT", CHEST, - "MANGROVE_CHEST_BOAT", CHEST, - "OAK_CHEST_BOAT", CHEST, - "SPRUCE_CHEST_BOAT", CHEST); - } - /** * Handle interaction with blocks * * @param e - event */ - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerInteract(final PlayerInteractEvent e) { // We only care about the RIGHT_CLICK_BLOCK action. @@ -65,13 +45,13 @@ public void onPlayerInteract(final PlayerInteractEvent e) } // Check clicked block - this.checkClickedBlock(e, e.getPlayer(), e.getClickedBlock().getLocation(), e.getClickedBlock().getType()); + this.checkClickedBlock(e, e.getPlayer(), e.getClickedBlock()); // Now check for in-hand items if (e.getItem() != null && !e.getItem().getType().equals(Material.AIR)) { // Boats - if (e.getItem().getType().name().endsWith("BOAT")) + if (Tag.ITEMS_BOATS.isTagged(e.getItem().getType())) { this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BOAT); } @@ -107,11 +87,12 @@ else if (e.getItem().getType() == Material.GLASS_BOTTLE) * * @param e - event called * @param player - player - * @param loc - location of clicked block - * @param type - material type of clicked block + * @param block - block being clicked or used */ - private void checkClickedBlock(Event e, Player player, Location loc, Material type) + private void checkClickedBlock(Event e, Player player, Block block) { + Material type = block.getType(); + Location loc = block.getLocation(); // Handle pots if (type.name().startsWith("POTTED")) { @@ -119,6 +100,12 @@ private void checkClickedBlock(Event e, Player player, Location loc, Material ty return; } + if (block.getState() instanceof BrushableBlock bb && BlockInteractionListener.holds(player, Material.BRUSH)) { + // Protect this using break blocks flag for now. Maybe in the future it can have its own flag. + this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS); + return; + } + if (Tag.ANVIL.isTagged(type)) { this.checkIsland(e, player, loc, Flags.ANVIL); @@ -155,14 +142,21 @@ private void checkClickedBlock(Event e, Player player, Location loc, Material ty return; } + if (Tag.SIGNS.isTagged(type) && block.getState() instanceof Sign sign && !sign.isWaxed()) { + // If waxed, then sign cannot be edited otherwise check + this.checkIsland(e, player, loc, Flags.SIGN_EDITING); + return; + } + if (Tag.FENCE_GATES.isTagged(type)) { this.checkIsland(e, player, loc, Flags.GATE); } - // TODO: 1.18 compatibility - // if (Tag.ITEMS_CHEST_BOATS.isTagged(type)) { - // this.checkIsland(e, player, loc, Flags.CHEST); - // } + + if (Tag.ITEMS_CHEST_BOATS.isTagged(type)) + { + this.checkIsland(e, player, loc, Flags.CHEST); + } switch (type) { @@ -177,14 +171,12 @@ private void checkClickedBlock(Event e, Player player, Location loc, Material ty case DISPENSER -> this.checkIsland(e, player, loc, Flags.DISPENSER); case DROPPER -> this.checkIsland(e, player, loc, Flags.DROPPER); case HOPPER, HOPPER_MINECART -> this.checkIsland(e, player, loc, Flags.HOPPER); - case BLAST_FURNACE, CAMPFIRE, FURNACE_MINECART, FURNACE, SMOKER -> - this.checkIsland(e, player, loc, Flags.FURNACE); + case BLAST_FURNACE, CAMPFIRE, FURNACE_MINECART, FURNACE, SMOKER -> this.checkIsland(e, player, loc, Flags.FURNACE); case ENCHANTING_TABLE -> this.checkIsland(e, player, loc, Flags.ENCHANTING); case ENDER_CHEST -> this.checkIsland(e, player, loc, Flags.ENDER_CHEST); case JUKEBOX -> this.checkIsland(e, player, loc, Flags.JUKEBOX); case NOTE_BLOCK -> this.checkIsland(e, player, loc, Flags.NOTE_BLOCK); - case CRAFTING_TABLE, CARTOGRAPHY_TABLE, GRINDSTONE, STONECUTTER, LOOM -> - this.checkIsland(e, player, loc, Flags.CRAFTING); + case CRAFTING_TABLE, CARTOGRAPHY_TABLE, GRINDSTONE, STONECUTTER, LOOM -> this.checkIsland(e, player, loc, Flags.CRAFTING); case LEVER -> this.checkIsland(e, player, loc, Flags.LEVER); case REDSTONE_WIRE, REPEATER, COMPARATOR, DAYLIGHT_DETECTOR -> this.checkIsland(e, player, loc, Flags.REDSTONE); case DRAGON_EGG -> this.checkIsland(e, player, loc, Flags.DRAGON_EGG); @@ -192,6 +184,7 @@ private void checkClickedBlock(Event e, Player player, Location loc, Material ty case GLOW_ITEM_FRAME, ITEM_FRAME -> this.checkIsland(e, player, loc, Flags.ITEM_FRAME); case SWEET_BERRY_BUSH, CAVE_VINES -> this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS); case CAKE -> this.checkIsland(e, player, loc, Flags.CAKE); + case CHISELED_BOOKSHELF -> this.checkIsland(e, player, loc, Flags.BOOKSHELF); case LAVA_CAULDRON -> { if (BlockInteractionListener.holds(player, Material.BUCKET)) @@ -232,12 +225,7 @@ else if (BlockInteractionListener.holds(player, Material.POTION)) } } default -> - { - if (stringFlags.containsKey(type.name())) - { - Optional f = BentoBox.getInstance().getFlagsManager().getFlag(stringFlags.get(type.name())); - f.ifPresent(flag -> this.checkIsland(e, player, loc, flag)); - } + { // nothing to do } } } @@ -253,7 +241,7 @@ else if (BlockInteractionListener.holds(player, Material.POTION)) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockBreak(final BlockBreakEvent e) { - this.checkClickedBlock(e, e.getPlayer(), e.getBlock().getLocation(), e.getBlock().getType()); + this.checkClickedBlock(e, e.getPlayer(), e.getBlock()); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java index 2d27c69e8..406e30202 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java @@ -39,7 +39,7 @@ public void onPlayerInteractAtEntity(final PlayerInteractAtEntityEvent e) { } } - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerInteractEntity(PlayerInteractEntityEvent e) { Player p = e.getPlayer(); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java index 924e38fb7..b68487729 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java @@ -3,6 +3,7 @@ import java.util.Set; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -44,7 +45,7 @@ public void onBlockPlace(final BlockPlaceEvent e) // Crops if (against.equals(Material.FARMLAND) && SEEDS.contains(e.getItemInHand().getType())) { this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.CROP_PLANTING); - } else { + } else { this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS); } } @@ -124,7 +125,7 @@ public void onPlayerInteract(final PlayerInteractEvent e) { this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.PLACE_BLOCKS); } - else if (e.getMaterial().name().contains("BOAT")) + else if (Tag.ITEMS_BOATS.isTagged(e.getMaterial())) { this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.BOAT); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java index f40a07ea4..8bef10902 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java @@ -13,6 +13,8 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockReceiveGameEvent; +import com.google.common.base.Enums; + import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.lists.Flags; @@ -34,11 +36,12 @@ public void onSculkSensor(BlockReceiveGameEvent event) return; } - if (event.getBlock().getType() == Material.SCULK_SENSOR && - event.getEntity() != null && - event.getEntity() instanceof Player player) + if ((event.getBlock().getType() == Material.SCULK_SENSOR + || event.getBlock().getType() == Enums.getIfPresent(Material.class, "CALIBRATED_SCULK_SENSOR").or(Material.SCULK_SENSOR)) + && event.getEntity() != null && event.getEntity() instanceof Player player) { this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SENSOR, true); } + return; } } diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 4f74ca46e..9d47bfc10 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -9,6 +9,7 @@ import com.google.common.base.Enums; import world.bentobox.bentobox.api.flags.Flag; +import world.bentobox.bentobox.api.flags.Flag.Mode; import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick; import world.bentobox.bentobox.listeners.flags.clicklisteners.CommandRankClickListener; @@ -158,11 +159,18 @@ private Flags() {} * @since 1.10.0 * @see LecternListener */ - public static final Flag LECTERN = new Flag.Builder("LECTERN", Material.LECTERN).listener(new LecternListener()).build(); + public static final Flag LECTERN = new Flag.Builder("LECTERN", Material.LECTERN).mode(Mode.ADVANCED).listener(new LecternListener()).build(); + + /** + * Prevents players from placing a book in a bookshelf or taking the book from it. + * @since 1.24.0 + * @see BlockInteractionListener + */ + public static final Flag BOOKSHELF = new Flag.Builder("BOOKSHELF", Material.CHISELED_BOOKSHELF).mode(Mode.ADVANCED).build(); // Entity interactions - public static final Flag ARMOR_STAND = new Flag.Builder("ARMOR_STAND", Material.ARMOR_STAND).listener(new EntityInteractListener()).mode(Flag.Mode.ADVANCED).build(); - public static final Flag RIDING = new Flag.Builder("RIDING", Material.GOLDEN_HORSE_ARMOR).build(); + public static final Flag ARMOR_STAND = new Flag.Builder("ARMOR_STAND", Material.ARMOR_STAND).listener(new EntityInteractListener()).mode(Mode.ADVANCED).build(); + public static final Flag RIDING = new Flag.Builder("RIDING", Material.GOLDEN_HORSE_ARMOR).mode(Mode.ADVANCED).build(); /** * Prevents players from issuing any kind of interactions with Minecarts (entering, placing and opening if chest). * @since 1.3.0 @@ -188,7 +196,7 @@ private Flags() {} // Buckets. All bucket use is covered by one listener public static final Flag BUCKET = new Flag.Builder("BUCKET", Material.BUCKET).listener(new BucketListener()).mode(Flag.Mode.BASIC).build(); - public static final Flag COLLECT_LAVA = new Flag.Builder("COLLECT_LAVA", Material.LAVA_BUCKET).build(); + public static final Flag COLLECT_LAVA = new Flag.Builder("COLLECT_LAVA", Material.LAVA_BUCKET).mode(Mode.ADVANCED).build(); public static final Flag COLLECT_WATER = new Flag.Builder("COLLECT_WATER", Material.WATER_BUCKET).mode(Flag.Mode.ADVANCED).build(); /** * @since 1.21 @@ -215,7 +223,7 @@ private Flags() {} * Prevents players from throwing eggs. * @see EggListener */ - public static final Flag EGGS = new Flag.Builder("EGGS", Material.EGG).listener(new EggListener()).build(); + public static final Flag EGGS = new Flag.Builder("EGGS", Material.EGG).mode(Mode.ADVANCED).listener(new EggListener()).build(); /** * Prevents players from throwing potions / experience bottles. * @since 1.1 @@ -259,7 +267,7 @@ private Flags() {} * Prevents players from extinguishing fires. * @see FireListener */ - public static final Flag FIRE_EXTINGUISH = new Flag.Builder("FIRE_EXTINGUISH", Material.POTION).build(); + public static final Flag FIRE_EXTINGUISH = new Flag.Builder("FIRE_EXTINGUISH", Material.POTION).mode(Mode.ADVANCED).build(); // Inventories public static final Flag MOUNT_INVENTORY = new Flag.Builder("MOUNT_INVENTORY", Material.IRON_HORSE_ARMOR).listener(new InventoryListener()).mode(Flag.Mode.ADVANCED).build(); @@ -277,12 +285,12 @@ private Flags() {} * Prevents players from going through the Nether Portal. * @see PortalListener */ - public static final Flag NETHER_PORTAL = new Flag.Builder("NETHER_PORTAL", Material.NETHERRACK).listener(new PortalListener()).build(); + public static final Flag NETHER_PORTAL = new Flag.Builder("NETHER_PORTAL", Material.NETHERRACK).mode(Mode.ADVANCED).listener(new PortalListener()).build(); /** * Prevents players from going through the End Portal. * @see PortalListener */ - public static final Flag END_PORTAL = new Flag.Builder("END_PORTAL", Material.END_PORTAL_FRAME).build(); + public static final Flag END_PORTAL = new Flag.Builder("END_PORTAL", Material.END_PORTAL_FRAME).mode(Mode.ADVANCED).build(); // Shearing public static final Flag SHEARING = new Flag.Builder("SHEARING", Material.SHEARS).listener(new ShearingListener()).mode(Flag.Mode.ADVANCED).build(); @@ -653,16 +661,21 @@ private Flags() {} * Controls who gets to harvest any crop related contents. e.g. Wheat, Sugar Cane, melon blocks, not stems, pumpkin blocks, etc. * @since 1.23.0 */ - public static final Flag HARVEST = new Flag.Builder("HARVEST", Material.PUMPKIN).type(Type.PROTECTION).build(); + public static final Flag HARVEST = new Flag.Builder("HARVEST", Material.PUMPKIN).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build(); /** * Crop Planting * Controls who gets to plant crops on tilled soil. * @since 1.23.0 */ - public static final Flag CROP_PLANTING = new Flag.Builder("CROP_PLANTING", Material.PUMPKIN_SEEDS).type(Type.PROTECTION).build(); + public static final Flag CROP_PLANTING = new Flag.Builder("CROP_PLANTING", Material.PUMPKIN_SEEDS).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build(); + + /** + * Sign edit protection + * @since 1.24.0 + */ + public static final Flag SIGN_EDITING = new Flag.Builder("SIGN_EDITING", Material.DARK_OAK_SIGN).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build(); - /** * Provides a list of all the Flag instances contained in this class using reflection. * Deprecated Flags are ignored. diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java index 8c06a6ee2..e3195e230 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java @@ -31,11 +31,13 @@ public class IslandDeletionManager implements Listener { */ private final Database handler; private final Set inDeletion; + private final IslandChunkDeletionManager islandChunkDeletionManager; public IslandDeletionManager(BentoBox plugin) { this.plugin = plugin; handler = new Database<>(plugin, IslandDeletion.class); inDeletion = new HashSet<>(); + islandChunkDeletionManager = new IslandChunkDeletionManager(plugin); } /** @@ -56,7 +58,7 @@ public void onBentoBoxReady(BentoBoxReadyEvent e) { } else { plugin.log("Resuming deletion of island at " + di.getLocation().getWorld().getName() + " " + Util.xyz(di.getLocation().toVector())); inDeletion.add(di.getLocation()); - plugin.getIslandChunkDeletionManager().add(di); + this.islandChunkDeletionManager.add(di); } }); } @@ -86,4 +88,12 @@ public void onIslandDeleted(IslandDeletedEvent e) { public boolean inDeletion(Location location) { return inDeletion.contains(location); } + + /** + * @return the islandChunkDeletionManager + */ + public IslandChunkDeletionManager getIslandChunkDeletionManager() { + return islandChunkDeletionManager; + } + } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java index cbb8a31d6..fbe8ce645 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java @@ -50,36 +50,48 @@ public IslandWorldManager(BentoBox plugin) { gameModes = new HashMap<>(); } - public void registerWorldsToMultiverse() { + /** + * Registers or unregisters worlds with world management plugins + * + * @param reg true to register, false to remove registration + * + * Updated 1.24.0 + */ + public void registerWorldsToMultiverse(boolean reg) { gameModes.values().stream().distinct().forEach(gm -> { - registerToWorldManagementPlugins(gm.getOverWorld(), true); + registerToWorldManagementPlugins(gm.getOverWorld(), true, reg); if (gm.getWorldSettings().isNetherGenerate()) { - registerToWorldManagementPlugins(gm.getNetherWorld(), gm.getWorldSettings().isNetherIslands()); + registerToWorldManagementPlugins(gm.getNetherWorld(), gm.getWorldSettings().isNetherIslands(), reg); } if (gm.getWorldSettings().isEndGenerate()) { - registerToWorldManagementPlugins(gm.getEndWorld(), gm.getWorldSettings().isEndIslands()); + registerToWorldManagementPlugins(gm.getEndWorld(), gm.getWorldSettings().isEndIslands(), reg); } }); } - /** - * Registers a world with world management plugins - * - * @param world the World to register - * @param islandWorld true if this is an island world - */ - private void registerToWorldManagementPlugins(@NonNull World world, boolean islandWorld) { - if (plugin.getHooks() != null) { - for (Hook hook : plugin.getHooks().getHooks()) { - if (hook instanceof final WorldManagementHook worldManagementHook) { - if (Bukkit.isPrimaryThread()) { - worldManagementHook.registerWorld(world, islandWorld); - } else { - Bukkit.getScheduler().runTask(plugin, () -> worldManagementHook.registerWorld(world, islandWorld)); - } + + private void registerToWorldManagementPlugins(@NonNull World world, boolean islandWorld, boolean reg) { + if (plugin.getHooks() == null) { + return; + } + for (Hook hook : plugin.getHooks().getHooks()) { + if (hook instanceof final WorldManagementHook worldManagementHook) { + if (Bukkit.isPrimaryThread()) { + runTask(worldManagementHook, world, islandWorld, reg); + } else { + Bukkit.getScheduler().runTask(plugin, () -> runTask(worldManagementHook, world, islandWorld, reg)); } } } + + } + + private void runTask(WorldManagementHook worldManagementHook, @NonNull World world, boolean islandWorld, boolean reg) { + if (reg) { + worldManagementHook.registerWorld(world, islandWorld); + } else { + worldManagementHook.unregisterWorld(world); + } } /** @@ -143,6 +155,16 @@ public boolean isKnownFriendlyWorldName(String name) { .anyMatch(gm -> gm.getWorldSettings().getFriendlyName().equalsIgnoreCase(name)); } + /** + * Associate a world with a game mode. This enables game modes to register more worlds than just the standard + * overworld, nether, and end worlds. + * @param world world + * @param gameMode game mode + * @since 1.24.0 + */ + public void addWorld(World world, GameModeAddon gameMode) { + gameModes.put(world, gameMode); + } /** * Adds a GameMode to island world manager @@ -159,27 +181,27 @@ public void addGameMode(@NonNull GameModeAddon gameMode) { // Add worlds to map gameModes.put(world, gameMode); // Call Multiverse - registerToWorldManagementPlugins(world, true); - if (settings.isNetherGenerate()) { + registerToWorldManagementPlugins(world, true, true); + if (settings.isNetherGenerate() && gameMode.getNetherWorld() != null) { gameModes.put(gameMode.getNetherWorld(), gameMode); if (settings.isNetherIslands()) { - registerToWorldManagementPlugins(gameMode.getNetherWorld(), true); + registerToWorldManagementPlugins(gameMode.getNetherWorld(), true, true); } } - if (settings.isEndGenerate()) { + if (settings.isEndGenerate() && gameMode.getEndWorld() != null) { gameModes.put(gameMode.getEndWorld(), gameMode); if (settings.isEndIslands()) { - registerToWorldManagementPlugins(gameMode.getEndWorld(), true); + registerToWorldManagementPlugins(gameMode.getEndWorld(), true, true); } } // Set default island settings plugin.getFlagsManager().getFlags().stream(). - filter(f -> f.getType().equals(Flag.Type.PROTECTION)). - forEach(f -> settings.getDefaultIslandFlagNames().putIfAbsent(f.getID(), f.getDefaultRank())); + filter(f -> f.getType().equals(Flag.Type.PROTECTION)). + forEach(f -> settings.getDefaultIslandFlagNames().putIfAbsent(f.getID(), f.getDefaultRank())); plugin.getFlagsManager().getFlags().stream(). - filter(f -> f.getType().equals(Flag.Type.SETTING)). - forEach(f -> settings.getDefaultIslandSettingNames().putIfAbsent(f.getID(), f.getDefaultRank())); + filter(f -> f.getType().equals(Flag.Type.SETTING)). + forEach(f -> settings.getDefaultIslandSettingNames().putIfAbsent(f.getID(), f.getDefaultRank())); Bukkit.getScheduler().runTask(plugin, () -> { // Set world difficulty @@ -484,8 +506,8 @@ public int getMaxHomes(@NonNull World world) { */ public String getFriendlyName(@NonNull World world) { return gameModes.containsKey(world) ? - gameModes.get(world).getWorldSettings().getFriendlyName() : - world.getName(); + gameModes.get(world).getWorldSettings().getFriendlyName() : + world.getName(); } /** @@ -710,8 +732,8 @@ public Optional getAddon(@Nullable World world) { public Map getDefaultIslandFlags(@NonNull World world) { return this.gameModes.containsKey(world) ? - this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandFlagNames()) : - Collections.emptyMap(); + this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandFlagNames()) : + Collections.emptyMap(); } /** @@ -732,8 +754,8 @@ public List getHiddenFlags(@NonNull World world) { public Map getDefaultIslandSettings(@NonNull World world) { return this.gameModes.containsKey(world) ? - this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandSettingNames()) : - Collections.emptyMap(); + this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandSettingNames()) : + Collections.emptyMap(); } public boolean isUseOwnGenerator(@NonNull World world) { @@ -945,7 +967,7 @@ private Map convertToFlags(Map flagNamesMap) { Map flagMap = new HashMap<>(); flagNamesMap.forEach((key, value) -> - this.plugin.getFlagsManager().getFlag(key).ifPresent(flag -> flagMap.put(flag, value))); + this.plugin.getFlagsManager().getFlag(key).ifPresent(flag -> flagMap.put(flag, value))); return flagMap; } } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index ce585a6cb..60fe88449 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -22,11 +22,11 @@ import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Boat; -import org.bukkit.entity.Boat.Type; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -38,8 +38,6 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import com.google.common.collect.ImmutableMap; - import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; @@ -67,15 +65,6 @@ public class IslandsManager { private final BentoBox plugin; - // Tree species to boat material map - private static final Map TREE_TO_BOAT = ImmutableMap.builder(). - put(Type.ACACIA, Material.ACACIA_BOAT). - put(Type.BIRCH, Material.BIRCH_BOAT). - put(Type.DARK_OAK, Material.DARK_OAK_BOAT). - put(Type.JUNGLE, Material.JUNGLE_BOAT). - put(Type.OAK, Material.OAK_BOAT). - put(Type.SPRUCE, Material.SPRUCE_BOAT).build(); - /** * One island can be spawn, this is the one - otherwise, this value is null */ @@ -250,8 +239,8 @@ public CompletableFuture isSafeLocationAsync(@NonNull Location l) { public boolean checkIfSafe(@Nullable World world, @NonNull Material ground, @NonNull Material space1, @NonNull Material space2) { // Ground must be solid, space 1 and 2 must not be solid if (world == null || !ground.isSolid() - || (space1.isSolid() && !space1.name().contains("SIGN")) - || (space2.isSolid() && !space2.name().contains("SIGN"))) { + || (space1.isSolid() && !Tag.SIGNS.isTagged(space1)) + || (space2.isSolid() && !Tag.SIGNS.isTagged(space2))) { return false; } // Cannot be submerged or water cannot be dangerous @@ -262,14 +251,19 @@ public boolean checkIfSafe(@Nullable World world, @NonNull Material ground, @Non if (ground.equals(Material.LAVA) || space1.equals(Material.LAVA) || space2.equals(Material.LAVA) - || ground.name().contains("FENCE") - || ground.name().contains("DOOR") - || ground.name().contains("GATE") - || ground.name().contains("PLATE") - || ground.name().contains("SIGN") - || ground.name().contains("BANNER") - || ground.name().contains("BUTTON") - || ground.name().contains("BOAT") + || Tag.SIGNS.isTagged(ground) + || Tag.TRAPDOORS.isTagged(ground) + || Tag.BANNERS.isTagged(ground) + || Tag.PRESSURE_PLATES.isTagged(ground) + || Tag.FENCE_GATES.isTagged(ground) + || Tag.DOORS.isTagged(ground) + || Tag.FENCES.isTagged(ground) + || Tag.BUTTONS.isTagged(ground) + || Tag.ITEMS_BOATS.isTagged(ground) + || Tag.ITEMS_CHEST_BOATS.isTagged(ground) + || Tag.CAMPFIRES.isTagged(ground) + || Tag.FIRE.isTagged(ground) + || Tag.FIRE.isTagged(space1) || space1.equals(Material.END_PORTAL) || space2.equals(Material.END_PORTAL) || space1.equals(Material.END_GATEWAY) @@ -349,7 +343,7 @@ public void deleteIsland(@NonNull Island island, boolean removeBlocks, @Nullable // Remove players from island removePlayersFromIsland(island); // Remove blocks from world - plugin.getIslandChunkDeletionManager().add(new IslandDeletion(island)); + plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(new IslandDeletion(island)); } } @@ -1072,19 +1066,7 @@ private CompletableFuture homeTeleportAsync(@NonNull World world, @NonN 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 - if (player.isInsideVehicle()) { - Entity boat = player.getVehicle(); - if (boat instanceof Boat boaty) { - player.leaveVehicle(); - // Remove the boat so they don't lie around everywhere - boat.remove(); - player.getInventory().addItem(new ItemStack(TREE_TO_BOAT.getOrDefault(boaty.getBoatType(), Material.OAK_BOAT))); - player.updateInventory(); - } - } + readyPlayer(player); this.getAsyncSafeHomeLocation(world, user, name).thenAccept(home -> { Island island = getIsland(world, user); if (home == null) { @@ -1196,23 +1178,7 @@ public void spawnTeleport(@NonNull World world, @NonNull Player player) { user.sendMessage("commands.island.spawn.no-spawn"); } else { // Teleport the player to the spawn - // Stop any gliding - player.setGliding(false); - // Check if the player is a passenger in a boat - if (player.isInsideVehicle()) { - Entity boat = player.getVehicle(); - if (boat instanceof Boat boaty) { - player.leaveVehicle(); - // Remove the boat so they don't lie around everywhere - boat.remove(); - Material boatMat = Material.getMaterial(boaty.getType() + "_BOAT"); - if (boatMat == null) { - boatMat = Material.OAK_BOAT; - } - player.getInventory().addItem(new ItemStack(boatMat, 1)); - player.updateInventory(); - } - } + readyPlayer(player); user.sendMessage("commands.island.spawn.teleporting"); // Safe teleport @@ -1220,6 +1186,23 @@ public void spawnTeleport(@NonNull World world, @NonNull Player player) { } } + private void readyPlayer(@NonNull Player player) { + // Stop any gliding + player.setGliding(false); + // Check if the player is a passenger in a boat + if (player.isInsideVehicle()) { + Entity boat = player.getVehicle(); + if (boat instanceof Boat boaty) { + player.leaveVehicle(); + // Remove the boat so they don't lie around everywhere + boat.remove(); + player.getInventory().addItem(new ItemStack(boaty.getBoatType().getMaterial())); + player.updateInventory(); + } + } + + } + /** * Indicates whether a player is at an island spawn or not * diff --git a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java index 141aa0852..e8c60c53d 100644 --- a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java +++ b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java @@ -17,6 +17,8 @@ import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; import org.bukkit.entity.ChestedHorse; @@ -45,6 +47,7 @@ * */ public abstract class CopyWorldRegenerator implements WorldRegenerator { + private final BentoBox plugin; protected CopyWorldRegenerator() { @@ -68,7 +71,7 @@ protected CopyWorldRegenerator() { public CompletableFuture regenerate(GameModeAddon gm, IslandDeletion di, World world) { return gm.isUsesNewChunkGeneration() ? regenerateCopy(gm, di, world) : regenerateSimple(gm, di, world); } - + public CompletableFuture regenerateCopy(GameModeAddon gm, IslandDeletion di, World world) { CompletableFuture bigFuture = new CompletableFuture<>(); new BukkitRunnable() { @@ -110,7 +113,7 @@ private boolean isEnded(int chunkX) { @Override public CompletableFuture regenerateChunk(Chunk chunk) { - return regenerateChunk(null, chunk.getWorld(), chunk.getX(), chunk.getZ()); + return regenerateChunk(null, chunk.getWorld(), chunk.getX(), chunk.getZ()); } private CompletableFuture regenerateChunk(@Nullable IslandDeletion di, World world, int chunkX, int chunkZ) { @@ -132,7 +135,7 @@ private CompletableFuture regenerateChunk(@Nullable IslandDeletion di, Wor copyChunkDataToChunk(chunkTo, chunkFrom, di != null ? di.getBox() : null); } catch (InterruptedException | ExecutionException e) { - Thread.currentThread().interrupt(); + Thread.currentThread().interrupt(); } }); return CompletableFuture.allOf(cleanFuture, copyFuture); @@ -159,7 +162,7 @@ private CompletableFuture cleanChunk(CompletableFuture chunkFuture, ); // Similarly, when the chunk is loaded, remove all the entities in the chunk apart from players - CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> + CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above Arrays.stream(chunk.getEntities()) .filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())) @@ -200,35 +203,35 @@ private void copyChunkDataToChunk(Chunk toChunk, Chunk fromChunk, BoundingBox li } private void processEntity(Entity entity, Location location) { - Entity bpe = location.getWorld().spawnEntity(location, entity.getType()); - bpe.setCustomName(entity.getCustomName()); - if (entity instanceof Villager villager && bpe instanceof Villager villager2) { - setVillager(villager, villager2); - } - if (entity instanceof Colorable c && bpe instanceof Colorable cc) { - if (c.getColor() != null) { - cc.setColor(c.getColor()); - } - } - if (entity instanceof Tameable t && bpe instanceof Tameable tt) { - tt.setTamed(t.isTamed()); - } - if (entity instanceof ChestedHorse ch && bpe instanceof ChestedHorse ch2) { - ch2.setCarryingChest(ch.isCarryingChest()); - } - // Only set if child. Most animals are adults - if (entity instanceof Ageable a && bpe instanceof Ageable aa) { - if (a.isAdult()) aa.setAdult(); - } - if (entity instanceof AbstractHorse horse && bpe instanceof AbstractHorse horse2) { - horse2.setDomestication(horse.getDomestication()); - horse2.getInventory().setContents(horse.getInventory().getContents()); + Entity bpe = location.getWorld().spawnEntity(location, entity.getType()); + bpe.setCustomName(entity.getCustomName()); + if (entity instanceof Villager villager && bpe instanceof Villager villager2) { + setVillager(villager, villager2); + } + if (entity instanceof Colorable c && bpe instanceof Colorable cc) { + if (c.getColor() != null) { + cc.setColor(c.getColor()); } + } + if (entity instanceof Tameable t && bpe instanceof Tameable tt) { + tt.setTamed(t.isTamed()); + } + if (entity instanceof ChestedHorse ch && bpe instanceof ChestedHorse ch2) { + ch2.setCarryingChest(ch.isCarryingChest()); + } + // Only set if child. Most animals are adults + if (entity instanceof Ageable a && bpe instanceof Ageable aa) { + if (a.isAdult()) aa.setAdult(); + } + if (entity instanceof AbstractHorse horse && bpe instanceof AbstractHorse horse2) { + horse2.setDomestication(horse.getDomestication()); + horse2.getInventory().setContents(horse.getInventory().getContents()); + } - if (entity instanceof Horse horse && bpe instanceof Horse horse2) { - horse2.setStyle(horse.getStyle()); - } + if (entity instanceof Horse horse && bpe instanceof Horse horse2) { + horse2.setStyle(horse.getStyle()); } + } /** * Set the villager stats @@ -241,7 +244,7 @@ private void setVillager(Villager v, Villager villager2) { villager2.setProfession(v.getProfession()); villager2.setVillagerType(v.getVillagerType()); } - + private void processTileEntity(Block fromBlock, Block toBlock) { // Block state BlockState blockState = fromBlock.getState(); @@ -249,11 +252,9 @@ private void processTileEntity(Block fromBlock, Block toBlock) { // Signs if (blockState instanceof Sign fromSign && b instanceof Sign toSign) { - int i = 0; - for (String line : fromSign.getLines()) { - toSign.setLine(i++, line); + for (Side side : Side.values()) { + writeSign(fromSign, toSign, side); } - toSign.setGlowingText(fromSign.isGlowingText()); } // Chests else if (blockState instanceof InventoryHolder ih && b instanceof InventoryHolder toChest) { @@ -270,7 +271,17 @@ else if (blockState instanceof Banner banner && b instanceof Banner toBanner) { toBanner.setPatterns(banner.getPatterns()); } } - + + + private void writeSign(Sign fromSign, Sign toSign, Side side) { + SignSide fromSide = fromSign.getSide(side); + SignSide toSide = toSign.getSide(side); + int i = 0; + for (String line : fromSide.getLines()) { + toSide.setLine(i++, line); + } + toSide.setGlowingText(fromSide.isGlowingText()); + } public CompletableFuture regenerateSimple(GameModeAddon gm, IslandDeletion di, World world) { CompletableFuture bigFuture = new CompletableFuture<>(); diff --git a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/PasteHandlerImpl.java b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/PasteHandlerImpl.java similarity index 95% rename from src/main/java/world/bentobox/bentobox/nms/v1_19_R3/PasteHandlerImpl.java rename to src/main/java/world/bentobox/bentobox/nms/v1_20_R1/PasteHandlerImpl.java index 14af30231..44c23adb9 100644 --- a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/PasteHandlerImpl.java +++ b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/PasteHandlerImpl.java @@ -1,4 +1,4 @@ -package world.bentobox.bentobox.nms.v1_19_R3; +package world.bentobox.bentobox.nms.v1_20_R1; import java.util.List; import java.util.Map; @@ -11,8 +11,8 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import net.minecraft.core.BlockPosition; import net.minecraft.world.level.block.state.IBlockData; diff --git a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/WorldRegeneratorImpl.java b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/WorldRegeneratorImpl.java similarity index 87% rename from src/main/java/world/bentobox/bentobox/nms/v1_19_R3/WorldRegeneratorImpl.java rename to src/main/java/world/bentobox/bentobox/nms/v1_20_R1/WorldRegeneratorImpl.java index 2d1aecc76..c1bd78391 100644 --- a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/WorldRegeneratorImpl.java +++ b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/WorldRegeneratorImpl.java @@ -1,10 +1,10 @@ -package world.bentobox.bentobox.nms.v1_19_R3; +package world.bentobox.bentobox.nms.v1_20_R1; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import net.minecraft.core.BlockPosition; import net.minecraft.world.level.World; diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 7cd43cef5..8231ffd8b 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -19,6 +19,8 @@ import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.WallSign; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -120,7 +122,9 @@ public static void setBlockState(Island island, Block block, BlueprintBlock bpBl BlockState bs = block.getState(); // Signs if (bs instanceof Sign) { - writeSign(island, block, bpBlock.getSignLines(), bpBlock.isGlowingText()); + for (Side side : Side.values()) { + writeSign(island, block, bpBlock, side); + } } // Chests, in general else if (bs instanceof InventoryHolder holder) { @@ -200,8 +204,12 @@ public static CompletableFuture setEntity(Island island, Location location * @param block - block * @param lines - lines * @param glow - is sign glowing? + * @param side - the side being writted */ - public static void writeSign(Island island, final Block block, final List lines, boolean glow) { + public static void writeSign(Island island, final Block block, BlueprintBlock bpSign, Side side) { + List lines = bpSign.getSignLines(side); + boolean glow = bpSign.isGlowingText(side); + BlockFace bf; if (block.getType().name().contains("WALL_SIGN")) { WallSign wallSign = (WallSign) block.getBlockData(); @@ -211,7 +219,7 @@ public static void writeSign(Island island, final Block block, final List user = Optional.ofNullable(island.getOwner()).map(User::getInstance); if (user.isPresent()) { for (int i = 0; i < 4; i++) { - s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(), + signSide.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(), addonName + ".sign.line" + i, "").replace(TextVariables.NAME, name))); } } } else { // Just paste for (int i = 0; i < 4; i++) { - s.setLine(i, lines.get(i)); + signSide.setLine(i, lines.get(i)); } } - s.setGlowingText(glow); + signSide.setGlowingText(glow); // Update the sign s.update(); } diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java index e772b6ba5..42428beab 100644 --- a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -10,6 +10,8 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -43,8 +45,9 @@ public IslandInfo(Island island) { /** * Shows admin info of this island * @param user user asking + * @param addon Addon executing this command */ - public void showAdminInfo(User user) { + public void showAdminInfo(User user, Addon addon) { user.sendMessage("commands.admin.info.title"); user.sendMessage("commands.admin.info.island-uuid", TextVariables.UUID, island.getUniqueId()); if (owner == null) { @@ -98,6 +101,15 @@ public void showAdminInfo(User user) { if (island.getPurgeProtected()) { user.sendMessage("commands.admin.info.purge-protected"); } + // Fire info event to allow other addons to add to info + IslandEvent.builder() + .island(island) + .location(island.getCenter()) + .reason(IslandEvent.Reason.INFO) + .involvedPlayer(user.getUniqueId()) + .addon(addon) + .admin(true) + .build(); } @@ -130,6 +142,13 @@ public boolean showInfo(User user) { user.sendMessage("commands.admin.info.banned-players"); island.getBanned().forEach(u -> user.sendMessage("commands.admin.info.banned-format", TextVariables.NAME, plugin.getPlayers().getName(u))); } + // Fire info event + IslandEvent.builder() + .island(island) + .location(island.getCenter()) + .reason(IslandEvent.Reason.INFO) + .involvedPlayer(user.getUniqueId()) + .build(); return true; } diff --git a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java index 50c58f533..feb5a270e 100644 --- a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java +++ b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java @@ -218,6 +218,14 @@ public enum ServerVersion { * @since 1.22.1 */ V1_19_4(Compatibility.COMPATIBLE), + /** + * @since 1.24.0 + */ + V1_20(Compatibility.INCOMPATIBLE), + /** + * @since 1.24.0 + */ + V1_20_1(Compatibility.COMPATIBLE), ; private final Compatibility compatibility; diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 7fead2e3b..a0f906643 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -21,6 +21,7 @@ general: no-permission: "&c You don't have the permission to execute this command (&7 [permission]&c )." insufficient-rank: "&c Your rank is not high enough to do that! (&7 [rank]&c )" use-in-game: "&c This command is only available in-game." + use-in-console: "&c This command is only available in the console." no-team: "&c You do not have a team!" no-island: "&c You do not have an island!" player-has-island: "&c Player already has an island!" @@ -428,6 +429,8 @@ commands: success: "&a Successfully reset [name]'s island name." bentobox: description: "BentoBox admin command" + perms: + description: "displays the effective perms for BentoBox and Addons in a YAML format" about: description: "displays copyright and license information" reload: @@ -773,6 +776,12 @@ protection: Toggle placing, breaking and entering into boats. hint: "No boat interaction allowed" + BOOKSHELF: + name: "Bookshelves" + description: |- + &a Allow to place books + &a or to take books. + hint: "cannot place a book or take a book." BREAK_BLOCKS: description: "Toggle breaking" name: "Break blocks" @@ -1325,6 +1334,12 @@ protection: &a activation. name: "Sculk Shrieker" hint: "sculk shrieker activation is disabled" + SIGN_EDITING: + description: |- + &a Allows text editing + &a of signs + name: "Sign Editing" + hint: "sign editing is disabled" TNT_DAMAGE: description: |- &a Allow TNT and TNT minecarts diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1d84e6f85..4dff30944 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -60,4 +60,6 @@ permissions: bentobox.version: description: Allows to use /bentobox version default: op - + bentobox.perms: + description: Allow use of '/bentobox perms' command + default: OP diff --git a/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java index d00950143..3809a61f9 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java @@ -13,6 +13,7 @@ import java.util.List; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -85,6 +86,21 @@ public void testTabCompleteCommandSenderStringStringArrayHidden() { CommandSender sender = mock(CommandSender.class); String[] args = {"h"}; List opList = tlc.tabComplete(sender, "top", args); + assertEquals(2, opList.size()); + assertEquals("help", opList.get(0)); // Console can see all commands + assertEquals("hidden", opList.get(1)); // Console can see all commands + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.CompositeCommand#tabComplete(Player, java.lang.String, java.lang.String[])}. + */ + @Test + public void testTabCompletePlayerStringStringArrayHidden() { + TopLevelCommand tlc = new TopLevelCommand(); + Player sender = mock(Player.class); + String[] args = {"h"}; + List opList = tlc.tabComplete(sender, "top", args); + opList.forEach(System.out::println); assertEquals(1, opList.size()); assertEquals("help", opList.get(0)); // Only help } @@ -95,7 +111,7 @@ public void testTabCompleteCommandSenderStringStringArrayHidden() { @Test public void testTabCompleteCommandSenderStringStringArrayInvisible() { TopLevelCommand tlc = new TopLevelCommand(); - CommandSender sender = mock(CommandSender.class); + Player sender = mock(Player.class); String[] args = {"i"}; List opList = tlc.tabComplete(sender, "top", args); assertTrue(opList.isEmpty()); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java index 5d0bb3a76..bb4b800fe 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -44,6 +45,7 @@ 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.util.Util; @@ -74,6 +76,8 @@ public class AdminTeleportCommandTest { private World netherWorld; @Mock private World endWorld; + @Mock + private PlaceholdersManager phm; /** @@ -96,16 +100,25 @@ public void setUp() throws Exception { while(notUUID.equals(uuid)) { notUUID = UUID.randomUUID(); } + when(p.getUniqueId()).thenReturn(uuid); + when(p.hasPermission("admin.tp")).thenReturn(true); + when(p.hasPermission("admin")).thenReturn(false); + when(user.getUniqueId()).thenReturn(uuid); when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); when(user.isPlayer()).thenReturn(true); + when(user.hasPermission("admin.tp")).thenReturn(true); + when(user.hasPermission("admin")).thenReturn(false); + User.setPlugin(plugin); // Parent command has no aliases when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); when(ac.getTopLabel()).thenReturn("bskyblock"); + when(ac.getLabel()).thenReturn("bskyblock"); when(ac.getWorld()).thenReturn(world); + when(ac.getPermission()).thenReturn("admin"); // World when(world.getEnvironment()).thenReturn(Environment.NORMAL); @@ -132,12 +145,12 @@ public void setUp() throws Exception { // Server & Scheduler BukkitScheduler sch = mock(BukkitScheduler.class); - PowerMockito.mockStatic(Bukkit.class); + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); when(Bukkit.getScheduler()).thenReturn(sch); // Locales LocalesManager lm = mock(LocalesManager.class); - when(lm.get(any(), any())).thenReturn("mock translation"); + when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); when(plugin.getLocalesManager()).thenReturn(lm); when(user.getTranslation(Mockito.anyString(),Mockito.anyString(), Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); @@ -165,6 +178,9 @@ public void setUp() throws Exception { // Util PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS); when(Util.getUUID(anyString())).thenCallRealMethod(); + + // Placeholder manager + when(plugin.getPlaceholdersManager()).thenReturn(phm); } @After @@ -276,5 +292,36 @@ public void testExecuteUserStringListOfStringKnownTargetHasIslandEnd() { verify(user).getTranslation(eq("commands.admin.tp.manual"), eq("[location]"), eq("0 0 0")); } + @Test + public void testPermissionsNoRootPermission() { + when(p.hasPermission("admin.tp")).thenReturn(true); + when(p.hasPermission("admin")).thenReturn(false); + when(pm.getUUID(eq("tastybento"))).thenReturn(notUUID); + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + AdminTeleportCommand atc = new AdminTeleportCommand(ac,"tpend"); + assertTrue(atc.canExecute(user, "tpend", List.of("tastybento"))); + String[] list = new String[2]; + list[0] = "tpend"; + list[1] = "tastybento"; + // Should fail + assertFalse(atc.execute(p, "tpend", list)); + } + + @Test + public void testPermissionsHasRootPermission() { + when(p.hasPermission("admin.tp")).thenReturn(true); + when(p.hasPermission("admin")).thenReturn(true); + when(pm.getUUID(eq("tastybento"))).thenReturn(notUUID); + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + AdminTeleportCommand atc = new AdminTeleportCommand(ac,"tpend"); + assertTrue(atc.canExecute(user, "tpend", List.of("tastybento"))); + String[] list = new String[2]; + list[0] = "tpend"; + list[1] = "tastybento"; + // Should pass + assertTrue(atc.execute(p, "tpend", list)); + verify(p).hasPermission("admin.tp"); + verify(p).hasPermission("admin"); + } } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java new file mode 100644 index 000000000..e1ee89ca1 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java @@ -0,0 +1,211 @@ +package world.bentobox.bentobox.api.commands.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.configuration.WorldSettings; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandsManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class}) +public class DefaultPlayerCommandTest { + + @Mock + GameModeAddon addon; + private PlayerCommand dpc; + @Mock + private WorldSettings ws; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private @Nullable Island island; + + + class PlayerCommand extends DefaultPlayerCommand { + + protected PlayerCommand(GameModeAddon addon) { + super(addon); + } + + } + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Addon + + // User + when(user.getUniqueId()).thenReturn(UUID.randomUUID()); + // IM + when(plugin.getIslandsManager()).thenReturn(im); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(island); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // World Settings + when(ws.getDefaultPlayerAction()).thenReturn("go"); + when(ws.getDefaultNewPlayerAction()).thenReturn("create"); + + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + when(ws.getPlayerCommandAliases()).thenReturn("island is"); + when(addon.getWorldSettings()).thenReturn(ws); + dpc = new PlayerCommand(addon); + dpc.setWorld(mock(World.class)); + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#DefaultPlayerCommand(world.bentobox.bentobox.api.addons.GameModeAddon)}. + */ + @Test + public void testDefaultPlayerCommand() { + assertNotNull(dpc); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("commands.island.help.description", dpc.getDescription()); + assertTrue(dpc.isOnlyPlayer()); + assertEquals("island", dpc.getPermission()); + // 20 = 19 subcommands + help command + assertEquals(20, dpc.getSubCommands().size()); // Update when commands are added or removed + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringUnknownCommand() { + assertFalse(dpc.execute(user, "label", List.of("unknown"))); + verify(user).sendMessage("general.errors.unknown-command", TextVariables.LABEL, "island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringNullUser() { + assertFalse(dpc.execute(null, "label", List.of())); + } + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasIsland() { + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).sendMessage("general.errors.use-in-game"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasNoIsland() { + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).sendMessage("general.errors.use-in-game"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasIslandUnknownCommand() { + when(ws.getDefaultPlayerAction()).thenReturn("goxxx"); + + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("label goxxx"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasNoIslandUnknownCommand() { + + when(ws.getDefaultNewPlayerAction()).thenReturn("createxxx"); + + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("label createxxx"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasIslandUnknownCommandSlash() { + when(ws.getDefaultPlayerAction()).thenReturn("/goxxx"); + + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("goxxx"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasNoIslandUnknownCommandSlash() { + + when(ws.getDefaultNewPlayerAction()).thenReturn("/createxxx"); + + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("createxxx"); + } +} \ No newline at end of file diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java new file mode 100644 index 000000000..6d39a6e04 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java @@ -0,0 +1,265 @@ +package world.bentobox.bentobox.api.commands.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.managers.RanksManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class }) +public class IslandDeletehomeCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private PlayersManager pm; + @Mock + private Island island; + private IslandDeletehomeCommand idh; + @Mock + private IslandWorldManager iwm; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Ranks Manager + RanksManager rm = new RanksManager(); + when(plugin.getRanksManager()).thenReturn(rm); + + // Addon + GameModeAddon addon = mock(GameModeAddon.class); + + // Settings + Settings settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); + + // Parent command has no aliases + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getParameters()).thenReturn("parameters"); + when(ic.getDescription()).thenReturn("description"); + when(ic.getPermissionPrefix()).thenReturn("permission."); + when(ic.getUsage()).thenReturn(""); + when(ic.getSubCommand(Mockito.anyString())).thenReturn(Optional.empty()); + when(ic.getAddon()).thenReturn(addon); + when(plugin.getIslands()).thenReturn(im); + // Player + Player player = mock(Player.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getName()).thenReturn("tastybento"); + when(user.getWorld()).thenReturn(mock(World.class)); + when(user.getTranslation(anyString())).thenAnswer(i -> i.getArgument(0, String.class)); + // Island + when(island.getOwner()).thenReturn(uuid); + when(island.onIsland(any())).thenReturn(true); + when(im.getIsland(any(), any(UUID.class))).thenReturn(island); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + @NotNull + Map homeMap = new HashMap<>(); + homeMap.put("Home", null); + homeMap.put("Home2", null); + homeMap.put("Home3", null); + homeMap.put("Home4", null); + when(island.getHomes()).thenReturn(homeMap); + + // IWM friendly name + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + // Not in nether + when(iwm.isNether(any())).thenReturn(false); + // Not in end + when(iwm.isEnd(any())).thenReturn(false); + // Number of homes default + when(iwm.getMaxHomes(any())).thenReturn(3); + when(plugin.getIWM()).thenReturn(iwm); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + idh = new IslandDeletehomeCommand(ic); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#IslandDeletehomeCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testIslandDeletehomeCommand() { + assertEquals("deletehome", idh.getLabel()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#setup()}. + */ + @Test + public void testSetup() { + assertTrue(idh.isOnlyPlayer()); + assertEquals("commands.island.deletehome.parameters", idh.getParameters()); + assertEquals("commands.island.deletehome.description", idh.getDescription()); + assertEquals("permission.island.deletehome", idh.getPermission()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteHelp() { + idh.canExecute(user, "label", List.of()); + verify(user).sendMessage("commands.help.header","[label]","commands.help.console"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoIsland() { + when(im.getIsland(any(), eq(user))).thenReturn(null); + assertFalse(idh.canExecute(user, "label", List.of("something"))); + verify(user).sendMessage("general.errors.no-island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteLowRank() { + when(island.getRank(user)).thenReturn(RanksManager.COOP_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.OWNER_RANK); + assertFalse(idh.canExecute(user, "label", List.of("something"))); + verify(user).sendMessage("general.errors.insufficient-rank", + TextVariables.RANK, "ranks.coop"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteUnknownHome() { + when(island.getRank(user)).thenReturn(RanksManager.OWNER_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.COOP_RANK); + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + + when(im.isHomeLocation(eq(island), anyString())).thenReturn(false); + + assertFalse(idh.canExecute(user, "label", List.of("something"))); + verify(user).sendMessage("commands.island.go.unknown-home"); + verify(user).sendMessage("commands.island.sethome.homes-are"); + verify(user).sendMessage("home-list-syntax", TextVariables.NAME, "home"); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteKnownHome() { + when(island.getRank(user)).thenReturn(RanksManager.OWNER_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.COOP_RANK); + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + + when(im.isHomeLocation(eq(island), anyString())).thenReturn(true); + + assertTrue(idh.canExecute(user, "label", List.of("home"))); + } + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + assertTrue(idh.execute(user, "label", List.of("home"))); + verify(user).sendMessage("commands.confirmation.confirm", "[seconds]", "10"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfString() { + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + Optional> list = idh.tabComplete(user, "label", List.of("hom")); + assertTrue(list.isPresent()); + assertEquals("home", list.get().get(0)); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringNothing() { + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + Optional> list = idh.tabComplete(user, "label", List.of("f")); + assertTrue(list.isPresent()); + assertTrue(list.get().isEmpty()); + } + +} diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java new file mode 100644 index 000000000..bdc919f7d --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java @@ -0,0 +1,281 @@ +/** + * + */ +package world.bentobox.bentobox.api.commands.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +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.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.managers.RanksManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class}) +public class IslandSetnameCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private PlayersManager pm; + @Mock + private Island island; + @Mock + private Addon addon; + + private IslandSetnameCommand isc; + @Mock + private @NonNull World world; + private RanksManager rm; + private Settings settings; + @Mock + private PluginManager pim; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); + + // User + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + Player p = mock(Player.class); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + when(user.getDisplayName()).thenReturn("&Ctastybento"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.getTranslation(any())).thenAnswer(invocation -> invocation.getArgument(0, String.class)); + + // Parent command has no aliases + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getWorld()).thenReturn(world); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + // Player has island to begin with + when(im.getIsland(world, user)).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + when(island.getName()).thenReturn("previous-name"); + + // Server and Plugin Manager for events + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + when(Bukkit.getPluginManager()).thenReturn(pim); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(any(), any())).thenAnswer(invocation -> invocation.getArgument(1, String.class)); + when(plugin.getLocalesManager()).thenReturn(lm); + PlaceholdersManager phm = mock(PlaceholdersManager.class); + when(phm.replacePlaceholders(any(), any())).thenAnswer(invocation -> invocation.getArgument(1, String.class)); + // Placeholder manager + when(plugin.getPlaceholdersManager()).thenReturn(phm); + + // Ranks Manager + rm = new RanksManager(); + when(plugin.getRanksManager()).thenReturn(rm); + + + // Test + isc = new IslandSetnameCommand(ic); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#IslandSetnameCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testIslandSetnameCommand() { + assertEquals("setname", isc.getLabel()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#setup()}. + */ + @Test + public void testSetup() { + assertTrue(isc.isOnlyPlayer()); + assertEquals("commands.island.setname.parameters", isc.getParameters()); + assertEquals("commands.island.setname.description", isc.getDescription()); + assertEquals("island.name", isc.getPermission()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNoArgs() { + assertFalse(isc.canExecute(user, isc.getLabel(), new ArrayList<>())); + verify(user).sendMessage("commands.help.header", "[label]", "BSkyBlock"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNoIsland() { + when(im.getIsland(world, user)).thenReturn(null); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name"))); + verify(user).sendMessage("general.errors.no-island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTooLowRank() { + when(island.getRank(any(User.class))).thenReturn(RanksManager.MEMBER_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.OWNER_RANK); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name"))); + verify(user).sendMessage("general.errors.insufficient-rank", TextVariables.RANK, "ranks.member"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameTooShort() { + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("x"))); + verify(user).sendMessage("commands.island.setname.name-too-short", TextVariables.NUMBER, "4"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameOnlyColors() { + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("§b§c§d§e"))); + verify(user).sendMessage("commands.island.setname.name-too-short", TextVariables.NUMBER, "4"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameTooLong() { + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("This is a very long name that is not allowed and will have to be prevented"))); + verify(user).sendMessage("commands.island.setname.name-too-long", TextVariables.NUMBER, "20"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameNotUnique() { + settings.setNameUniqueness(true); + when(im.nameExists(eq(world), anyString())).thenReturn(true); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name2"))); + verify(user).sendMessage("commands.island.setname.name-already-exists"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameApplyColors() { + when(user.hasPermission(anyString())).thenReturn(true); + settings.setNameUniqueness(true); + when(im.nameExists(world, "name§b")).thenReturn(true); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name&b"))); + verify(user).sendMessage("commands.island.setname.name-already-exists"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandAllOK() { + assertTrue(isc.canExecute(user, isc.getLabel(), List.of("name-okay"))); + verify(user, never()).sendMessage(anyString()); + } + + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + when(user.hasPermission(anyString())).thenReturn(true); + assertTrue(isc.execute(user, isc.getLabel(), List.of("name-okay"))); + verify(island).setName("name-okay"); + verify(user).sendMessage("commands.island.setname.success", TextVariables.NAME, "name-okay"); + verify(pim, times(2)).callEvent(any()); + } + +} diff --git a/src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java b/src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java new file mode 100644 index 000000000..9779feeeb --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java @@ -0,0 +1,201 @@ +package world.bentobox.bentobox.api.panels; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class }) +public class PanelItemTest { + + @Mock + private PanelItemBuilder pib; + private PanelItem pi; + @Mock + private ClickHandler clickHandler; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + // Builder + when(pib.getAmount()).thenReturn(2); + when(pib.getClickHandler()).thenReturn(clickHandler); + when(pib.getDescription()).thenReturn(List.of("Description", "hello")); + when(pib.getIcon()).thenReturn(new ItemStack(Material.STONE)); + when(pib.getName()).thenReturn("Name"); + when(pib.getPlayerHeadName()).thenReturn("tastybento"); + pi = new PanelItem(pib); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#empty()}. + */ + @Test + public void testEmpty() { + PanelItem panelItem = PanelItem.empty(); + assertTrue(panelItem.getName().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getItem()}. + */ + @Test + public void testGetItem() { + ItemStack i = pi.getItem(); + assertNotNull(i); + assertEquals(Material.STONE, i.getType()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getDescription()}. + */ + @Test + public void testGetDescription() { + assertEquals(2, pi.getDescription().size()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setDescription(java.util.List)}. + */ + @Test + public void testSetDescription() { + assertEquals(2, pi.getDescription().size()); + pi.setDescription(List.of("1","2","3")); + assertEquals(3, pi.getDescription().size()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getName()}. + */ + @Test + public void testGetName() { + assertEquals("Name", pi.getName()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setName(java.lang.String)}. + */ + @Test + public void testSetName() { + assertEquals("Name", pi.getName()); + pi.setName("Name2"); + assertEquals("Name2", pi.getName()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#isInvisible()}. + */ + @Test + public void testIsInvisible() { + assertFalse(pi.isInvisible()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setInvisible(boolean)}. + */ + @Test + public void testSetInvisible() { + assertFalse(pi.isInvisible()); + pi.setInvisible(true); + assertTrue(pi.isInvisible()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getClickHandler()}. + */ + @Test + public void testGetClickHandler() { + assertEquals(clickHandler, pi.getClickHandler().get()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setClickHandler(world.bentobox.bentobox.api.panels.PanelItem.ClickHandler)}. + */ + @Test + public void testSetClickHandler() { + assertEquals(clickHandler, pi.getClickHandler().get()); + pi.setClickHandler(null); + assertEquals(Optional.empty(), pi.getClickHandler()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#isGlow()}. + */ + @Test + public void testIsGlow() { + assertFalse(pi.isGlow()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setGlow(boolean)}. + */ + @Test + public void testSetGlow() { + assertFalse(pi.isGlow()); + pi.setGlow(true); + assertTrue(pi.isGlow()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#isPlayerHead()}. + */ + @Test + public void testIsPlayerHead() { + assertTrue(pi.isPlayerHead()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getPlayerHeadName()}. + */ + @Test + public void testGetPlayerHeadName() { + assertEquals("tastybento", pi.getPlayerHeadName()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setHead(org.bukkit.inventory.ItemStack)}. + */ + @Test + public void testSetHead() { + pi.setHead(new ItemStack(Material.PLAYER_HEAD)); + } + +} diff --git a/src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java b/src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java new file mode 100644 index 000000000..0604f1af8 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java @@ -0,0 +1,192 @@ +package world.bentobox.bentobox.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +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; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class }) +public class BentoBoxPermsCommandTest { + + @Mock + private BentoBox plugin; + @Mock + private CompositeCommand ac; + @Mock + private User user; + @Mock + private LocalesManager lm; + + BentoBoxPermsCommand cmd; + @Mock + private PlaceholdersManager phm; + @Mock + private PluginManager pim; + @Mock + private Permission perm; + + private PermissionDefault defaultPerm = PermissionDefault.OP; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + @NonNull + Map cmdMap = new HashMap<>(); + cmdMap.put("test", ac); + when(cm.getCommands()).thenReturn(cmdMap); + + + // Parent command has no aliases + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ac.getSubCommands()).thenReturn(new HashMap<>()); + when(ac.getLabel()).thenReturn("bbox"); + when(ac.getTopLabel()).thenReturn("bbox"); + when(ac.getPermission()).thenReturn("admin.bbox"); + when(ac.getDescription()).thenReturn("description"); + + + // User + when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.isPlayer()).thenReturn(false); + User.setPlugin(plugin); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + when(perm.getDefault()).thenReturn(defaultPerm); + when(pim.getPermission(anyString())).thenReturn(perm); + when(Bukkit.getPluginManager()).thenReturn(pim); + + // Placeholders + when(phm.replacePlaceholders(any(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + + // BentoBox + when(plugin.getLocalesManager()).thenReturn(lm); + when(plugin.getPlaceholdersManager()).thenReturn(phm); + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + + // Commands for perms + + + cmd = new BentoBoxPermsCommand(ac); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#BentoBoxPermsCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testBentoBoxPermsCommand() { + assertNotNull(cmd); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#setup()}. + */ + @Test + public void testSetup() { + assertTrue(cmd.isOnlyConsole()); + assertFalse(cmd.isOnlyPlayer()); + assertEquals("bentobox.admin.perms", cmd.getPermission()); + assertEquals("commands.bentobox.perms.description", cmd.getDescription()); + assertEquals("commands.bentobox.perms.parameters", cmd.getParameters()); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + assertTrue(cmd.execute(user, "perms", List.of())); + verify(user).sendMessage("*** BentoBox effective perms:"); + verify(user).sendRawMessage("permissions:"); + verify(user).sendRawMessage(" admin.bbox:"); + verify(user).sendRawMessage(" description: Allow use of '/bbox' command - null"); + verify(user).sendRawMessage(" bentobox.admin.perms:"); + verify(user).sendRawMessage(" description: Allow use of '/bbox perms' command - null"); + verify(user, times(2)).sendRawMessage(" default: OP"); + + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#execute(Player, java.lang.String, String[])}. + */ + @Test + public void testExecuteUserStringListOfStringConsole() { + String[] args = new String[1]; + args[0] = ""; + CommandSender p = mock(CommandSender.class); + assertTrue(cmd.execute(p, "perms", args)); + verify(p, never()).sendMessage("general.errors.use-in-console"); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#execute(Player, java.lang.String, String[])}. + */ + @Test + public void testExecuteUserStringListOfStringIsPlayer() { + when(user.isPlayer()).thenReturn(true); + String[] args = new String[1]; + args[0] = ""; + Player p = mock(Player.class); + assertFalse(cmd.execute(p, "perms", args)); + verify(p).sendMessage("general.errors.use-in-console"); + } +} diff --git a/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java b/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java index 0061942f3..40e69ae69 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java @@ -262,6 +262,106 @@ public void testBannedCommandsWithBannedCommandWithExtraBannedStuff() { } + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand2() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/spawn"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand3() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/cmi sethome"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertTrue(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedComman4() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/cmi"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand5() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/cmi homey"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand6() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/spawn"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + banned.add("spawn sethome now"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand7() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/spawn"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + banned.add("spawn sethome now"); + banned.add("cmi multi now"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} */ 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 6d5406a37..e780d3b84 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java @@ -12,6 +12,8 @@ import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; @@ -121,6 +123,7 @@ public void setUp() throws Exception { when(player.getInventory()).thenReturn(inv); User.setPlugin(plugin); + User.clearUsers(); User.getInstance(player); // IWM @@ -169,12 +172,36 @@ public void setUp() throws Exception { // Util translate color codes (used in user translate methods) when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + // Tags + for (Material m : Material.values()) { + if (m.name().contains("_SIGN")) { + when(Tag.ALL_SIGNS.isTagged(m)).thenReturn(true); + when(Tag.SIGNS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_WALL_SIGN")) { + when(Tag.WALL_SIGNS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_TRAPDOOR")) { + when(Tag.TRAPDOORS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("FENCE")) { + when(Tag.FENCES.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_DOOR")) { + when(Tag.DOORS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_BOAT") || m.name().contains("_RAFT")) { + when(Tag.ITEMS_BOATS.isTagged(m)).thenReturn(true); + } + + } } /** + * @throws Exception */ @After - public void tearDown() { + public void tearDown() throws Exception { User.clearUsers(); Mockito.framework().clearInlineMocks(); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java index e04f72b41..8c39590aa 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java @@ -13,7 +13,6 @@ import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; @@ -45,7 +44,7 @@ * */ @RunWith(PowerMockRunner.class) -@PrepareForTest( {BentoBox.class, Flags.class, Util.class, Bukkit.class, Tag.class} ) +@PrepareForTest( {BentoBox.class, Flags.class, Util.class, Bukkit.class} ) public class PlaceBlocksListenerTest extends AbstractCommonSetup { private PlaceBlocksListener pbl; @@ -163,7 +162,7 @@ public void testOnBlockPlaceNotAllowed() { assertTrue(e.isCancelled()); verify(notifier).notify(any(), eq("protection.protected")); } - + /** * Test method for {@link PlaceBlocksListener#onBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. */ @@ -184,7 +183,7 @@ public void testOnBlockCropsAllowed() { pbl.onBlockPlace(e); assertFalse(e.isCancelled()); } - + /** * Test method for {@link PlaceBlocksListener#onBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. */ @@ -206,7 +205,7 @@ public void testOnBlockCropsAllowedNotCrop() { assertTrue(e.isCancelled()); verify(notifier).notify(any(), eq("protection.protected")); } - + /** * Test method for {@link PlaceBlocksListener#onBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. */ diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java new file mode 100644 index 000000000..9e8443725 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java @@ -0,0 +1,166 @@ +package world.bentobox.bentobox.listeners.flags.protection; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.bukkit.Bukkit; +import org.bukkit.GameEvent; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.event.block.BlockReceiveGameEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup; +import world.bentobox.bentobox.lists.Flags; +import world.bentobox.bentobox.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BentoBox.class, Flags.class, Util.class, Bukkit.class} ) +public class SculkSensorListenerTest extends AbstractCommonSetup { + + private SculkSensorListener ssl; + @Mock + private Block block; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + super.setUp(); + // Default is that everything is allowed + when(island.isAllowed(any(), any())).thenReturn(true); + + // In world + when(iwm.inWorld(any(World.class))).thenReturn(true); + + // Block + when(block.getType()).thenReturn(Material.SCULK_SENSOR); + when(block.getWorld()).thenReturn(world); + when(block.getLocation()).thenReturn(location); + + // User + when(player.getWorld()).thenReturn(world); + when(player.getLocation()).thenReturn(location); + User.getInstance(player); + + ssl = new SculkSensorListener(); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotAllowed() { + when(island.isAllowed(any(), any())).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorAllowed() { + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotInWorld() { + when(iwm.inWorld(any(World.class))).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotAllowedCalibrated() { + when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); + when(island.isAllowed(any(), any())).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorAllowedCalibrated() { + when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotInWorldCalibrated() { + when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); + when(iwm.inWorld(any(World.class))).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotAllowedNotSculk() { + when(block.getType()).thenReturn(Material.SHULKER_BOX); + when(island.isAllowed(any(), any())).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorAllowedNotSculk() { + when(block.getType()).thenReturn(Material.SHULKER_BOX); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotInWorldNotSculk() { + when(block.getType()).thenReturn(Material.SHULKER_BOX); + when(iwm.inWorld(any(World.class))).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); + } + +} diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java index 54c805df4..3bd91e968 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java @@ -23,6 +23,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason; import org.bukkit.inventory.ItemStack; import org.junit.After; import org.junit.Before; @@ -213,7 +214,7 @@ public void testOnPlayerRespawn() { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(safeLocation, ev.getRespawnLocation()); // Verify commands @@ -232,7 +233,7 @@ public void testOnPlayerRespawnWithoutDeath() { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(location, ev.getRespawnLocation()); } @@ -255,7 +256,7 @@ public void testOnPlayerRespawnWrongWorld() { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(location, ev.getRespawnLocation()); } @@ -276,7 +277,7 @@ public void testOnPlayerRespawnFlagNotSet() { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(location, ev.getRespawnLocation()); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java index 8cfb2cf8c..a74929e35 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java @@ -17,6 +17,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason; import org.bukkit.scheduler.BukkitScheduler; import org.junit.After; import org.junit.Before; @@ -200,7 +201,7 @@ public void testOnUserTeleportToNotIsland() { */ @Test public void testOnUserRespawn() { - PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false); + PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH); new RemoveMobsListener().onUserRespawn(e); verify(scheduler).runTask(any(), any(Runnable.class)); } @@ -211,7 +212,7 @@ public void testOnUserRespawn() { @Test public void testOnUserRespawnDoNotRemove() { Flags.REMOVE_MOBS.setSetting(world, false); - PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false); + PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH); new RemoveMobsListener().onUserRespawn(e); verify(scheduler, never()).runTask(any(), any(Runnable.class)); } @@ -223,7 +224,7 @@ public void testOnUserRespawnDoNotRemove() { public void testOnUserRespawnNotIsland() { // Not on island when(im.locationIsOnIsland(any(), any())).thenReturn(false); - PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false); + PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH); new RemoveMobsListener().onUserRespawn(e); verify(scheduler, never()).runTask(any(), any(Runnable.class)); } diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java index 66f90c51a..30e24cb16 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java @@ -12,7 +12,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java index 055efe1b3..b3b24ab42 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java @@ -40,7 +40,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java index 62f0a3879..36acd6362 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java @@ -67,9 +67,6 @@ public class IslandDeletionManagerTest { private BukkitScheduler scheduler; @Mock private IslandWorldManager iwm; - @Mock - private IslandChunkDeletionManager chunkDeletionManager; - /** */ @@ -102,8 +99,6 @@ public void setUp() throws Exception { // IWM when(plugin.getIWM()).thenReturn(iwm); when(iwm.getIslandDistance(any())).thenReturn(64); - // Chunk deletion manager - when(plugin.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager); // Island Deletion Manager idm = new IslandDeletionManager(plugin); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java index 8b4af6d1e..00474ac58 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java @@ -114,7 +114,7 @@ public void tearDown() { */ @Test public void testRegisterWorldsToMultiverse() { - iwm.registerWorldsToMultiverse(); + iwm.registerWorldsToMultiverse(true); } /** @@ -614,6 +614,7 @@ public void testGetAddonNull() { /** * Test method for {@link world.bentobox.bentobox.managers.IslandWorldManager#getDefaultIslandFlags(org.bukkit.World)}. */ + @SuppressWarnings("removal") @Test public void testGetDefaultIslandFlags() { Map flags = new HashMap<>(); @@ -634,6 +635,7 @@ public void testGetVisibleSettings() { /** * Test method for {@link world.bentobox.bentobox.managers.IslandWorldManager#getDefaultIslandSettings(org.bukkit.World)}. */ + @SuppressWarnings("removal") @Test public void testGetDefaultIslandSettings() { Map flags = new HashMap<>(); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index 169551906..b1f626c43 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -78,13 +78,14 @@ import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.island.IslandCache; import world.bentobox.bentobox.util.Util; @RunWith(PowerMockRunner.class) @PrepareForTest( { Bukkit.class, BentoBox.class, Util.class, Location.class }) -public class IslandsManagerTest { +public class IslandsManagerTest extends AbstractCommonSetup { @Mock private BentoBox plugin; @@ -108,6 +109,8 @@ public class IslandsManagerTest { @Mock private IslandWorldManager iwm; @Mock + private IslandDeletionManager deletionManager; + @Mock private IslandChunkDeletionManager chunkDeletionManager; @Mock private IslandCache islandCache; @@ -141,11 +144,12 @@ public class IslandsManagerTest { // Class under test IslandsManager im; - /** - */ + @Override @SuppressWarnings("unchecked") @Before public void setUp() throws Exception { + super.setUp(); + // Clear any lingering database tearDown(); // Set up plugin @@ -159,9 +163,6 @@ public void setUp() throws Exception { when(iwm.inWorld(any(Location.class))).thenReturn(true); when(plugin.getIWM()).thenReturn(iwm); - // Chunk deletion manager - when(plugin.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager); - // Settings Settings s = mock(Settings.class); when(plugin.getSettings()).thenReturn(s); @@ -309,20 +310,16 @@ public void setUp() throws Exception { .getNearbyEntities(any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble())) .thenReturn(collection); - + // Deletion Manager + when(deletionManager.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager); + when(plugin.getIslandDeletionManager()).thenReturn(deletionManager); // database must be mocked here db = mock(Database.class); // Signs - sign = Material.getMaterial("SIGN"); - if (sign == null) { - sign = Material.getMaterial("OAK_SIGN"); - } - wallSign = Material.getMaterial("WALL_SIGN"); - if (wallSign == null) { - wallSign = Material.getMaterial("OAK_WALL_SIGN"); - } + sign = Material.BIRCH_SIGN; + wallSign = Material.ACACIA_WALL_SIGN; // PaperLib env = new CraftBukkitEnvironment(); @@ -337,10 +334,10 @@ public void setUp() throws Exception { //im.setIslandCache(islandCache); } - /** - */ + @Override @After public void tearDown() throws Exception { + super.tearDown(); Mockito.framework().clearInlineMocks(); deleteAll(new File("database")); deleteAll(new File("database_backup")); @@ -393,12 +390,13 @@ public void testIsSafeLocationSubmerged() { assertFalse(im.isSafeLocation(location)); } + @SuppressWarnings("deprecation") @Test public void testCheckIfSafeTrapdoor() { for (Material d : Material.values()) { if (d.name().contains("DOOR")) { for (Material s : Material.values()) { - if (s.name().contains("_SIGN")) { + if (s.name().contains("_SIGN") && !s.isLegacy()) { assertFalse("Fail " + d.name() + " " + s.name(), im.checkIfSafe(world, d, s, Material.AIR)); } } @@ -486,13 +484,13 @@ public void testTrapDoor() { @Test public void testBadBlocks() { // Fences - Arrays.stream(Material.values()).filter(m -> m.toString().contains("FENCE")).forEach(m -> { - when(ground.getType()).thenReturn(m); - assertFalse("Fence :" + m.toString(), im.isSafeLocation(location)); - }); + when(ground.getType()).thenReturn(Material.SPRUCE_FENCE); + assertFalse("Fence :" + Material.SPRUCE_FENCE.toString(), im.isSafeLocation(location)); // Signs + sign = Material.BIRCH_SIGN; when(ground.getType()).thenReturn(sign); assertFalse("Sign", im.isSafeLocation(location)); + wallSign = Material.ACACIA_WALL_SIGN; when(ground.getType()).thenReturn(wallSign); assertFalse("Sign", im.isSafeLocation(location)); // Bad Blocks